blob: f251b86b7a09270aee318007b5b74ffc99f98aa3 [file] [log] [blame]
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static android.Manifest.permission.DUMP;
Nathan Harold031acb82017-03-07 13:23:36 -080020import static android.net.IpSecManager.INVALID_RESOURCE_ID;
Nathan Harold80865392017-04-04 19:37:48 -070021import static android.system.OsConstants.AF_INET;
evitayan43d93a02018-03-22 17:53:08 -070022import static android.system.OsConstants.AF_INET6;
23import static android.system.OsConstants.AF_UNSPEC;
manojboopathiac927fe2017-11-30 17:11:49 -080024import static android.system.OsConstants.EINVAL;
Nathan Harold80865392017-04-04 19:37:48 -070025import static android.system.OsConstants.IPPROTO_UDP;
26import static android.system.OsConstants.SOCK_DGRAM;
Benedict Wong683441d2018-07-25 18:46:19 -070027
Nathan Harold65ef8432018-03-15 18:06:06 -070028import android.annotation.NonNull;
29import android.app.AppOpsManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080030import android.content.Context;
Benedict Wong4aac3e92019-02-25 12:33:22 -080031import android.content.pm.PackageManager;
Yan Yana2f3b492020-09-29 23:38:00 -070032import android.net.ConnectivityManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080033import android.net.IIpSecService;
34import android.net.INetd;
Serik Beketayev7f507332020-12-06 22:31:23 -080035import android.net.InetAddresses;
Nathan Harold031acb82017-03-07 13:23:36 -080036import android.net.IpSecAlgorithm;
37import android.net.IpSecConfig;
38import android.net.IpSecManager;
Nathan Harold80865392017-04-04 19:37:48 -070039import android.net.IpSecSpiResponse;
Nathan Harold031acb82017-03-07 13:23:36 -080040import android.net.IpSecTransform;
Nathan Harold80865392017-04-04 19:37:48 -070041import android.net.IpSecTransformResponse;
Benedict Wong8bc90732018-01-18 18:31:45 -080042import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold80865392017-04-04 19:37:48 -070043import android.net.IpSecUdpEncapResponse;
Benedict Wong97c3c942018-03-01 18:53:07 -080044import android.net.LinkAddress;
Yan Yana2f3b492020-09-29 23:38:00 -070045import android.net.LinkProperties;
Benedict Wong8bc90732018-01-18 18:31:45 -080046import android.net.Network;
Benedict Wong083faee2017-12-03 19:42:36 -080047import android.net.TrafficStats;
Remi NGUYEN VAN5db09192019-01-29 15:38:52 +090048import android.net.util.NetdService;
Nathan Harold031acb82017-03-07 13:23:36 -080049import android.os.Binder;
Nathan Harold031acb82017-03-07 13:23:36 -080050import android.os.IBinder;
51import android.os.ParcelFileDescriptor;
Benedict Wong908d34e2021-04-15 11:59:16 -070052import android.os.Process;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080053import android.os.RemoteException;
Nathan Harold031acb82017-03-07 13:23:36 -080054import android.os.ServiceSpecificException;
Nathan Harold80865392017-04-04 19:37:48 -070055import android.system.ErrnoException;
56import android.system.Os;
57import android.system.OsConstants;
Nathan Harold19b99d92017-08-23 13:46:33 -070058import android.text.TextUtils;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080059import android.util.Log;
lucaslin7eb76592021-03-11 17:39:49 +080060import android.util.Range;
Nathan Harold031acb82017-03-07 13:23:36 -080061import android.util.SparseArray;
Benedict Wong8bc90732018-01-18 18:31:45 -080062import android.util.SparseBooleanArray;
Nathan Harold19b99d92017-08-23 13:46:33 -070063
Nathan Harold031acb82017-03-07 13:23:36 -080064import com.android.internal.annotations.GuardedBy;
ludi5e623ea2017-05-12 09:15:00 -070065import com.android.internal.annotations.VisibleForTesting;
Benedict Wong70867e52017-11-06 20:49:10 -080066import com.android.internal.util.Preconditions;
paulhu00e34562021-10-26 09:00:50 +000067import com.android.net.module.util.BinderUtils;
lucaslinff6fe7b2021-02-03 23:59:45 +080068import com.android.net.module.util.NetdUtils;
Benedict Wong908d34e2021-04-15 11:59:16 -070069import com.android.net.module.util.PermissionUtils;
Nathan Harold19b99d92017-08-23 13:46:33 -070070
Benedict Wong683441d2018-07-25 18:46:19 -070071import libcore.io.IoUtils;
72
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080073import java.io.FileDescriptor;
Nathan Harold80865392017-04-04 19:37:48 -070074import java.io.IOException;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080075import java.io.PrintWriter;
evitayan43d93a02018-03-22 17:53:08 -070076import java.net.Inet4Address;
77import java.net.Inet6Address;
Nathan Harold80865392017-04-04 19:37:48 -070078import java.net.InetAddress;
79import java.net.InetSocketAddress;
80import java.net.UnknownHostException;
Benedict Wong02346822017-10-26 19:41:43 -070081import java.util.ArrayList;
82import java.util.List;
Benedict Wong529e8aa2020-02-11 23:49:36 -080083import java.util.Objects;
Nathan Harold19b99d92017-08-23 13:46:33 -070084
Benedict Wong02346822017-10-26 19:41:43 -070085/**
86 * A service to manage multiple clients that want to access the IpSec API. The service is
87 * responsible for maintaining a list of clients and managing the resources (and related quotas)
88 * that each of them own.
89 *
90 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
91 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
92 * thread is ever running at a time.
93 *
94 * @hide
95 */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080096public class IpSecService extends IIpSecService.Stub {
97 private static final String TAG = "IpSecService";
98 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Nathan Harold80865392017-04-04 19:37:48 -070099
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800100 private static final String NETD_SERVICE_NAME = "netd";
Benedict Wong38e52972018-05-07 20:06:44 -0700101 private static final int[] ADDRESS_FAMILIES =
102 new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800103
ludi5e623ea2017-05-12 09:15:00 -0700104 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold80865392017-04-04 19:37:48 -0700105 private static final InetAddress INADDR_ANY;
106
Benedict Wong29c30772019-03-20 09:44:09 -0700107 @VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
108
Nathan Harold80865392017-04-04 19:37:48 -0700109 static {
110 try {
111 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
112 } catch (UnknownHostException e) {
113 throw new RuntimeException(e);
114 }
115 }
116
117 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
118 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
119
120 /* Binder context for this service */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800121 private final Context mContext;
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800122 private final Dependencies mDeps;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800123
Nathan Haroldfdafce22017-12-13 19:16:33 -0800124 /**
Nathan Harold5a19b952018-01-05 19:25:13 -0800125 * The next non-repeating global ID for tracking resources between users, this service, and
126 * kernel data structures. Accessing this variable is not thread safe, so it is only read or
127 * modified within blocks synchronized on IpSecService.this. We want to avoid -1
128 * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
Nathan Haroldfdafce22017-12-13 19:16:33 -0800129 */
130 @GuardedBy("IpSecService.this")
131 private int mNextResourceId = 1;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800132
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800133 /**
134 * Dependencies of IpSecService, for injection in tests.
135 */
136 @VisibleForTesting
137 public static class Dependencies {
138 /**
139 * Get a reference to INetd.
140 */
141 public INetd getNetdInstance(Context context) throws RemoteException {
142 final INetd netd = INetd.Stub.asInterface((IBinder)
143 context.getSystemService(Context.NETD_SERVICE));
144 if (netd == null) {
145 throw new RemoteException("Failed to Get Netd Instance");
146 }
147 return netd;
148 }
ludi5e623ea2017-05-12 09:15:00 -0700149 }
150
Benedict Wong083faee2017-12-03 19:42:36 -0800151 final UidFdTagger mUidFdTagger;
ludi5e623ea2017-05-12 09:15:00 -0700152
Benedict Wong02346822017-10-26 19:41:43 -0700153 /**
154 * Interface for user-reference and kernel-resource cleanup.
155 *
156 * <p>This interface must be implemented for a resource to be reference counted.
157 */
158 @VisibleForTesting
159 public interface IResource {
160 /**
161 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
162 * objects dependent on it.
163 *
164 * <p>Implementations of this method are expected to remove references to the IResource
165 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
166 * the resource is considered invalid for user access or allocation or use in other
167 * resources.
168 *
169 * <p>References to the IResource object may be held by other RefcountedResource objects,
Benedict Wongcbd329b2017-12-13 17:16:53 -0800170 * and as such, the underlying resources and quota may not be cleaned up.
Benedict Wong02346822017-10-26 19:41:43 -0700171 */
172 void invalidate() throws RemoteException;
173
174 /**
175 * Releases underlying resources and related quotas.
176 *
177 * <p>Implementations of this method are expected to remove all system resources that are
178 * tracked by the IResource object. Due to other RefcountedResource objects potentially
Benedict Wong6855aee2017-11-16 15:27:22 -0800179 * having references to the IResource object, freeUnderlyingResources may not always be
Benedict Wong02346822017-10-26 19:41:43 -0700180 * called from releaseIfUnreferencedRecursively().
181 */
182 void freeUnderlyingResources() throws RemoteException;
183 }
184
185 /**
186 * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
187 *
188 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
189 * RefcountedResource object creates an explicit reference that must be freed by calling
190 * userRelease(). Additionally, adding this object as a child of another RefcountedResource
191 * object will add an implicit reference.
192 *
193 * <p>Resources are cleaned up when all references, both implicit and explicit, are released
194 * (ie, when userRelease() is called and when all parents have called releaseReference() on this
195 * object.)
196 */
197 @VisibleForTesting
198 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000199 private final T mResource;
200 private final List<RefcountedResource> mChildren;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800201 int mRefCount = 1; // starts at 1 for user's reference.
Junyu Lai75eabfe2019-04-26 01:38:04 +0000202 IBinder mBinder;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800203
Junyu Lai75eabfe2019-04-26 01:38:04 +0000204 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
Benedict Wong02346822017-10-26 19:41:43 -0700205 synchronized (IpSecService.this) {
206 this.mResource = resource;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000207 this.mChildren = new ArrayList<>(children.length);
Benedict Wong02346822017-10-26 19:41:43 -0700208 this.mBinder = binder;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000209
Benedict Wong02346822017-10-26 19:41:43 -0700210 for (RefcountedResource child : children) {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000211 mChildren.add(child);
Benedict Wong02346822017-10-26 19:41:43 -0700212 child.mRefCount++;
213 }
214
215 try {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000216 mBinder.linkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700217 } catch (RemoteException e) {
218 binderDied();
Benedict Wong04738f52019-02-28 20:28:48 -0800219 e.rethrowFromSystemServer();
Benedict Wong02346822017-10-26 19:41:43 -0700220 }
221 }
222 }
223
224 /**
225 * If the Binder object dies, this function is called to free the system resources that are
Benedict Wong6855aee2017-11-16 15:27:22 -0800226 * being tracked by this record and to subsequently release this record for garbage
Benedict Wong02346822017-10-26 19:41:43 -0700227 * collection
228 */
229 @Override
230 public void binderDied() {
231 synchronized (IpSecService.this) {
232 try {
233 userRelease();
234 } catch (Exception e) {
235 Log.e(TAG, "Failed to release resource: " + e);
236 }
237 }
238 }
239
240 public T getResource() {
241 return mResource;
242 }
243
244 /**
245 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
246 * arrays)
247 *
248 * <p>If this method has been previously called, the RefcountedResource's binder field will
249 * be null, and the method will return without performing the cleanup a second time.
250 *
251 * <p>Note that calling this function does not imply that kernel resources will be freed at
252 * this time, or that the related quota will be returned. Such actions will only be
253 * performed upon the reference count reaching zero.
254 */
255 @GuardedBy("IpSecService.this")
256 public void userRelease() throws RemoteException {
257 // Prevent users from putting reference counts into a bad state by calling
258 // userRelease() multiple times.
259 if (mBinder == null) {
260 return;
261 }
262
Junyu Lai75eabfe2019-04-26 01:38:04 +0000263 mBinder.unlinkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700264 mBinder = null;
265
266 mResource.invalidate();
Junyu Lai75eabfe2019-04-26 01:38:04 +0000267
Benedict Wong02346822017-10-26 19:41:43 -0700268 releaseReference();
269 }
270
271 /**
272 * Removes a reference to this resource. If the resultant reference count is zero, the
273 * underlying resources are freed, and references to all child resources are also dropped
274 * recursively (resulting in them freeing their resources and children, etcetera)
275 *
276 * <p>This method also sets the reference count to an invalid value (-1) to signify that it
277 * has been fully released. Any subsequent calls to this method will result in an
278 * IllegalStateException being thrown due to resource already having been previously
279 * released
280 */
281 @VisibleForTesting
282 @GuardedBy("IpSecService.this")
283 public void releaseReference() throws RemoteException {
284 mRefCount--;
285
286 if (mRefCount > 0) {
287 return;
288 } else if (mRefCount < 0) {
289 throw new IllegalStateException(
290 "Invalid operation - resource has already been released.");
291 }
292
293 // Cleanup own resources
294 mResource.freeUnderlyingResources();
295
296 // Cleanup child resources as needed
297 for (RefcountedResource<? extends IResource> child : mChildren) {
298 child.releaseReference();
299 }
300
301 // Enforce that resource cleanup can only be called once
302 // By decrementing the refcount (from 0 to -1), the next call will throw an
303 // IllegalStateException - it has already been released fully.
304 mRefCount--;
305 }
306
307 @Override
308 public String toString() {
309 return new StringBuilder()
310 .append("{mResource=")
311 .append(mResource)
312 .append(", mRefCount=")
313 .append(mRefCount)
314 .append(", mChildren=")
315 .append(mChildren)
316 .append("}")
317 .toString();
318 }
319 }
320
Benedict Wongcbd329b2017-12-13 17:16:53 -0800321 /**
322 * Very simple counting class that looks much like a counting semaphore
323 *
324 * <p>This class is not thread-safe, and expects that that users of this class will ensure
325 * synchronization and thread safety by holding the IpSecService.this instance lock.
326 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800327 @VisibleForTesting
328 static class ResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700329 private final int mMax;
330 int mCurrent;
331
332 ResourceTracker(int max) {
333 mMax = max;
334 mCurrent = 0;
335 }
336
Benedict Wong6855aee2017-11-16 15:27:22 -0800337 boolean isAvailable() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700338 return (mCurrent < mMax);
339 }
340
Benedict Wong6855aee2017-11-16 15:27:22 -0800341 void take() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700342 if (!isAvailable()) {
343 Log.wtf(TAG, "Too many resources allocated!");
344 }
345 mCurrent++;
346 }
347
Benedict Wong6855aee2017-11-16 15:27:22 -0800348 void give() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700349 if (mCurrent <= 0) {
350 Log.wtf(TAG, "We've released this resource too many times");
351 }
352 mCurrent--;
353 }
ludi529fdec2017-08-10 15:44:40 -0700354
355 @Override
356 public String toString() {
357 return new StringBuilder()
358 .append("{mCurrent=")
359 .append(mCurrent)
360 .append(", mMax=")
361 .append(mMax)
362 .append("}")
363 .toString();
364 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700365 }
366
Benedict Wong6855aee2017-11-16 15:27:22 -0800367 @VisibleForTesting
368 static final class UserRecord {
Benedict Wong6855aee2017-11-16 15:27:22 -0800369 /* Maximum number of each type of resource that a single UID may possess */
Benedict Wong7bcf9c22020-02-11 23:36:42 -0800370
371 // Up to 4 active VPNs/IWLAN with potential soft handover.
372 public static final int MAX_NUM_TUNNEL_INTERFACES = 8;
373 public static final int MAX_NUM_ENCAP_SOCKETS = 16;
374
375 // SPIs and Transforms are both cheap, and are 1:1 correlated.
376 public static final int MAX_NUM_TRANSFORMS = 64;
377 public static final int MAX_NUM_SPIS = 64;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700378
Benedict Wongcbd329b2017-12-13 17:16:53 -0800379 /**
380 * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
381 * and explicit (user) reference management.
382 *
383 * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
384 *
385 * <p>Resources are removed from this array when the user releases their explicit reference
386 * by calling one of the releaseResource() methods.
387 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800388 final RefcountedResourceArray<SpiRecord> mSpiRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800389 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800390 final RefcountedResourceArray<TransformRecord> mTransformRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800391 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800392 final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800393 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
Benedict Wong8bc90732018-01-18 18:31:45 -0800394 final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
395 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
Benedict Wongcbd329b2017-12-13 17:16:53 -0800396
397 /**
398 * Trackers for quotas for each of the OwnedResource types.
399 *
400 * <p>These trackers are separate from the resource arrays, since they are incremented and
401 * decremented at different points in time. Specifically, quota is only returned upon final
402 * resource deallocation (after all explicit and implicit references are released). Note
403 * that it is possible that calls to releaseResource() will not return the used quota if
404 * there are other resources that depend on (are parents of) the resource being released.
405 */
406 final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
407 final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
Benedict Wong6855aee2017-11-16 15:27:22 -0800408 final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
Benedict Wong8bc90732018-01-18 18:31:45 -0800409 final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
Benedict Wong6855aee2017-11-16 15:27:22 -0800410
411 void removeSpiRecord(int resourceId) {
412 mSpiRecords.remove(resourceId);
Nathan Harold6e4681c2017-04-24 16:16:34 -0700413 }
414
Benedict Wong6855aee2017-11-16 15:27:22 -0800415 void removeTransformRecord(int resourceId) {
416 mTransformRecords.remove(resourceId);
417 }
418
Benedict Wong8bc90732018-01-18 18:31:45 -0800419 void removeTunnelInterfaceRecord(int resourceId) {
420 mTunnelInterfaceRecords.remove(resourceId);
421 }
422
Benedict Wong6855aee2017-11-16 15:27:22 -0800423 void removeEncapSocketRecord(int resourceId) {
424 mEncapSocketRecords.remove(resourceId);
425 }
426
427 @Override
428 public String toString() {
429 return new StringBuilder()
430 .append("{mSpiQuotaTracker=")
431 .append(mSpiQuotaTracker)
432 .append(", mTransformQuotaTracker=")
433 .append(mTransformQuotaTracker)
434 .append(", mSocketQuotaTracker=")
435 .append(mSocketQuotaTracker)
Benedict Wong76603702018-01-24 15:31:39 -0800436 .append(", mTunnelQuotaTracker=")
437 .append(mTunnelQuotaTracker)
Benedict Wong6855aee2017-11-16 15:27:22 -0800438 .append(", mSpiRecords=")
439 .append(mSpiRecords)
440 .append(", mTransformRecords=")
441 .append(mTransformRecords)
442 .append(", mEncapSocketRecords=")
443 .append(mEncapSocketRecords)
Benedict Wong76603702018-01-24 15:31:39 -0800444 .append(", mTunnelInterfaceRecords=")
445 .append(mTunnelInterfaceRecords)
Benedict Wong6855aee2017-11-16 15:27:22 -0800446 .append("}")
447 .toString();
448 }
449 }
450
Benedict Wongcbd329b2017-12-13 17:16:53 -0800451 /**
452 * This class is not thread-safe, and expects that that users of this class will ensure
453 * synchronization and thread safety by holding the IpSecService.this instance lock.
454 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800455 @VisibleForTesting
456 static final class UserResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700457 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
458
Benedict Wongcbd329b2017-12-13 17:16:53 -0800459 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
Benedict Wong6855aee2017-11-16 15:27:22 -0800460 public UserRecord getUserRecord(int uid) {
461 checkCallerUid(uid);
462
Nathan Harold6e4681c2017-04-24 16:16:34 -0700463 UserRecord r = mUserRecords.get(uid);
464 if (r == null) {
465 r = new UserRecord();
466 mUserRecords.put(uid, r);
467 }
468 return r;
469 }
ludi529fdec2017-08-10 15:44:40 -0700470
Benedict Wong6855aee2017-11-16 15:27:22 -0800471 /** Safety method; guards against access of other user's UserRecords */
472 private void checkCallerUid(int uid) {
Benedict Wong908d34e2021-04-15 11:59:16 -0700473 if (uid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800474 throw new SecurityException("Attempted access of unowned resources");
475 }
476 }
477
ludi529fdec2017-08-10 15:44:40 -0700478 @Override
479 public String toString() {
480 return mUserRecords.toString();
481 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700482 }
483
Benedict Wong6855aee2017-11-16 15:27:22 -0800484 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700485
Nathan Harold80865392017-04-04 19:37:48 -0700486 /**
Benedict Wongcbd329b2017-12-13 17:16:53 -0800487 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
Benedict Wong6855aee2017-11-16 15:27:22 -0800488 * resources. It relies on a provided resourceId that should uniquely identify the kernel
489 * resource. To use this class, the user should implement the invalidate() and
490 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
Benedict Wongcbd329b2017-12-13 17:16:53 -0800491 * tracking arrays and kernel resources, respectively.
492 *
493 * <p>This class associates kernel resources with the UID that owns and controls them.
Nathan Harold80865392017-04-04 19:37:48 -0700494 */
Benedict Wongcbd329b2017-12-13 17:16:53 -0800495 private abstract class OwnedResourceRecord implements IResource {
Aaron Huangfbae3082021-12-06 15:18:42 +0800496 final int mPid;
497 final int mUid;
Benedict Wong6855aee2017-11-16 15:27:22 -0800498 protected final int mResourceId;
Nathan Harold031acb82017-03-07 13:23:36 -0800499
Benedict Wongcbd329b2017-12-13 17:16:53 -0800500 OwnedResourceRecord(int resourceId) {
Nathan Harold031acb82017-03-07 13:23:36 -0800501 super();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700502 if (resourceId == INVALID_RESOURCE_ID) {
503 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
504 }
Nathan Harold80865392017-04-04 19:37:48 -0700505 mResourceId = resourceId;
Aaron Huangfbae3082021-12-06 15:18:42 +0800506 mPid = Binder.getCallingPid();
507 mUid = Binder.getCallingUid();
Nathan Harold031acb82017-03-07 13:23:36 -0800508
Nathan Harold6e4681c2017-04-24 16:16:34 -0700509 getResourceTracker().take();
Nathan Harold031acb82017-03-07 13:23:36 -0800510 }
511
Benedict Wong6855aee2017-11-16 15:27:22 -0800512 @Override
513 public abstract void invalidate() throws RemoteException;
514
515 /** Convenience method; retrieves the user resource record for the stored UID. */
516 protected UserRecord getUserRecord() {
Aaron Huangfbae3082021-12-06 15:18:42 +0800517 return mUserResourceTracker.getUserRecord(mUid);
Nathan Harold80865392017-04-04 19:37:48 -0700518 }
519
Benedict Wong6855aee2017-11-16 15:27:22 -0800520 @Override
521 public abstract void freeUnderlyingResources() throws RemoteException;
ludi89194d62017-05-22 10:52:23 -0700522
Nathan Harold6e4681c2017-04-24 16:16:34 -0700523 /** Get the resource tracker for this resource */
524 protected abstract ResourceTracker getResourceTracker();
525
ludi89194d62017-05-22 10:52:23 -0700526 @Override
527 public String toString() {
528 return new StringBuilder()
529 .append("{mResourceId=")
530 .append(mResourceId)
531 .append(", pid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800532 .append(mPid)
ludi89194d62017-05-22 10:52:23 -0700533 .append(", uid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800534 .append(mUid)
ludi89194d62017-05-22 10:52:23 -0700535 .append("}")
536 .toString();
537 }
Nathan Harold031acb82017-03-07 13:23:36 -0800538 };
539
Nathan Harold80865392017-04-04 19:37:48 -0700540 /**
Benedict Wong6855aee2017-11-16 15:27:22 -0800541 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
542 *
543 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
544 * if a key is not found during a retrieval process.
Nathan Harold80865392017-04-04 19:37:48 -0700545 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800546 static class RefcountedResourceArray<T extends IResource> {
547 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
548 private final String mTypeName;
Nathan Harold031acb82017-03-07 13:23:36 -0800549
Aaron Huangfbae3082021-12-06 15:18:42 +0800550 RefcountedResourceArray(String typeName) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800551 this.mTypeName = typeName;
Nathan Harold80865392017-04-04 19:37:48 -0700552 }
553
Benedict Wong6855aee2017-11-16 15:27:22 -0800554 /**
555 * Accessor method to get inner resource object.
556 *
557 * @throws IllegalArgumentException if no resource with provided key is found.
558 */
559 T getResourceOrThrow(int key) {
560 return getRefcountedResourceOrThrow(key).getResource();
561 }
562
563 /**
564 * Accessor method to get reference counting wrapper.
565 *
566 * @throws IllegalArgumentException if no resource with provided key is found.
567 */
568 RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
569 RefcountedResource<T> resource = mArray.get(key);
570 if (resource == null) {
571 throw new IllegalArgumentException(
572 String.format("No such %s found for given id: %d", mTypeName, key));
573 }
574
575 return resource;
576 }
577
578 void put(int key, RefcountedResource<T> obj) {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +0000579 Objects.requireNonNull(obj, "Null resources cannot be added");
Nathan Harold80865392017-04-04 19:37:48 -0700580 mArray.put(key, obj);
581 }
582
583 void remove(int key) {
584 mArray.remove(key);
585 }
ludi89194d62017-05-22 10:52:23 -0700586
587 @Override
588 public String toString() {
589 return mArray.toString();
590 }
Nathan Harold80865392017-04-04 19:37:48 -0700591 }
592
Benedict Wongcbd329b2017-12-13 17:16:53 -0800593 /**
594 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
595 * created, the SpiRecord that originally tracked the SAs will reliquish the
596 * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
597 */
598 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700599 private final IpSecConfig mConfig;
Nathan Harold5a19b952018-01-05 19:25:13 -0800600 private final SpiRecord mSpi;
Benedict Wong6855aee2017-11-16 15:27:22 -0800601 private final EncapSocketRecord mSocket;
Nathan Harold80865392017-04-04 19:37:48 -0700602
603 TransformRecord(
Nathan Harold5a19b952018-01-05 19:25:13 -0800604 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800605 super(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -0800606 mConfig = config;
Nathan Harold5a19b952018-01-05 19:25:13 -0800607 mSpi = spi;
Nathan Harold80865392017-04-04 19:37:48 -0700608 mSocket = socket;
Benedict Wong68aac2a2017-12-13 18:26:40 -0800609
610 spi.setOwnedByTransform();
Nathan Harold031acb82017-03-07 13:23:36 -0800611 }
612
613 public IpSecConfig getConfig() {
614 return mConfig;
615 }
616
Nathan Harold5a19b952018-01-05 19:25:13 -0800617 public SpiRecord getSpiRecord() {
618 return mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700619 }
620
Benedict Wong8edc5572018-01-19 17:36:02 -0800621 public EncapSocketRecord getSocketRecord() {
622 return mSocket;
623 }
624
Nathan Harold80865392017-04-04 19:37:48 -0700625 /** always guarded by IpSecService#this */
Nathan Harold031acb82017-03-07 13:23:36 -0800626 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800627 public void freeUnderlyingResources() {
Nathan Harold5a19b952018-01-05 19:25:13 -0800628 int spi = mSpi.getSpi();
629 try {
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800630 mDeps
631 .getNetdInstance(mContext)
Nathan Harold5a19b952018-01-05 19:25:13 -0800632 .ipSecDeleteSecurityAssociation(
Aaron Huangfbae3082021-12-06 15:18:42 +0800633 mUid,
Nathan Harold5a19b952018-01-05 19:25:13 -0800634 mConfig.getSourceAddress(),
635 mConfig.getDestinationAddress(),
Di Lu3fef7042018-01-11 11:35:25 -0800636 spi,
637 mConfig.getMarkValue(),
Benedict Wong5d749842018-09-06 11:31:25 -0700638 mConfig.getMarkMask(),
639 mConfig.getXfrmInterfaceId());
Benedict Wong97c3c942018-03-01 18:53:07 -0800640 } catch (RemoteException | ServiceSpecificException e) {
641 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800642 }
Nathan Harold031acb82017-03-07 13:23:36 -0800643
Benedict Wong6855aee2017-11-16 15:27:22 -0800644 getResourceTracker().give();
Nathan Harold031acb82017-03-07 13:23:36 -0800645 }
ludi89194d62017-05-22 10:52:23 -0700646
Benedict Wong6855aee2017-11-16 15:27:22 -0800647 @Override
648 public void invalidate() throws RemoteException {
649 getUserRecord().removeTransformRecord(mResourceId);
650 }
651
652 @Override
Nathan Harold6e4681c2017-04-24 16:16:34 -0700653 protected ResourceTracker getResourceTracker() {
Benedict Wong6855aee2017-11-16 15:27:22 -0800654 return getUserRecord().mTransformQuotaTracker;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700655 }
656
ludi89194d62017-05-22 10:52:23 -0700657 @Override
658 public String toString() {
659 StringBuilder strBuilder = new StringBuilder();
660 strBuilder
661 .append("{super=")
662 .append(super.toString())
663 .append(", mSocket=")
664 .append(mSocket)
Nathan Harold5a19b952018-01-05 19:25:13 -0800665 .append(", mSpi.mResourceId=")
666 .append(mSpi.mResourceId)
ludi89194d62017-05-22 10:52:23 -0700667 .append(", mConfig=")
668 .append(mConfig)
669 .append("}");
670 return strBuilder.toString();
671 }
Nathan Harold031acb82017-03-07 13:23:36 -0800672 }
673
Benedict Wongcbd329b2017-12-13 17:16:53 -0800674 /**
675 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
676 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
677 * object
678 */
679 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harold5a19b952018-01-05 19:25:13 -0800680 private final String mSourceAddress;
681 private final String mDestinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800682 private int mSpi;
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800683 private final Context mContext;
Nathan Harold80865392017-04-04 19:37:48 -0700684
685 private boolean mOwnedByTransform = false;
Nathan Harold031acb82017-03-07 13:23:36 -0800686
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800687 SpiRecord(Context context, int resourceId, String sourceAddress,
688 String destinationAddress, int spi) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800689 super(resourceId);
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800690 mContext = context;
Nathan Harold5a19b952018-01-05 19:25:13 -0800691 mSourceAddress = sourceAddress;
692 mDestinationAddress = destinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800693 mSpi = spi;
Nathan Harold031acb82017-03-07 13:23:36 -0800694 }
695
Nathan Harold80865392017-04-04 19:37:48 -0700696 /** always guarded by IpSecService#this */
697 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800698 public void freeUnderlyingResources() {
Nathan Harold031acb82017-03-07 13:23:36 -0800699 try {
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800700 if (!mOwnedByTransform) {
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800701 mDeps
702 .getNetdInstance(mContext)
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800703 .ipSecDeleteSecurityAssociation(
Aaron Huangfbae3082021-12-06 15:18:42 +0800704 mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
Benedict Wong5d749842018-09-06 11:31:25 -0700705 0 /* mask */, 0 /* if_id */);
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800706 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800707 } catch (ServiceSpecificException | RemoteException e) {
708 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800709 }
Nathan Harold80865392017-04-04 19:37:48 -0700710
711 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -0800712
Benedict Wong6855aee2017-11-16 15:27:22 -0800713 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700714 }
715
Nathan Harold80865392017-04-04 19:37:48 -0700716 public int getSpi() {
717 return mSpi;
718 }
719
Nathan Harold5a19b952018-01-05 19:25:13 -0800720 public String getDestinationAddress() {
721 return mDestinationAddress;
722 }
723
Nathan Harold80865392017-04-04 19:37:48 -0700724 public void setOwnedByTransform() {
725 if (mOwnedByTransform) {
726 // Programming error
Andreas Gampeafb01e22017-07-11 10:25:09 -0700727 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold80865392017-04-04 19:37:48 -0700728 }
729
730 mOwnedByTransform = true;
Nathan Harold031acb82017-03-07 13:23:36 -0800731 }
ludi89194d62017-05-22 10:52:23 -0700732
Benedict Wong68aac2a2017-12-13 18:26:40 -0800733 public boolean getOwnedByTransform() {
734 return mOwnedByTransform;
735 }
736
ludi89194d62017-05-22 10:52:23 -0700737 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800738 public void invalidate() throws RemoteException {
739 getUserRecord().removeSpiRecord(mResourceId);
740 }
741
742 @Override
743 protected ResourceTracker getResourceTracker() {
744 return getUserRecord().mSpiQuotaTracker;
745 }
746
747 @Override
ludi89194d62017-05-22 10:52:23 -0700748 public String toString() {
749 StringBuilder strBuilder = new StringBuilder();
750 strBuilder
751 .append("{super=")
752 .append(super.toString())
753 .append(", mSpi=")
754 .append(mSpi)
Nathan Harold5a19b952018-01-05 19:25:13 -0800755 .append(", mSourceAddress=")
756 .append(mSourceAddress)
757 .append(", mDestinationAddress=")
758 .append(mDestinationAddress)
ludi89194d62017-05-22 10:52:23 -0700759 .append(", mOwnedByTransform=")
760 .append(mOwnedByTransform)
761 .append("}");
762 return strBuilder.toString();
763 }
Nathan Harold031acb82017-03-07 13:23:36 -0800764 }
765
Benedict Wong8bc90732018-01-18 18:31:45 -0800766 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
lucaslin7eb76592021-03-11 17:39:49 +0800767 final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
768 private int mNextTunnelNetId = mNetIdRange.getLower();
Benedict Wong8bc90732018-01-18 18:31:45 -0800769
770 /**
771 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
772 *
773 * <p>This method should only be called from Binder threads. Do not call this from within the
774 * system server as it will crash the system on failure.
775 *
776 * @return an integer key within the netId range, if successful
777 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
778 */
779 @VisibleForTesting
780 int reserveNetId() {
lucaslin7eb76592021-03-11 17:39:49 +0800781 final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
Benedict Wong8bc90732018-01-18 18:31:45 -0800782 synchronized (mTunnelNetIds) {
lucaslin7eb76592021-03-11 17:39:49 +0800783 for (int i = 0; i < range; i++) {
784 final int netId = mNextTunnelNetId;
785 if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
786 mNextTunnelNetId = mNetIdRange.getLower();
787 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800788 if (!mTunnelNetIds.get(netId)) {
789 mTunnelNetIds.put(netId, true);
790 return netId;
791 }
792 }
793 }
794 throw new IllegalStateException("No free netIds to allocate");
795 }
796
797 @VisibleForTesting
798 void releaseNetId(int netId) {
799 synchronized (mTunnelNetIds) {
800 mTunnelNetIds.delete(netId);
801 }
802 }
803
Yan Yana2f3b492020-09-29 23:38:00 -0700804 /**
805 * Tracks an tunnel interface, and manages cleanup paths.
806 *
807 * <p>This class is not thread-safe, and expects that that users of this class will ensure
808 * synchronization and thread safety by holding the IpSecService.this instance lock
809 */
810 @VisibleForTesting
811 final class TunnelInterfaceRecord extends OwnedResourceRecord {
Benedict Wong8bc90732018-01-18 18:31:45 -0800812 private final String mInterfaceName;
Benedict Wong8bc90732018-01-18 18:31:45 -0800813
814 // outer addresses
815 private final String mLocalAddress;
816 private final String mRemoteAddress;
817
818 private final int mIkey;
819 private final int mOkey;
820
Benedict Wong5d749842018-09-06 11:31:25 -0700821 private final int mIfId;
822
Yan Yana2f3b492020-09-29 23:38:00 -0700823 private Network mUnderlyingNetwork;
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800824 private final Context mContext;
Yan Yana2f3b492020-09-29 23:38:00 -0700825
Benedict Wong8bc90732018-01-18 18:31:45 -0800826 TunnelInterfaceRecord(
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800827 Context context,
Benedict Wong8bc90732018-01-18 18:31:45 -0800828 int resourceId,
829 String interfaceName,
830 Network underlyingNetwork,
831 String localAddr,
832 String remoteAddr,
833 int ikey,
Benedict Wong5d749842018-09-06 11:31:25 -0700834 int okey,
835 int intfId) {
Benedict Wong8bc90732018-01-18 18:31:45 -0800836 super(resourceId);
837
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800838 mContext = context;
Benedict Wong8bc90732018-01-18 18:31:45 -0800839 mInterfaceName = interfaceName;
840 mUnderlyingNetwork = underlyingNetwork;
841 mLocalAddress = localAddr;
842 mRemoteAddress = remoteAddr;
843 mIkey = ikey;
844 mOkey = okey;
Benedict Wong5d749842018-09-06 11:31:25 -0700845 mIfId = intfId;
Benedict Wong8bc90732018-01-18 18:31:45 -0800846 }
847
848 /** always guarded by IpSecService#this */
849 @Override
850 public void freeUnderlyingResources() {
Benedict Wong8edc5572018-01-19 17:36:02 -0800851 // Calls to netd
Benedict Wong8bc90732018-01-18 18:31:45 -0800852 // Teardown VTI
853 // Delete global policies
Benedict Wong8edc5572018-01-19 17:36:02 -0800854 try {
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800855 final INetd netd = mDeps.getNetdInstance(mContext);
Benedict Wong5d749842018-09-06 11:31:25 -0700856 netd.ipSecRemoveTunnelInterface(mInterfaceName);
Benedict Wong8edc5572018-01-19 17:36:02 -0800857
Benedict Wong38e52972018-05-07 20:06:44 -0700858 for (int selAddrFamily : ADDRESS_FAMILIES) {
859 netd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800860 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700861 selAddrFamily,
862 IpSecManager.DIRECTION_OUT,
863 mOkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700864 0xffffffff,
865 mIfId);
Benedict Wong38e52972018-05-07 20:06:44 -0700866 netd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800867 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700868 selAddrFamily,
869 IpSecManager.DIRECTION_IN,
870 mIkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700871 0xffffffff,
872 mIfId);
Benedict Wong8edc5572018-01-19 17:36:02 -0800873 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800874 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong8edc5572018-01-19 17:36:02 -0800875 Log.e(
876 TAG,
877 "Failed to delete VTI with interface name: "
878 + mInterfaceName
879 + " and id: "
Benedict Wong97c3c942018-03-01 18:53:07 -0800880 + mResourceId, e);
Benedict Wong8edc5572018-01-19 17:36:02 -0800881 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800882
883 getResourceTracker().give();
884 releaseNetId(mIkey);
885 releaseNetId(mOkey);
886 }
887
Yan Yana2f3b492020-09-29 23:38:00 -0700888 @GuardedBy("IpSecService.this")
889 public void setUnderlyingNetwork(Network underlyingNetwork) {
890 // When #applyTunnelModeTransform is called, this new underlying network will be used to
891 // update the output mark of the input transform.
892 mUnderlyingNetwork = underlyingNetwork;
Benedict Wong8bc90732018-01-18 18:31:45 -0800893 }
894
Yan Yana2f3b492020-09-29 23:38:00 -0700895 @GuardedBy("IpSecService.this")
Benedict Wong8bc90732018-01-18 18:31:45 -0800896 public Network getUnderlyingNetwork() {
897 return mUnderlyingNetwork;
898 }
899
Yan Yana2f3b492020-09-29 23:38:00 -0700900 public String getInterfaceName() {
901 return mInterfaceName;
902 }
903
Benedict Wong8bc90732018-01-18 18:31:45 -0800904 /** Returns the local, outer address for the tunnelInterface */
905 public String getLocalAddress() {
906 return mLocalAddress;
907 }
908
909 /** Returns the remote, outer address for the tunnelInterface */
910 public String getRemoteAddress() {
911 return mRemoteAddress;
912 }
913
914 public int getIkey() {
915 return mIkey;
916 }
917
918 public int getOkey() {
919 return mOkey;
920 }
921
Benedict Wong5d749842018-09-06 11:31:25 -0700922 public int getIfId() {
923 return mIfId;
924 }
925
Benedict Wong8bc90732018-01-18 18:31:45 -0800926 @Override
927 protected ResourceTracker getResourceTracker() {
928 return getUserRecord().mTunnelQuotaTracker;
929 }
930
931 @Override
932 public void invalidate() {
933 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
934 }
935
936 @Override
937 public String toString() {
938 return new StringBuilder()
939 .append("{super=")
940 .append(super.toString())
941 .append(", mInterfaceName=")
942 .append(mInterfaceName)
943 .append(", mUnderlyingNetwork=")
944 .append(mUnderlyingNetwork)
945 .append(", mLocalAddress=")
946 .append(mLocalAddress)
947 .append(", mRemoteAddress=")
948 .append(mRemoteAddress)
949 .append(", mIkey=")
950 .append(mIkey)
951 .append(", mOkey=")
952 .append(mOkey)
953 .append("}")
954 .toString();
955 }
956 }
957
Benedict Wongcbd329b2017-12-13 17:16:53 -0800958 /**
959 * Tracks a UDP encap socket, and manages cleanup paths
960 *
961 * <p>While this class does not manage non-kernel resources, race conditions around socket
962 * binding require that the service creates the encap socket, binds it and applies the socket
963 * policy before handing it to a user.
964 */
965 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700966 private FileDescriptor mSocket;
967 private final int mPort;
Nathan Harold031acb82017-03-07 13:23:36 -0800968
Benedict Wong6855aee2017-11-16 15:27:22 -0800969 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
970 super(resourceId);
Nathan Harold80865392017-04-04 19:37:48 -0700971 mSocket = socket;
972 mPort = port;
973 }
974
975 /** always guarded by IpSecService#this */
976 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800977 public void freeUnderlyingResources() {
Nathan Harold80865392017-04-04 19:37:48 -0700978 Log.d(TAG, "Closing port " + mPort);
979 IoUtils.closeQuietly(mSocket);
980 mSocket = null;
Nathan Harold80865392017-04-04 19:37:48 -0700981
Benedict Wong6855aee2017-11-16 15:27:22 -0800982 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700983 }
984
Nathan Harold80865392017-04-04 19:37:48 -0700985 public int getPort() {
986 return mPort;
987 }
988
Benedict Wonga386e372018-03-27 16:55:48 -0700989 public FileDescriptor getFileDescriptor() {
Nathan Harold80865392017-04-04 19:37:48 -0700990 return mSocket;
991 }
ludi89194d62017-05-22 10:52:23 -0700992
993 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800994 protected ResourceTracker getResourceTracker() {
995 return getUserRecord().mSocketQuotaTracker;
996 }
997
998 @Override
999 public void invalidate() {
1000 getUserRecord().removeEncapSocketRecord(mResourceId);
1001 }
1002
1003 @Override
ludi89194d62017-05-22 10:52:23 -07001004 public String toString() {
1005 return new StringBuilder()
1006 .append("{super=")
1007 .append(super.toString())
1008 .append(", mSocket=")
1009 .append(mSocket)
1010 .append(", mPort=")
1011 .append(mPort)
1012 .append("}")
1013 .toString();
1014 }
Nathan Harold80865392017-04-04 19:37:48 -07001015 }
Nathan Harold031acb82017-03-07 13:23:36 -08001016
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001017 /**
1018 * Constructs a new IpSecService instance
1019 *
1020 * @param context Binder context for this service
1021 */
lucaslind66e6082021-01-18 13:06:39 +08001022 private IpSecService(Context context) {
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001023 this(context, new Dependencies());
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001024 }
1025
lucaslind66e6082021-01-18 13:06:39 +08001026 static IpSecService create(Context context)
Benedict Wong529e8aa2020-02-11 23:49:36 -08001027 throws InterruptedException {
lucaslind66e6082021-01-18 13:06:39 +08001028 final IpSecService service = new IpSecService(context);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001029 service.connectNativeNetdService();
1030 return service;
1031 }
1032
Nathan Harold65ef8432018-03-15 18:06:06 -07001033 @NonNull
1034 private AppOpsManager getAppOpsManager() {
1035 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Aaron Huangfbae3082021-12-06 15:18:42 +08001036 if (appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
Nathan Harold65ef8432018-03-15 18:06:06 -07001037 return appOps;
1038 }
1039
ludi5e623ea2017-05-12 09:15:00 -07001040 /** @hide */
1041 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001042 public IpSecService(Context context, Dependencies deps) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001043 this(
1044 context,
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001045 deps,
Nathan Harold5a19b952018-01-05 19:25:13 -08001046 (fd, uid) -> {
1047 try {
1048 TrafficStats.setThreadStatsUid(uid);
1049 TrafficStats.tagFileDescriptor(fd);
1050 } finally {
1051 TrafficStats.clearThreadStatsUid();
1052 }
1053 });
Benedict Wong083faee2017-12-03 19:42:36 -08001054 }
1055
1056 /** @hide */
1057 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001058 public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
ludi5e623ea2017-05-12 09:15:00 -07001059 mContext = context;
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001060 mDeps = deps;
Benedict Wong083faee2017-12-03 19:42:36 -08001061 mUidFdTagger = uidFdTagger;
ludi5e623ea2017-05-12 09:15:00 -07001062 }
1063
Aaron Huangfbae3082021-12-06 15:18:42 +08001064 /** Called by system server when system is ready. */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001065 public void systemReady() {
1066 if (isNetdAlive()) {
paulhu00e34562021-10-26 09:00:50 +00001067 Log.d(TAG, "IpSecService is ready");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001068 } else {
paulhu00e34562021-10-26 09:00:50 +00001069 Log.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001070 }
1071 }
1072
1073 private void connectNativeNetdService() {
1074 // Avoid blocking the system server to do this
Nathan Harold12accd02017-07-17 14:01:53 -07001075 new Thread() {
1076 @Override
1077 public void run() {
1078 synchronized (IpSecService.this) {
ludi5e623ea2017-05-12 09:15:00 -07001079 NetdService.get(NETD_FETCH_TIMEOUT_MS);
Nathan Harold12accd02017-07-17 14:01:53 -07001080 }
1081 }
1082 }.start();
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001083 }
1084
Nathan Harold80865392017-04-04 19:37:48 -07001085 synchronized boolean isNetdAlive() {
1086 try {
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001087 final INetd netd = mDeps.getNetdInstance(mContext);
Nathan Harold80865392017-04-04 19:37:48 -07001088 if (netd == null) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001089 return false;
1090 }
Nathan Harold80865392017-04-04 19:37:48 -07001091 return netd.isAlive();
1092 } catch (RemoteException re) {
1093 return false;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001094 }
1095 }
1096
Nathan Harold19b99d92017-08-23 13:46:33 -07001097 /**
1098 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1099 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1100 */
1101 private static void checkInetAddress(String inetAddress) {
1102 if (TextUtils.isEmpty(inetAddress)) {
1103 throw new IllegalArgumentException("Unspecified address");
1104 }
1105
Serik Beketayev7f507332020-12-06 22:31:23 -08001106 InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
Nathan Harold19b99d92017-08-23 13:46:33 -07001107
1108 if (checkAddr.isAnyLocalAddress()) {
1109 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1110 }
1111 }
1112
1113 /**
1114 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1115 * DIRECTION_IN or DIRECTION_OUT
1116 */
Benedict Wong908d34e2021-04-15 11:59:16 -07001117 private void checkDirection(int direction) {
Nathan Harold19b99d92017-08-23 13:46:33 -07001118 switch (direction) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001119 case IpSecManager.DIRECTION_OUT:
1120 case IpSecManager.DIRECTION_IN:
Nathan Harold19b99d92017-08-23 13:46:33 -07001121 return;
Benedict Wong908d34e2021-04-15 11:59:16 -07001122 case IpSecManager.DIRECTION_FWD:
Benedict Wong47b528c2021-05-10 18:26:02 -07001123 // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
Benedict Wong908d34e2021-04-15 11:59:16 -07001124 PermissionUtils.enforceNetworkStackPermission(mContext);
1125 return;
Nathan Harold19b99d92017-08-23 13:46:33 -07001126 }
1127 throw new IllegalArgumentException("Invalid Direction: " + direction);
1128 }
1129
Nathan Harold031acb82017-03-07 13:23:36 -08001130 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri20e96c52017-11-16 10:58:01 -08001131 @Override
1132 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harold5a19b952018-01-05 19:25:13 -08001133 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1134 checkInetAddress(destinationAddress);
Nathan Harold1b88f0e2018-03-28 08:52:51 -07001135 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1136 if (requestedSpi > 0 && requestedSpi < 256) {
1137 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1138 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001139 Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harold19b99d92017-08-23 13:46:33 -07001140
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001141 int callingUid = Binder.getCallingUid();
1142 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001143 final int resourceId = mNextResourceId++;
Nathan Harold031acb82017-03-07 13:23:36 -08001144
1145 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -08001146 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001147 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001148 return new IpSecSpiResponse(
Nathan Harold19b99d92017-08-23 13:46:33 -07001149 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold6e4681c2017-04-24 16:16:34 -07001150 }
Nathan Harold5a19b952018-01-05 19:25:13 -08001151
Nathan Harold031acb82017-03-07 13:23:36 -08001152 spi =
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001153 mDeps
1154 .getNetdInstance(mContext)
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001155 .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold031acb82017-03-07 13:23:36 -08001156 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong6855aee2017-11-16 15:27:22 -08001157 userRecord.mSpiRecords.put(
Nathan Harold80865392017-04-04 19:37:48 -07001158 resourceId,
Benedict Wong6855aee2017-11-16 15:27:22 -08001159 new RefcountedResource<SpiRecord>(
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001160 new SpiRecord(mContext, resourceId, "",
1161 destinationAddress, spi), binder));
Nathan Harold031acb82017-03-07 13:23:36 -08001162 } catch (ServiceSpecificException e) {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001163 if (e.errorCode == OsConstants.ENOENT) {
1164 return new IpSecSpiResponse(
1165 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1166 }
1167 throw e;
Nathan Harold031acb82017-03-07 13:23:36 -08001168 } catch (RemoteException e) {
1169 throw e.rethrowFromSystemServer();
1170 }
Nathan Harold80865392017-04-04 19:37:48 -07001171 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1172 }
1173
1174 /* This method should only be called from Binder threads. Do not call this from
1175 * within the system server as it will crash the system on failure.
1176 */
Benedict Wong6855aee2017-11-16 15:27:22 -08001177 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold80865392017-04-04 19:37:48 -07001178 throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001179 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold031acb82017-03-07 13:23:36 -08001180 }
1181
1182 /** Release a previously allocated SPI that has been registered with the system server */
1183 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001184 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1185 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1186 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001187 }
1188
1189 /**
1190 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1191 * be unbound.
1192 *
1193 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1194 * a random open port and then bind by number, this function creates a temp socket, binds to a
1195 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1196 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1197 * FileHandle.
1198 *
1199 * <p>The loop in this function handles the inherent race window between un-binding to a port
1200 * and re-binding, during which the system could *technically* hand that port out to someone
1201 * else.
1202 */
Benedict Wongc423cc82017-10-10 20:44:28 -07001203 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold80865392017-04-04 19:37:48 -07001204 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1205 try {
1206 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1207 Os.bind(probeSocket, INADDR_ANY, 0);
1208 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1209 Os.close(probeSocket);
1210 Log.v(TAG, "Binding to port " + port);
1211 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongc423cc82017-10-10 20:44:28 -07001212 return port;
Nathan Harold80865392017-04-04 19:37:48 -07001213 } catch (ErrnoException e) {
1214 // Someone miraculously claimed the port just after we closed probeSocket.
1215 if (e.errno == OsConstants.EADDRINUSE) {
1216 continue;
1217 }
1218 throw e.rethrowAsIOException();
1219 }
1220 }
1221 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1222 }
Nathan Harold031acb82017-03-07 13:23:36 -08001223
1224 /**
Benedict Wong083faee2017-12-03 19:42:36 -08001225 * Functional interface to do traffic tagging of given sockets to UIDs.
1226 *
1227 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1228 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1229 *
1230 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1231 * methods that cannot be easily mocked/tested.
1232 */
1233 @VisibleForTesting
1234 public interface UidFdTagger {
1235 /**
1236 * Sets socket tag to assign all traffic to the provided UID.
1237 *
1238 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1239 * should be accounted to the UID of the unprivileged application.
1240 */
Aaron Huangfbae3082021-12-06 15:18:42 +08001241 void tag(FileDescriptor fd, int uid) throws IOException;
Benedict Wong083faee2017-12-03 19:42:36 -08001242 }
1243
1244 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001245 * Open a socket via the system server and bind it to the specified port (random if port=0).
1246 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1247 * cache the socket and a record of its owner so that it can and must be freed when no longer
1248 * needed.
1249 */
1250 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001251 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1252 throws RemoteException {
1253 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1254 throw new IllegalArgumentException(
1255 "Specified port number must be a valid non-reserved UDP port");
1256 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001257 Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
Nathan Harold19b99d92017-08-23 13:46:33 -07001258
Benedict Wong083faee2017-12-03 19:42:36 -08001259 int callingUid = Binder.getCallingUid();
1260 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001261 final int resourceId = mNextResourceId++;
Nathan Harold80865392017-04-04 19:37:48 -07001262 FileDescriptor sockFd = null;
1263 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001264 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001265 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1266 }
1267
Nathan Harold80865392017-04-04 19:37:48 -07001268 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Benedict Wong083faee2017-12-03 19:42:36 -08001269 mUidFdTagger.tag(sockFd, callingUid);
Nathan Harold80865392017-04-04 19:37:48 -07001270
Nathan Harold80865392017-04-04 19:37:48 -07001271 // This code is common to both the unspecified and specified port cases
1272 Os.setsockoptInt(
1273 sockFd,
1274 OsConstants.IPPROTO_UDP,
1275 OsConstants.UDP_ENCAP,
1276 OsConstants.UDP_ENCAP_ESPINUDP);
1277
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001278 mDeps.getNetdInstance(mContext).ipSecSetEncapSocketOwner(
Luke Huangd913fb42018-11-23 12:01:41 +08001279 new ParcelFileDescriptor(sockFd), callingUid);
Benedict Wong17687442017-12-06 21:56:35 -08001280 if (port != 0) {
1281 Log.v(TAG, "Binding to port " + port);
1282 Os.bind(sockFd, INADDR_ANY, port);
1283 } else {
1284 port = bindToRandomPort(sockFd);
1285 }
1286
Benedict Wong6855aee2017-11-16 15:27:22 -08001287 userRecord.mEncapSocketRecords.put(
1288 resourceId,
1289 new RefcountedResource<EncapSocketRecord>(
1290 new EncapSocketRecord(resourceId, sockFd, port), binder));
Nathan Harold80865392017-04-04 19:37:48 -07001291 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
1292 } catch (IOException | ErrnoException e) {
1293 IoUtils.closeQuietly(sockFd);
1294 }
1295 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1296 // The only reasonable condition that would cause that is resource unavailable.
1297 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold031acb82017-03-07 13:23:36 -08001298 }
1299
1300 /** close a socket that has been been allocated by and registered with the system server */
1301 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001302 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1303 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1304 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001305 }
Nathan Harold031acb82017-03-07 13:23:36 -08001306
Benedict Wong8bc90732018-01-18 18:31:45 -08001307 /**
1308 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1309 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1310 * needed.
1311 */
1312 @Override
1313 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001314 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1315 String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001316 enforceTunnelFeatureAndPermissions(callingPackage);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001317 Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
1318 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
Benedict Wong8bc90732018-01-18 18:31:45 -08001319 checkInetAddress(localAddr);
1320 checkInetAddress(remoteAddr);
1321
1322 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1323 // network (b/72316676).
1324
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001325 int callerUid = Binder.getCallingUid();
1326 UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001327 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1328 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1329 }
1330
1331 final int resourceId = mNextResourceId++;
1332 final int ikey = reserveNetId();
1333 final int okey = reserveNetId();
Nathan Harold7be7f452018-04-26 11:47:14 -07001334 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001335
Benedict Wong8edc5572018-01-19 17:36:02 -08001336 try {
1337 // Calls to netd:
1338 // Create VTI
1339 // Add inbound/outbound global policies
1340 // (use reqid = 0)
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001341 final INetd netd = mDeps.getNetdInstance(mContext);
Benedict Wong5d749842018-09-06 11:31:25 -07001342 netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001343
paulhu00e34562021-10-26 09:00:50 +00001344 BinderUtils.withCleanCallingIdentity(() -> {
lucaslinff6fe7b2021-02-03 23:59:45 +08001345 NetdUtils.setInterfaceUp(netd, intfName);
Benedict Wong529e8aa2020-02-11 23:49:36 -08001346 });
1347
Benedict Wong38e52972018-05-07 20:06:44 -07001348 for (int selAddrFamily : ADDRESS_FAMILIES) {
1349 // Always send down correct local/remote addresses for template.
1350 netd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001351 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001352 selAddrFamily,
1353 IpSecManager.DIRECTION_OUT,
1354 localAddr,
1355 remoteAddr,
1356 0,
1357 okey,
Benedict Wong5d749842018-09-06 11:31:25 -07001358 0xffffffff,
1359 resourceId);
Benedict Wong38e52972018-05-07 20:06:44 -07001360 netd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001361 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001362 selAddrFamily,
1363 IpSecManager.DIRECTION_IN,
1364 remoteAddr,
1365 localAddr,
1366 0,
1367 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001368 0xffffffff,
1369 resourceId);
Benedict Wong47b528c2021-05-10 18:26:02 -07001370
1371 // Add a forwarding policy on the tunnel interface. In order to support forwarding
1372 // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
1373 //
1374 // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
1375 // forwarding will be blocked by default (as would be the case if this policy was
1376 // absent).
1377 //
1378 // This is necessary only on the tunnel interface, and not any the interface to
1379 // which traffic will be forwarded to.
Benedict Wong908d34e2021-04-15 11:59:16 -07001380 netd.ipSecAddSecurityPolicy(
1381 callerUid,
1382 selAddrFamily,
1383 IpSecManager.DIRECTION_FWD,
1384 remoteAddr,
1385 localAddr,
1386 0,
1387 ikey,
1388 0xffffffff,
1389 resourceId);
Benedict Wong8edc5572018-01-19 17:36:02 -08001390 }
1391
1392 userRecord.mTunnelInterfaceRecords.put(
1393 resourceId,
1394 new RefcountedResource<TunnelInterfaceRecord>(
1395 new TunnelInterfaceRecord(
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001396 mContext,
Benedict Wong8edc5572018-01-19 17:36:02 -08001397 resourceId,
1398 intfName,
1399 underlyingNetwork,
1400 localAddr,
1401 remoteAddr,
1402 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001403 okey,
1404 resourceId),
Benedict Wong8edc5572018-01-19 17:36:02 -08001405 binder));
1406 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1407 } catch (RemoteException e) {
1408 // Release keys if we got an error.
1409 releaseNetId(ikey);
1410 releaseNetId(okey);
1411 throw e.rethrowFromSystemServer();
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001412 } catch (Throwable t) {
1413 // Release keys if we got an error.
1414 releaseNetId(ikey);
1415 releaseNetId(okey);
1416 throw t;
Benedict Wong8edc5572018-01-19 17:36:02 -08001417 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001418 }
1419
1420 /**
1421 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1422 * from multiple local IP addresses over the same tunnel.
1423 */
1424 @Override
Benedict Wong97c3c942018-03-01 18:53:07 -08001425 public synchronized void addAddressToTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001426 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001427 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001428 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1429
1430 // Get tunnelInterface record; if no such interface is found, will throw
1431 // IllegalArgumentException
1432 TunnelInterfaceRecord tunnelInterfaceInfo =
1433 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1434
Benedict Wong97c3c942018-03-01 18:53:07 -08001435 try {
1436 // We can assume general validity of the IP address, since we get them as a
1437 // LinkAddress, which does some validation.
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001438 mDeps
1439 .getNetdInstance(mContext)
Benedict Wong97c3c942018-03-01 18:53:07 -08001440 .interfaceAddAddress(
1441 tunnelInterfaceInfo.mInterfaceName,
1442 localAddr.getAddress().getHostAddress(),
1443 localAddr.getPrefixLength());
1444 } catch (RemoteException e) {
1445 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001446 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001447 }
1448
1449 /**
1450 * Remove a new local address from the tunnel interface. After removal, the address will no
1451 * longer be available to send from, or receive on.
1452 */
1453 @Override
1454 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001455 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001456 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001457
Nathan Harold65ef8432018-03-15 18:06:06 -07001458 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8bc90732018-01-18 18:31:45 -08001459 // Get tunnelInterface record; if no such interface is found, will throw
1460 // IllegalArgumentException
1461 TunnelInterfaceRecord tunnelInterfaceInfo =
1462 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1463
Benedict Wong97c3c942018-03-01 18:53:07 -08001464 try {
1465 // We can assume general validity of the IP address, since we get them as a
1466 // LinkAddress, which does some validation.
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001467 mDeps
1468 .getNetdInstance(mContext)
Benedict Wong97c3c942018-03-01 18:53:07 -08001469 .interfaceDelAddress(
1470 tunnelInterfaceInfo.mInterfaceName,
1471 localAddr.getAddress().getHostAddress(),
1472 localAddr.getPrefixLength());
1473 } catch (RemoteException e) {
1474 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001475 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001476 }
1477
Yan Yana2f3b492020-09-29 23:38:00 -07001478 /** Set TunnelInterface to use a specific underlying network. */
1479 @Override
1480 public synchronized void setNetworkForTunnelInterface(
1481 int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
1482 enforceTunnelFeatureAndPermissions(callingPackage);
1483 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1484
1485 final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1486
1487 // Get tunnelInterface record; if no such interface is found, will throw
1488 // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
1489 final TunnelInterfaceRecord tunnelInterfaceInfo =
1490 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1491
1492 final ConnectivityManager connectivityManager =
1493 mContext.getSystemService(ConnectivityManager.class);
1494 final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
1495 if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
1496 throw new IllegalArgumentException(
1497 "Underlying network cannot be the network being exposed by this tunnel");
1498 }
1499
1500 // It is meaningless to check if the network exists or is valid because the network might
1501 // disconnect at any time after it passes the check.
1502
1503 tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
1504 }
1505
Benedict Wong8bc90732018-01-18 18:31:45 -08001506 /**
1507 * Delete a TunnelInterface that has been been allocated by and registered with the system
1508 * server
1509 */
1510 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001511 public synchronized void deleteTunnelInterface(
1512 int resourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001513 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001514 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1515 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1516 }
1517
Benedict Wong70867e52017-11-06 20:49:10 -08001518 @VisibleForTesting
Nathan Harold5a19b952018-01-05 19:25:13 -08001519 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1520 IpSecAlgorithm auth = config.getAuthentication();
1521 IpSecAlgorithm crypt = config.getEncryption();
1522 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong70867e52017-11-06 20:49:10 -08001523
Nathan Harold5a19b952018-01-05 19:25:13 -08001524 // Validate the algorithm set
1525 Preconditions.checkArgument(
1526 aead != null || crypt != null || auth != null,
1527 "No Encryption or Authentication algorithms specified");
1528 Preconditions.checkArgument(
1529 auth == null || auth.isAuthentication(),
1530 "Unsupported algorithm for Authentication");
1531 Preconditions.checkArgument(
Benedict Wong70867e52017-11-06 20:49:10 -08001532 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harold5a19b952018-01-05 19:25:13 -08001533 Preconditions.checkArgument(
1534 aead == null || aead.isAead(),
1535 "Unsupported algorithm for Authenticated Encryption");
1536 Preconditions.checkArgument(
1537 aead == null || (auth == null && crypt == null),
1538 "Authenticated Encryption is mutually exclusive with other Authentication "
1539 + "or Encryption algorithms");
Benedict Wong70867e52017-11-06 20:49:10 -08001540 }
1541
evitayan43d93a02018-03-22 17:53:08 -07001542 private int getFamily(String inetAddress) {
1543 int family = AF_UNSPEC;
Serik Beketayev7f507332020-12-06 22:31:23 -08001544 InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
evitayan43d93a02018-03-22 17:53:08 -07001545 if (checkAddress instanceof Inet4Address) {
1546 family = AF_INET;
1547 } else if (checkAddress instanceof Inet6Address) {
1548 family = AF_INET6;
1549 }
1550 return family;
1551 }
1552
Nathan Harold031acb82017-03-07 13:23:36 -08001553 /**
Chiachang Wang2fea4a72020-08-12 12:23:59 +08001554 * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
Nathan Harold19b99d92017-08-23 13:46:33 -07001555 * IllegalArgumentException if they are not.
1556 */
1557 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001558 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1559
Nathan Harold19b99d92017-08-23 13:46:33 -07001560 switch (config.getEncapType()) {
1561 case IpSecTransform.ENCAP_NONE:
1562 break;
1563 case IpSecTransform.ENCAP_ESPINUDP:
1564 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong6855aee2017-11-16 15:27:22 -08001565 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1566 userRecord.mEncapSocketRecords.getResourceOrThrow(
1567 config.getEncapSocketResourceId());
Nathan Harold19b99d92017-08-23 13:46:33 -07001568
1569 int port = config.getEncapRemotePort();
1570 if (port <= 0 || port > 0xFFFF) {
1571 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1572 }
1573 break;
1574 default:
1575 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1576 }
1577
Nathan Harold5a19b952018-01-05 19:25:13 -08001578 validateAlgorithms(config);
Nathan Harold19b99d92017-08-23 13:46:33 -07001579
Nathan Harold5a19b952018-01-05 19:25:13 -08001580 // Retrieve SPI record; will throw IllegalArgumentException if not found
1581 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1582
Benedict Wong68aac2a2017-12-13 18:26:40 -08001583 // Check to ensure that SPI has not already been used.
1584 if (s.getOwnedByTransform()) {
1585 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1586 }
1587
Nathan Harold5a19b952018-01-05 19:25:13 -08001588 // If no remote address is supplied, then use one from the SPI.
1589 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1590 config.setDestinationAddress(s.getDestinationAddress());
1591 }
1592
1593 // All remote addresses must match
1594 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1595 throw new IllegalArgumentException("Mismatched remote addresseses.");
1596 }
1597
1598 // This check is technically redundant due to the chain of custody between the SPI and
1599 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1600 // the transform, this will prevent us from messing up.
1601 checkInetAddress(config.getDestinationAddress());
1602
1603 // Require a valid source address for all transforms.
1604 checkInetAddress(config.getSourceAddress());
1605
evitayan43d93a02018-03-22 17:53:08 -07001606 // Check to ensure source and destination have the same address family.
1607 String sourceAddress = config.getSourceAddress();
1608 String destinationAddress = config.getDestinationAddress();
1609 int sourceFamily = getFamily(sourceAddress);
1610 int destinationFamily = getFamily(destinationAddress);
1611 if (sourceFamily != destinationFamily) {
1612 throw new IllegalArgumentException(
1613 "Source address ("
1614 + sourceAddress
1615 + ") and destination address ("
1616 + destinationAddress
1617 + ") have different address families.");
1618 }
1619
1620 // Throw an error if UDP Encapsulation is not used in IPv4.
1621 if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
1622 throw new IllegalArgumentException(
1623 "UDP Encapsulation is not supported for this address family");
1624 }
1625
Nathan Harold5a19b952018-01-05 19:25:13 -08001626 switch (config.getMode()) {
1627 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold025aae12018-02-02 18:34:25 -08001628 break;
Nathan Harold5a19b952018-01-05 19:25:13 -08001629 case IpSecTransform.MODE_TUNNEL:
1630 break;
1631 default:
1632 throw new IllegalArgumentException(
1633 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harold19b99d92017-08-23 13:46:33 -07001634 }
Benedict Wong683441d2018-07-25 18:46:19 -07001635
1636 config.setMarkValue(0);
1637 config.setMarkMask(0);
Nathan Harold19b99d92017-08-23 13:46:33 -07001638 }
1639
Benedict Wong2b6a14e2018-09-13 16:45:12 -07001640 private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
Nathan Harold7c250ae2018-05-15 19:18:38 -07001641
Benedict Wonge9763752018-11-08 19:45:34 -08001642 private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1643 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1644 throw new UnsupportedOperationException(
1645 "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1646 }
1647
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001648 Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Benedict Wongc85b7b02019-11-12 22:31:51 -08001649
1650 // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
1651 // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
1652 // permission or is the System Server.
1653 if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
1654 TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1655 return;
Nathan Harold65ef8432018-03-15 18:06:06 -07001656 }
Benedict Wongc85b7b02019-11-12 22:31:51 -08001657 mContext.enforceCallingOrSelfPermission(
1658 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
Nathan Harold025aae12018-02-02 18:34:25 -08001659 }
1660
Benedict Wong8edc5572018-01-19 17:36:02 -08001661 private void createOrUpdateTransform(
1662 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1663 throws RemoteException {
1664
1665 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1666 if (encapType != IpSecTransform.ENCAP_NONE) {
1667 encapLocalPort = socketRecord.getPort();
1668 encapRemotePort = c.getEncapRemotePort();
1669 }
1670
1671 IpSecAlgorithm auth = c.getAuthentication();
1672 IpSecAlgorithm crypt = c.getEncryption();
1673 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1674
Benedict Wong778327e2018-03-15 19:41:41 -07001675 String cryptName;
1676 if (crypt == null) {
1677 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1678 } else {
1679 cryptName = crypt.getName();
1680 }
1681
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001682 mDeps
1683 .getNetdInstance(mContext)
Benedict Wong8edc5572018-01-19 17:36:02 -08001684 .ipSecAddSecurityAssociation(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001685 Binder.getCallingUid(),
Benedict Wong8edc5572018-01-19 17:36:02 -08001686 c.getMode(),
1687 c.getSourceAddress(),
1688 c.getDestinationAddress(),
lucaslin20966d92021-03-19 16:06:33 +08001689 (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
Benedict Wong8edc5572018-01-19 17:36:02 -08001690 spiRecord.getSpi(),
1691 c.getMarkValue(),
1692 c.getMarkMask(),
1693 (auth != null) ? auth.getName() : "",
1694 (auth != null) ? auth.getKey() : new byte[] {},
1695 (auth != null) ? auth.getTruncationLengthBits() : 0,
Benedict Wong778327e2018-03-15 19:41:41 -07001696 cryptName,
Benedict Wong8edc5572018-01-19 17:36:02 -08001697 (crypt != null) ? crypt.getKey() : new byte[] {},
1698 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1699 (authCrypt != null) ? authCrypt.getName() : "",
1700 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1701 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1702 encapType,
1703 encapLocalPort,
Benedict Wong5d749842018-09-06 11:31:25 -07001704 encapRemotePort,
1705 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001706 }
1707
Nathan Harold19b99d92017-08-23 13:46:33 -07001708 /**
Benedict Wong8edc5572018-01-19 17:36:02 -08001709 * Create a IPsec transform, which represents a single security association in the kernel. The
1710 * transform will be cached by the system server and must be freed when no longer needed. It is
1711 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1712 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold031acb82017-03-07 13:23:36 -08001713 */
1714 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001715 public synchronized IpSecTransformResponse createTransform(
1716 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001717 Objects.requireNonNull(c);
Nathan Harold65ef8432018-03-15 18:06:06 -07001718 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
Benedict Wonge9763752018-11-08 19:45:34 -08001719 enforceTunnelFeatureAndPermissions(callingPackage);
Nathan Harold65ef8432018-03-15 18:06:06 -07001720 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001721 checkIpSecConfig(c);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001722 Objects.requireNonNull(binder, "Null Binder passed to createTransform");
Nathan Haroldfdafce22017-12-13 19:16:33 -08001723 final int resourceId = mNextResourceId++;
Benedict Wong6855aee2017-11-16 15:27:22 -08001724
1725 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wongcbd329b2017-12-13 17:16:53 -08001726 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong6855aee2017-11-16 15:27:22 -08001727
1728 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001729 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1730 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001731
Benedict Wong6855aee2017-11-16 15:27:22 -08001732 EncapSocketRecord socketRecord = null;
Benedict Wong8edc5572018-01-19 17:36:02 -08001733 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001734 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1735 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1736 c.getEncapSocketResourceId());
1737 dependencies.add(refcountedSocketRecord);
Benedict Wong6855aee2017-11-16 15:27:22 -08001738 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold80865392017-04-04 19:37:48 -07001739 }
1740
Nathan Harold5a19b952018-01-05 19:25:13 -08001741 RefcountedResource<SpiRecord> refcountedSpiRecord =
1742 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1743 dependencies.add(refcountedSpiRecord);
1744 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong6855aee2017-11-16 15:27:22 -08001745
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001746 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong8edc5572018-01-19 17:36:02 -08001747
1748 // SA was created successfully, time to construct a record and lock it away
Benedict Wong6855aee2017-11-16 15:27:22 -08001749 userRecord.mTransformRecords.put(
1750 resourceId,
1751 new RefcountedResource<TransformRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001752 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong6855aee2017-11-16 15:27:22 -08001753 binder,
1754 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold80865392017-04-04 19:37:48 -07001755 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001756 }
1757
1758 /**
1759 * Delete a transport mode transform that was previously allocated by + registered with the
1760 * system server. If this is called on an inactive (or non-existent) transform, it will not
1761 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1762 * other reasons.
1763 */
1764 @Override
Benedict Wong0fff56e2018-01-18 14:38:16 -08001765 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001766 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1767 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001768 }
1769
1770 /**
1771 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1772 * association as a correspondent policy to the provided socket
1773 */
1774 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001775 public synchronized void applyTransportModeTransform(
Nathan Harold5a19b952018-01-05 19:25:13 -08001776 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001777 int callingUid = Binder.getCallingUid();
1778 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harold5a19b952018-01-05 19:25:13 -08001779 checkDirection(direction);
Benedict Wong6855aee2017-11-16 15:27:22 -08001780 // Get transform record; if no transform is found, will throw IllegalArgumentException
1781 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001782
Nathan Harold80865392017-04-04 19:37:48 -07001783 // TODO: make this a function.
Aaron Huangfbae3082021-12-06 15:18:42 +08001784 if (info.mPid != getCallingPid() || info.mUid != callingUid) {
Nathan Harold80865392017-04-04 19:37:48 -07001785 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1786 }
1787
Benedict Wong8bc90732018-01-18 18:31:45 -08001788 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold80865392017-04-04 19:37:48 -07001789 IpSecConfig c = info.getConfig();
Benedict Wong8bc90732018-01-18 18:31:45 -08001790 Preconditions.checkArgument(
1791 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1792 "Transform mode was not Transport mode; cannot be applied to a socket");
1793
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001794 mDeps
1795 .getNetdInstance(mContext)
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001796 .ipSecApplyTransportModeTransform(
Luke Huangd913fb42018-11-23 12:01:41 +08001797 socket,
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001798 callingUid,
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001799 direction,
1800 c.getSourceAddress(),
1801 c.getDestinationAddress(),
1802 info.getSpiRecord().getSpi());
Nathan Harold031acb82017-03-07 13:23:36 -08001803 }
Nathan Harold80865392017-04-04 19:37:48 -07001804
Nathan Harold031acb82017-03-07 13:23:36 -08001805 /**
Nathan Harold5a19b952018-01-05 19:25:13 -08001806 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1807 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1808 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1809 * reserved for future improved input validation.
Nathan Harold031acb82017-03-07 13:23:36 -08001810 */
1811 @Override
Nathan Harold0d483b72018-01-17 01:00:20 -08001812 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1813 throws RemoteException {
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001814 mDeps
1815 .getNetdInstance(mContext)
Luke Huangd913fb42018-11-23 12:01:41 +08001816 .ipSecRemoveTransportModeTransform(socket);
Nathan Harold031acb82017-03-07 13:23:36 -08001817 }
1818
Benedict Wong8bc90732018-01-18 18:31:45 -08001819 /**
1820 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1821 * security association as a correspondent policy to the provided interface
1822 */
1823 @Override
1824 public synchronized void applyTunnelModeTransform(
Nathan Harold65ef8432018-03-15 18:06:06 -07001825 int tunnelResourceId, int direction,
1826 int transformResourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001827 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001828 checkDirection(direction);
1829
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001830 int callingUid = Binder.getCallingUid();
1831 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001832
1833 // Get transform record; if no transform is found, will throw IllegalArgumentException
1834 TransformRecord transformInfo =
1835 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1836
1837 // Get tunnelInterface record; if no such interface is found, will throw
1838 // IllegalArgumentException
1839 TunnelInterfaceRecord tunnelInterfaceInfo =
1840 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1841
1842 // Get config and check that to-be-applied transform has the correct mode
1843 IpSecConfig c = transformInfo.getConfig();
1844 Preconditions.checkArgument(
1845 c.getMode() == IpSecTransform.MODE_TUNNEL,
1846 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1847
Benedict Wong8edc5572018-01-19 17:36:02 -08001848 EncapSocketRecord socketRecord = null;
1849 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1850 socketRecord =
1851 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1852 }
Benedict Wongec2e2e22019-10-03 11:09:00 -07001853 SpiRecord spiRecord = transformInfo.getSpiRecord();
Benedict Wong8edc5572018-01-19 17:36:02 -08001854
Benedict Wong8bc90732018-01-18 18:31:45 -08001855 int mark =
Benedict Wong38e52972018-05-07 20:06:44 -07001856 (direction == IpSecManager.DIRECTION_OUT)
1857 ? tunnelInterfaceInfo.getOkey()
Benedict Wong908d34e2021-04-15 11:59:16 -07001858 : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
Benedict Wong8bc90732018-01-18 18:31:45 -08001859
Benedict Wong8edc5572018-01-19 17:36:02 -08001860 try {
Benedict Wong5d749842018-09-06 11:31:25 -07001861 // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
1862 // SPI matching as part of the template resolution.
1863 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1864 c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
1865
Benedict Wong683441d2018-07-25 18:46:19 -07001866 // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
1867 // (and backporting) would allow us to narrow the mark space, and ensure that the SA
1868 // and SPs have matching marks (as VTI are meant to be built).
1869 // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
1870 // config matches the actual allocated resources in the kernel.
Benedict Wongc6fcedd2018-11-21 21:24:55 -08001871 // All SAs will have zero marks (from creation time), and any policy that matches the
1872 // same src/dst could match these SAs. Non-IpSecService governed processes that
1873 // establish floating policies with the same src/dst may result in undefined
1874 // behavior. This is generally limited to vendor code due to the permissions
1875 // (CAP_NET_ADMIN) required.
Benedict Wong683441d2018-07-25 18:46:19 -07001876 //
1877 // c.setMarkValue(mark);
1878 // c.setMarkMask(0xffffffff);
Benedict Wong8edc5572018-01-19 17:36:02 -08001879
1880 if (direction == IpSecManager.DIRECTION_OUT) {
1881 // Set output mark via underlying network (output only)
1882 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1883
Benedict Wong5d749842018-09-06 11:31:25 -07001884 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
1885 // but want to guarantee outbound packets are sent over the new SA.
Benedict Wongec2e2e22019-10-03 11:09:00 -07001886 spi = spiRecord.getSpi();
Benedict Wong5d749842018-09-06 11:31:25 -07001887 }
1888
1889 // Always update the policy with the relevant XFRM_IF_ID
1890 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001891 mDeps
1892 .getNetdInstance(mContext)
Benedict Wong5d749842018-09-06 11:31:25 -07001893 .ipSecUpdateSecurityPolicy(
1894 callingUid,
1895 selAddrFamily,
1896 direction,
1897 transformInfo.getConfig().getSourceAddress(),
1898 transformInfo.getConfig().getDestinationAddress(),
1899 spi, // If outbound, also add SPI to the policy.
1900 mark, // Must always set policy mark; ikey/okey for VTIs
1901 0xffffffff,
1902 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001903 }
1904
1905 // Update SA with tunnel mark (ikey or okey based on direction)
1906 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1907 } catch (ServiceSpecificException e) {
1908 if (e.errorCode == EINVAL) {
1909 throw new IllegalArgumentException(e.toString());
1910 } else {
1911 throw e;
1912 }
1913 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001914 }
1915
Nathan Harold031acb82017-03-07 13:23:36 -08001916 @Override
ludi89194d62017-05-22 10:52:23 -07001917 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001918 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludi89194d62017-05-22 10:52:23 -07001919
1920 pw.println("IpSecService dump:");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001921 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1922 pw.println();
ludi89194d62017-05-22 10:52:23 -07001923
Benedict Wong6855aee2017-11-16 15:27:22 -08001924 pw.println("mUserResourceTracker:");
1925 pw.println(mUserResourceTracker);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001926 }
1927}