blob: ca96c0690f023c76b0b90c2e75d39f7fc22342a3 [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;
Yan Yane114b382021-02-16 16:29:48 -080039import android.net.IpSecMigrateInfoParcel;
Nathan Harold80865392017-04-04 19:37:48 -070040import android.net.IpSecSpiResponse;
Nathan Harold031acb82017-03-07 13:23:36 -080041import android.net.IpSecTransform;
Nathan Harold80865392017-04-04 19:37:48 -070042import android.net.IpSecTransformResponse;
Benedict Wong8bc90732018-01-18 18:31:45 -080043import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold80865392017-04-04 19:37:48 -070044import android.net.IpSecUdpEncapResponse;
Benedict Wong97c3c942018-03-01 18:53:07 -080045import android.net.LinkAddress;
Yan Yana2f3b492020-09-29 23:38:00 -070046import android.net.LinkProperties;
Benedict Wong8bc90732018-01-18 18:31:45 -080047import android.net.Network;
Benedict Wong083faee2017-12-03 19:42:36 -080048import android.net.TrafficStats;
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);
Benedict Wong38e52972018-05-07 20:06:44 -070099 private static final int[] ADDRESS_FAMILIES =
100 new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800101
ludi5e623ea2017-05-12 09:15:00 -0700102 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold80865392017-04-04 19:37:48 -0700103 private static final InetAddress INADDR_ANY;
104
Benedict Wong29c30772019-03-20 09:44:09 -0700105 @VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
106
Aaron Huang2617cf52021-11-29 16:31:32 +0800107 private final INetd mNetd;
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 /**
Yan Yane114b382021-02-16 16:29:48 -0800594 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is created, the
595 * SpiRecord that originally tracked the SAs will reliquish the responsibility of freeing the
596 * underlying SA to this class via the mOwnedByTransform flag.
597 *
598 * <p>This class is not thread-safe, and expects that that users of this class will ensure
599 * synchronization and thread safety by holding the IpSecService.this instance lock
Benedict Wongcbd329b2017-12-13 17:16:53 -0800600 */
601 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700602 private final IpSecConfig mConfig;
Nathan Harold5a19b952018-01-05 19:25:13 -0800603 private final SpiRecord mSpi;
Benedict Wong6855aee2017-11-16 15:27:22 -0800604 private final EncapSocketRecord mSocket;
Yan Yane114b382021-02-16 16:29:48 -0800605 private String mNewSourceAddress = null;
606 private String mNewDestinationAddress = null;
Nathan Harold80865392017-04-04 19:37:48 -0700607
608 TransformRecord(
Nathan Harold5a19b952018-01-05 19:25:13 -0800609 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800610 super(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -0800611 mConfig = config;
Nathan Harold5a19b952018-01-05 19:25:13 -0800612 mSpi = spi;
Nathan Harold80865392017-04-04 19:37:48 -0700613 mSocket = socket;
Benedict Wong68aac2a2017-12-13 18:26:40 -0800614
615 spi.setOwnedByTransform();
Nathan Harold031acb82017-03-07 13:23:36 -0800616 }
617
618 public IpSecConfig getConfig() {
619 return mConfig;
620 }
621
Nathan Harold5a19b952018-01-05 19:25:13 -0800622 public SpiRecord getSpiRecord() {
623 return mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700624 }
625
Benedict Wong8edc5572018-01-19 17:36:02 -0800626 public EncapSocketRecord getSocketRecord() {
627 return mSocket;
628 }
629
Yan Yane114b382021-02-16 16:29:48 -0800630 @GuardedBy("IpSecService.this")
631 public String getNewSourceAddress() {
632 return mNewSourceAddress;
633 }
634
635 @GuardedBy("IpSecService.this")
636 public String getNewDestinationAddress() {
637 return mNewDestinationAddress;
638 }
639
640 private void verifyTunnelModeOrThrow() {
641 if (mConfig.getMode() != IpSecTransform.MODE_TUNNEL) {
642 throw new UnsupportedOperationException(
643 "Migration requested/called on non-tunnel-mode transform");
644 }
645 }
646
647 /** Start migrating this transform to new source and destination addresses */
648 @GuardedBy("IpSecService.this")
649 public void startMigration(String newSourceAddress, String newDestinationAddress) {
650 verifyTunnelModeOrThrow();
651 Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
652 Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
653 mNewSourceAddress = newSourceAddress;
654 mNewDestinationAddress = newDestinationAddress;
655 }
656
657 /** Finish migration and update addresses. */
658 @GuardedBy("IpSecService.this")
659 public void finishMigration() {
660 verifyTunnelModeOrThrow();
661 mConfig.setSourceAddress(mNewSourceAddress);
662 mConfig.setDestinationAddress(mNewDestinationAddress);
663 mNewSourceAddress = null;
664 mNewDestinationAddress = null;
665 }
666
667 /** Return if this transform is going to be migrated. */
668 @GuardedBy("IpSecService.this")
669 public boolean isMigrating() {
670 verifyTunnelModeOrThrow();
671
672 return mNewSourceAddress != null;
673 }
674
Nathan Harold80865392017-04-04 19:37:48 -0700675 /** always guarded by IpSecService#this */
Nathan Harold031acb82017-03-07 13:23:36 -0800676 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800677 public void freeUnderlyingResources() {
Nathan Harold5a19b952018-01-05 19:25:13 -0800678 int spi = mSpi.getSpi();
679 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800680 mNetd.ipSecDeleteSecurityAssociation(
681 mUid,
682 mConfig.getSourceAddress(),
683 mConfig.getDestinationAddress(),
684 spi,
685 mConfig.getMarkValue(),
686 mConfig.getMarkMask(),
687 mConfig.getXfrmInterfaceId());
Benedict Wong97c3c942018-03-01 18:53:07 -0800688 } catch (RemoteException | ServiceSpecificException e) {
689 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800690 }
Nathan Harold031acb82017-03-07 13:23:36 -0800691
Benedict Wong6855aee2017-11-16 15:27:22 -0800692 getResourceTracker().give();
Nathan Harold031acb82017-03-07 13:23:36 -0800693 }
ludi89194d62017-05-22 10:52:23 -0700694
Benedict Wong6855aee2017-11-16 15:27:22 -0800695 @Override
696 public void invalidate() throws RemoteException {
697 getUserRecord().removeTransformRecord(mResourceId);
698 }
699
700 @Override
Nathan Harold6e4681c2017-04-24 16:16:34 -0700701 protected ResourceTracker getResourceTracker() {
Benedict Wong6855aee2017-11-16 15:27:22 -0800702 return getUserRecord().mTransformQuotaTracker;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700703 }
704
ludi89194d62017-05-22 10:52:23 -0700705 @Override
706 public String toString() {
707 StringBuilder strBuilder = new StringBuilder();
708 strBuilder
709 .append("{super=")
710 .append(super.toString())
711 .append(", mSocket=")
712 .append(mSocket)
Nathan Harold5a19b952018-01-05 19:25:13 -0800713 .append(", mSpi.mResourceId=")
714 .append(mSpi.mResourceId)
ludi89194d62017-05-22 10:52:23 -0700715 .append(", mConfig=")
716 .append(mConfig)
717 .append("}");
718 return strBuilder.toString();
719 }
Nathan Harold031acb82017-03-07 13:23:36 -0800720 }
721
Benedict Wongcbd329b2017-12-13 17:16:53 -0800722 /**
723 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
724 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
725 * object
726 */
727 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harold5a19b952018-01-05 19:25:13 -0800728 private final String mSourceAddress;
729 private final String mDestinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800730 private int mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700731
732 private boolean mOwnedByTransform = false;
Nathan Harold031acb82017-03-07 13:23:36 -0800733
Aaron Huang2617cf52021-11-29 16:31:32 +0800734 SpiRecord(int resourceId, String sourceAddress,
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800735 String destinationAddress, int spi) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800736 super(resourceId);
Nathan Harold5a19b952018-01-05 19:25:13 -0800737 mSourceAddress = sourceAddress;
738 mDestinationAddress = destinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800739 mSpi = spi;
Nathan Harold031acb82017-03-07 13:23:36 -0800740 }
741
Nathan Harold80865392017-04-04 19:37:48 -0700742 /** always guarded by IpSecService#this */
743 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800744 public void freeUnderlyingResources() {
Nathan Harold031acb82017-03-07 13:23:36 -0800745 try {
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800746 if (!mOwnedByTransform) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800747 mNetd.ipSecDeleteSecurityAssociation(
748 mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
749 0 /* mask */, 0 /* if_id */);
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800750 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800751 } catch (ServiceSpecificException | RemoteException e) {
752 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800753 }
Nathan Harold80865392017-04-04 19:37:48 -0700754
755 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -0800756
Benedict Wong6855aee2017-11-16 15:27:22 -0800757 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700758 }
759
Nathan Harold80865392017-04-04 19:37:48 -0700760 public int getSpi() {
761 return mSpi;
762 }
763
Nathan Harold5a19b952018-01-05 19:25:13 -0800764 public String getDestinationAddress() {
765 return mDestinationAddress;
766 }
767
Nathan Harold80865392017-04-04 19:37:48 -0700768 public void setOwnedByTransform() {
769 if (mOwnedByTransform) {
770 // Programming error
Andreas Gampeafb01e22017-07-11 10:25:09 -0700771 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold80865392017-04-04 19:37:48 -0700772 }
773
774 mOwnedByTransform = true;
Nathan Harold031acb82017-03-07 13:23:36 -0800775 }
ludi89194d62017-05-22 10:52:23 -0700776
Benedict Wong68aac2a2017-12-13 18:26:40 -0800777 public boolean getOwnedByTransform() {
778 return mOwnedByTransform;
779 }
780
ludi89194d62017-05-22 10:52:23 -0700781 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800782 public void invalidate() throws RemoteException {
783 getUserRecord().removeSpiRecord(mResourceId);
784 }
785
786 @Override
787 protected ResourceTracker getResourceTracker() {
788 return getUserRecord().mSpiQuotaTracker;
789 }
790
791 @Override
ludi89194d62017-05-22 10:52:23 -0700792 public String toString() {
793 StringBuilder strBuilder = new StringBuilder();
794 strBuilder
795 .append("{super=")
796 .append(super.toString())
797 .append(", mSpi=")
798 .append(mSpi)
Nathan Harold5a19b952018-01-05 19:25:13 -0800799 .append(", mSourceAddress=")
800 .append(mSourceAddress)
801 .append(", mDestinationAddress=")
802 .append(mDestinationAddress)
ludi89194d62017-05-22 10:52:23 -0700803 .append(", mOwnedByTransform=")
804 .append(mOwnedByTransform)
805 .append("}");
806 return strBuilder.toString();
807 }
Nathan Harold031acb82017-03-07 13:23:36 -0800808 }
809
Benedict Wong8bc90732018-01-18 18:31:45 -0800810 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
lucaslin7eb76592021-03-11 17:39:49 +0800811 final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
812 private int mNextTunnelNetId = mNetIdRange.getLower();
Benedict Wong8bc90732018-01-18 18:31:45 -0800813
814 /**
815 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
816 *
817 * <p>This method should only be called from Binder threads. Do not call this from within the
818 * system server as it will crash the system on failure.
819 *
820 * @return an integer key within the netId range, if successful
821 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
822 */
823 @VisibleForTesting
824 int reserveNetId() {
lucaslin7eb76592021-03-11 17:39:49 +0800825 final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
Benedict Wong8bc90732018-01-18 18:31:45 -0800826 synchronized (mTunnelNetIds) {
lucaslin7eb76592021-03-11 17:39:49 +0800827 for (int i = 0; i < range; i++) {
828 final int netId = mNextTunnelNetId;
829 if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
830 mNextTunnelNetId = mNetIdRange.getLower();
831 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800832 if (!mTunnelNetIds.get(netId)) {
833 mTunnelNetIds.put(netId, true);
834 return netId;
835 }
836 }
837 }
838 throw new IllegalStateException("No free netIds to allocate");
839 }
840
841 @VisibleForTesting
842 void releaseNetId(int netId) {
843 synchronized (mTunnelNetIds) {
844 mTunnelNetIds.delete(netId);
845 }
846 }
847
Yan Yana2f3b492020-09-29 23:38:00 -0700848 /**
849 * Tracks an tunnel interface, and manages cleanup paths.
850 *
851 * <p>This class is not thread-safe, and expects that that users of this class will ensure
852 * synchronization and thread safety by holding the IpSecService.this instance lock
853 */
854 @VisibleForTesting
855 final class TunnelInterfaceRecord extends OwnedResourceRecord {
Benedict Wong8bc90732018-01-18 18:31:45 -0800856 private final String mInterfaceName;
Benedict Wong8bc90732018-01-18 18:31:45 -0800857
858 // outer addresses
859 private final String mLocalAddress;
860 private final String mRemoteAddress;
861
862 private final int mIkey;
863 private final int mOkey;
864
Benedict Wong5d749842018-09-06 11:31:25 -0700865 private final int mIfId;
866
Yan Yana2f3b492020-09-29 23:38:00 -0700867 private Network mUnderlyingNetwork;
868
Benedict Wong8bc90732018-01-18 18:31:45 -0800869 TunnelInterfaceRecord(
870 int resourceId,
871 String interfaceName,
872 Network underlyingNetwork,
873 String localAddr,
874 String remoteAddr,
875 int ikey,
Benedict Wong5d749842018-09-06 11:31:25 -0700876 int okey,
877 int intfId) {
Benedict Wong8bc90732018-01-18 18:31:45 -0800878 super(resourceId);
879
880 mInterfaceName = interfaceName;
881 mUnderlyingNetwork = underlyingNetwork;
882 mLocalAddress = localAddr;
883 mRemoteAddress = remoteAddr;
884 mIkey = ikey;
885 mOkey = okey;
Benedict Wong5d749842018-09-06 11:31:25 -0700886 mIfId = intfId;
Benedict Wong8bc90732018-01-18 18:31:45 -0800887 }
888
889 /** always guarded by IpSecService#this */
890 @Override
891 public void freeUnderlyingResources() {
Benedict Wong8edc5572018-01-19 17:36:02 -0800892 // Calls to netd
Benedict Wong8bc90732018-01-18 18:31:45 -0800893 // Teardown VTI
894 // Delete global policies
Benedict Wong8edc5572018-01-19 17:36:02 -0800895 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800896 mNetd.ipSecRemoveTunnelInterface(mInterfaceName);
Benedict Wong8edc5572018-01-19 17:36:02 -0800897
Benedict Wong38e52972018-05-07 20:06:44 -0700898 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800899 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800900 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700901 selAddrFamily,
902 IpSecManager.DIRECTION_OUT,
903 mOkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700904 0xffffffff,
905 mIfId);
Aaron Huang2617cf52021-11-29 16:31:32 +0800906 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800907 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700908 selAddrFamily,
909 IpSecManager.DIRECTION_IN,
910 mIkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700911 0xffffffff,
912 mIfId);
Yan Yanf4eceba2022-10-31 20:41:13 +0000913 mNetd.ipSecDeleteSecurityPolicy(
914 mUid,
915 selAddrFamily,
916 IpSecManager.DIRECTION_FWD,
917 mIkey,
918 0xffffffff,
919 mIfId);
Benedict Wong8edc5572018-01-19 17:36:02 -0800920 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800921 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong8edc5572018-01-19 17:36:02 -0800922 Log.e(
923 TAG,
924 "Failed to delete VTI with interface name: "
925 + mInterfaceName
926 + " and id: "
Benedict Wong97c3c942018-03-01 18:53:07 -0800927 + mResourceId, e);
Benedict Wong8edc5572018-01-19 17:36:02 -0800928 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800929
930 getResourceTracker().give();
931 releaseNetId(mIkey);
932 releaseNetId(mOkey);
933 }
934
Yan Yana2f3b492020-09-29 23:38:00 -0700935 @GuardedBy("IpSecService.this")
936 public void setUnderlyingNetwork(Network underlyingNetwork) {
937 // When #applyTunnelModeTransform is called, this new underlying network will be used to
938 // update the output mark of the input transform.
939 mUnderlyingNetwork = underlyingNetwork;
Benedict Wong8bc90732018-01-18 18:31:45 -0800940 }
941
Yan Yana2f3b492020-09-29 23:38:00 -0700942 @GuardedBy("IpSecService.this")
Benedict Wong8bc90732018-01-18 18:31:45 -0800943 public Network getUnderlyingNetwork() {
944 return mUnderlyingNetwork;
945 }
946
Yan Yana2f3b492020-09-29 23:38:00 -0700947 public String getInterfaceName() {
948 return mInterfaceName;
949 }
950
Benedict Wong8bc90732018-01-18 18:31:45 -0800951 /** Returns the local, outer address for the tunnelInterface */
952 public String getLocalAddress() {
953 return mLocalAddress;
954 }
955
956 /** Returns the remote, outer address for the tunnelInterface */
957 public String getRemoteAddress() {
958 return mRemoteAddress;
959 }
960
961 public int getIkey() {
962 return mIkey;
963 }
964
965 public int getOkey() {
966 return mOkey;
967 }
968
Benedict Wong5d749842018-09-06 11:31:25 -0700969 public int getIfId() {
970 return mIfId;
971 }
972
Benedict Wong8bc90732018-01-18 18:31:45 -0800973 @Override
974 protected ResourceTracker getResourceTracker() {
975 return getUserRecord().mTunnelQuotaTracker;
976 }
977
978 @Override
979 public void invalidate() {
980 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
981 }
982
983 @Override
984 public String toString() {
985 return new StringBuilder()
986 .append("{super=")
987 .append(super.toString())
988 .append(", mInterfaceName=")
989 .append(mInterfaceName)
990 .append(", mUnderlyingNetwork=")
991 .append(mUnderlyingNetwork)
992 .append(", mLocalAddress=")
993 .append(mLocalAddress)
994 .append(", mRemoteAddress=")
995 .append(mRemoteAddress)
996 .append(", mIkey=")
997 .append(mIkey)
998 .append(", mOkey=")
999 .append(mOkey)
1000 .append("}")
1001 .toString();
1002 }
1003 }
1004
Benedict Wongcbd329b2017-12-13 17:16:53 -08001005 /**
1006 * Tracks a UDP encap socket, and manages cleanup paths
1007 *
1008 * <p>While this class does not manage non-kernel resources, race conditions around socket
1009 * binding require that the service creates the encap socket, binds it and applies the socket
1010 * policy before handing it to a user.
1011 */
1012 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -07001013 private FileDescriptor mSocket;
1014 private final int mPort;
Nathan Harold031acb82017-03-07 13:23:36 -08001015
Benedict Wong6855aee2017-11-16 15:27:22 -08001016 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
1017 super(resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001018 mSocket = socket;
1019 mPort = port;
1020 }
1021
1022 /** always guarded by IpSecService#this */
1023 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001024 public void freeUnderlyingResources() {
Nathan Harold80865392017-04-04 19:37:48 -07001025 Log.d(TAG, "Closing port " + mPort);
1026 IoUtils.closeQuietly(mSocket);
1027 mSocket = null;
Nathan Harold80865392017-04-04 19:37:48 -07001028
Benedict Wong6855aee2017-11-16 15:27:22 -08001029 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -07001030 }
1031
Nathan Harold80865392017-04-04 19:37:48 -07001032 public int getPort() {
1033 return mPort;
1034 }
1035
Benedict Wonga386e372018-03-27 16:55:48 -07001036 public FileDescriptor getFileDescriptor() {
Nathan Harold80865392017-04-04 19:37:48 -07001037 return mSocket;
1038 }
ludi89194d62017-05-22 10:52:23 -07001039
1040 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001041 protected ResourceTracker getResourceTracker() {
1042 return getUserRecord().mSocketQuotaTracker;
1043 }
1044
1045 @Override
1046 public void invalidate() {
1047 getUserRecord().removeEncapSocketRecord(mResourceId);
1048 }
1049
1050 @Override
ludi89194d62017-05-22 10:52:23 -07001051 public String toString() {
1052 return new StringBuilder()
1053 .append("{super=")
1054 .append(super.toString())
1055 .append(", mSocket=")
1056 .append(mSocket)
1057 .append(", mPort=")
1058 .append(mPort)
1059 .append("}")
1060 .toString();
1061 }
Nathan Harold80865392017-04-04 19:37:48 -07001062 }
Nathan Harold031acb82017-03-07 13:23:36 -08001063
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001064 /**
1065 * Constructs a new IpSecService instance
1066 *
1067 * @param context Binder context for this service
1068 */
Aaron Huangb944ff12022-01-12 15:11:01 +08001069 public IpSecService(Context context) {
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001070 this(context, new Dependencies());
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001071 }
1072
Nathan Harold65ef8432018-03-15 18:06:06 -07001073 @NonNull
1074 private AppOpsManager getAppOpsManager() {
1075 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Aaron Huangfbae3082021-12-06 15:18:42 +08001076 if (appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
Nathan Harold65ef8432018-03-15 18:06:06 -07001077 return appOps;
1078 }
1079
ludi5e623ea2017-05-12 09:15:00 -07001080 /** @hide */
1081 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001082 public IpSecService(Context context, Dependencies deps) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001083 this(
1084 context,
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001085 deps,
Nathan Harold5a19b952018-01-05 19:25:13 -08001086 (fd, uid) -> {
1087 try {
1088 TrafficStats.setThreadStatsUid(uid);
1089 TrafficStats.tagFileDescriptor(fd);
1090 } finally {
1091 TrafficStats.clearThreadStatsUid();
1092 }
1093 });
Benedict Wong083faee2017-12-03 19:42:36 -08001094 }
1095
1096 /** @hide */
1097 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001098 public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
ludi5e623ea2017-05-12 09:15:00 -07001099 mContext = context;
Aaron Huang2617cf52021-11-29 16:31:32 +08001100 mDeps = Objects.requireNonNull(deps, "Missing dependencies.");
Benedict Wong083faee2017-12-03 19:42:36 -08001101 mUidFdTagger = uidFdTagger;
Aaron Huang2617cf52021-11-29 16:31:32 +08001102 try {
1103 mNetd = mDeps.getNetdInstance(mContext);
1104 } catch (RemoteException e) {
1105 throw e.rethrowFromSystemServer();
1106 }
ludi5e623ea2017-05-12 09:15:00 -07001107 }
1108
Nathan Harold19b99d92017-08-23 13:46:33 -07001109 /**
1110 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1111 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1112 */
1113 private static void checkInetAddress(String inetAddress) {
1114 if (TextUtils.isEmpty(inetAddress)) {
1115 throw new IllegalArgumentException("Unspecified address");
1116 }
1117
Serik Beketayev7f507332020-12-06 22:31:23 -08001118 InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
Nathan Harold19b99d92017-08-23 13:46:33 -07001119
1120 if (checkAddr.isAnyLocalAddress()) {
1121 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1122 }
1123 }
1124
1125 /**
1126 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1127 * DIRECTION_IN or DIRECTION_OUT
1128 */
Benedict Wong908d34e2021-04-15 11:59:16 -07001129 private void checkDirection(int direction) {
Nathan Harold19b99d92017-08-23 13:46:33 -07001130 switch (direction) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001131 case IpSecManager.DIRECTION_OUT:
1132 case IpSecManager.DIRECTION_IN:
Nathan Harold19b99d92017-08-23 13:46:33 -07001133 return;
Benedict Wong908d34e2021-04-15 11:59:16 -07001134 case IpSecManager.DIRECTION_FWD:
Benedict Wong47b528c2021-05-10 18:26:02 -07001135 // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
Benedict Wong908d34e2021-04-15 11:59:16 -07001136 PermissionUtils.enforceNetworkStackPermission(mContext);
1137 return;
Nathan Harold19b99d92017-08-23 13:46:33 -07001138 }
1139 throw new IllegalArgumentException("Invalid Direction: " + direction);
1140 }
1141
Nathan Harold031acb82017-03-07 13:23:36 -08001142 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri20e96c52017-11-16 10:58:01 -08001143 @Override
1144 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harold5a19b952018-01-05 19:25:13 -08001145 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1146 checkInetAddress(destinationAddress);
Nathan Harold1b88f0e2018-03-28 08:52:51 -07001147 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1148 if (requestedSpi > 0 && requestedSpi < 256) {
1149 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1150 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001151 Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harold19b99d92017-08-23 13:46:33 -07001152
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001153 int callingUid = Binder.getCallingUid();
1154 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001155 final int resourceId = mNextResourceId++;
Nathan Harold031acb82017-03-07 13:23:36 -08001156
1157 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -08001158 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001159 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001160 return new IpSecSpiResponse(
Nathan Harold19b99d92017-08-23 13:46:33 -07001161 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold6e4681c2017-04-24 16:16:34 -07001162 }
Nathan Harold5a19b952018-01-05 19:25:13 -08001163
Aaron Huang2617cf52021-11-29 16:31:32 +08001164 spi = mNetd.ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold031acb82017-03-07 13:23:36 -08001165 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong6855aee2017-11-16 15:27:22 -08001166 userRecord.mSpiRecords.put(
Nathan Harold80865392017-04-04 19:37:48 -07001167 resourceId,
Benedict Wong6855aee2017-11-16 15:27:22 -08001168 new RefcountedResource<SpiRecord>(
Aaron Huang2617cf52021-11-29 16:31:32 +08001169 new SpiRecord(resourceId, "",
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001170 destinationAddress, spi), binder));
Nathan Harold031acb82017-03-07 13:23:36 -08001171 } catch (ServiceSpecificException e) {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001172 if (e.errorCode == OsConstants.ENOENT) {
1173 return new IpSecSpiResponse(
1174 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1175 }
1176 throw e;
Nathan Harold031acb82017-03-07 13:23:36 -08001177 } catch (RemoteException e) {
1178 throw e.rethrowFromSystemServer();
1179 }
Nathan Harold80865392017-04-04 19:37:48 -07001180 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1181 }
1182
1183 /* This method should only be called from Binder threads. Do not call this from
1184 * within the system server as it will crash the system on failure.
1185 */
Benedict Wong6855aee2017-11-16 15:27:22 -08001186 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold80865392017-04-04 19:37:48 -07001187 throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001188 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold031acb82017-03-07 13:23:36 -08001189 }
1190
1191 /** Release a previously allocated SPI that has been registered with the system server */
1192 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001193 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1194 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1195 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001196 }
1197
1198 /**
1199 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1200 * be unbound.
1201 *
1202 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1203 * a random open port and then bind by number, this function creates a temp socket, binds to a
1204 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1205 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1206 * FileHandle.
1207 *
1208 * <p>The loop in this function handles the inherent race window between un-binding to a port
1209 * and re-binding, during which the system could *technically* hand that port out to someone
1210 * else.
1211 */
Benedict Wongc423cc82017-10-10 20:44:28 -07001212 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold80865392017-04-04 19:37:48 -07001213 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1214 try {
1215 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1216 Os.bind(probeSocket, INADDR_ANY, 0);
1217 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1218 Os.close(probeSocket);
1219 Log.v(TAG, "Binding to port " + port);
1220 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongc423cc82017-10-10 20:44:28 -07001221 return port;
Nathan Harold80865392017-04-04 19:37:48 -07001222 } catch (ErrnoException e) {
1223 // Someone miraculously claimed the port just after we closed probeSocket.
1224 if (e.errno == OsConstants.EADDRINUSE) {
1225 continue;
1226 }
1227 throw e.rethrowAsIOException();
1228 }
1229 }
1230 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1231 }
Nathan Harold031acb82017-03-07 13:23:36 -08001232
1233 /**
Benedict Wong083faee2017-12-03 19:42:36 -08001234 * Functional interface to do traffic tagging of given sockets to UIDs.
1235 *
1236 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1237 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1238 *
1239 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1240 * methods that cannot be easily mocked/tested.
1241 */
1242 @VisibleForTesting
1243 public interface UidFdTagger {
1244 /**
1245 * Sets socket tag to assign all traffic to the provided UID.
1246 *
1247 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1248 * should be accounted to the UID of the unprivileged application.
1249 */
Aaron Huangfbae3082021-12-06 15:18:42 +08001250 void tag(FileDescriptor fd, int uid) throws IOException;
Benedict Wong083faee2017-12-03 19:42:36 -08001251 }
1252
1253 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001254 * Open a socket via the system server and bind it to the specified port (random if port=0).
1255 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1256 * cache the socket and a record of its owner so that it can and must be freed when no longer
1257 * needed.
1258 */
1259 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001260 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1261 throws RemoteException {
1262 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1263 throw new IllegalArgumentException(
1264 "Specified port number must be a valid non-reserved UDP port");
1265 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001266 Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
Nathan Harold19b99d92017-08-23 13:46:33 -07001267
Benedict Wong083faee2017-12-03 19:42:36 -08001268 int callingUid = Binder.getCallingUid();
1269 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001270 final int resourceId = mNextResourceId++;
Aaron Huangb01254f2021-12-23 10:47:05 +08001271
1272 ParcelFileDescriptor pFd = null;
Nathan Harold80865392017-04-04 19:37:48 -07001273 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001274 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001275 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1276 }
1277
Aaron Huangb01254f2021-12-23 10:47:05 +08001278 FileDescriptor sockFd = null;
1279 try {
1280 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1281 pFd = ParcelFileDescriptor.dup(sockFd);
1282 } finally {
1283 IoUtils.closeQuietly(sockFd);
1284 }
Nathan Harold80865392017-04-04 19:37:48 -07001285
Aaron Huangb01254f2021-12-23 10:47:05 +08001286 mUidFdTagger.tag(pFd.getFileDescriptor(), callingUid);
Nathan Harold80865392017-04-04 19:37:48 -07001287 // This code is common to both the unspecified and specified port cases
1288 Os.setsockoptInt(
Aaron Huangb01254f2021-12-23 10:47:05 +08001289 pFd.getFileDescriptor(),
Nathan Harold80865392017-04-04 19:37:48 -07001290 OsConstants.IPPROTO_UDP,
1291 OsConstants.UDP_ENCAP,
1292 OsConstants.UDP_ENCAP_ESPINUDP);
1293
Aaron Huangb01254f2021-12-23 10:47:05 +08001294 mNetd.ipSecSetEncapSocketOwner(pFd, callingUid);
Benedict Wong17687442017-12-06 21:56:35 -08001295 if (port != 0) {
1296 Log.v(TAG, "Binding to port " + port);
Aaron Huangb01254f2021-12-23 10:47:05 +08001297 Os.bind(pFd.getFileDescriptor(), INADDR_ANY, port);
Benedict Wong17687442017-12-06 21:56:35 -08001298 } else {
Aaron Huangb01254f2021-12-23 10:47:05 +08001299 port = bindToRandomPort(pFd.getFileDescriptor());
Benedict Wong17687442017-12-06 21:56:35 -08001300 }
1301
Benedict Wong6855aee2017-11-16 15:27:22 -08001302 userRecord.mEncapSocketRecords.put(
1303 resourceId,
1304 new RefcountedResource<EncapSocketRecord>(
Aaron Huangb01254f2021-12-23 10:47:05 +08001305 new EncapSocketRecord(resourceId, pFd.getFileDescriptor(), port),
1306 binder));
1307 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port,
1308 pFd.getFileDescriptor());
Nathan Harold80865392017-04-04 19:37:48 -07001309 } catch (IOException | ErrnoException e) {
Aaron Huangb01254f2021-12-23 10:47:05 +08001310 try {
1311 if (pFd != null) {
1312 pFd.close();
1313 }
1314 } catch (IOException ex) {
1315 // Nothing can be done at this point
1316 Log.e(TAG, "Failed to close pFd.");
1317 }
Nathan Harold80865392017-04-04 19:37:48 -07001318 }
1319 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1320 // The only reasonable condition that would cause that is resource unavailable.
1321 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold031acb82017-03-07 13:23:36 -08001322 }
1323
1324 /** close a socket that has been been allocated by and registered with the system server */
1325 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001326 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1327 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1328 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001329 }
Nathan Harold031acb82017-03-07 13:23:36 -08001330
Benedict Wong8bc90732018-01-18 18:31:45 -08001331 /**
1332 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1333 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1334 * needed.
1335 */
1336 @Override
1337 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001338 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1339 String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001340 enforceTunnelFeatureAndPermissions(callingPackage);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001341 Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
1342 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
Benedict Wong8bc90732018-01-18 18:31:45 -08001343 checkInetAddress(localAddr);
1344 checkInetAddress(remoteAddr);
1345
1346 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1347 // network (b/72316676).
1348
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001349 int callerUid = Binder.getCallingUid();
1350 UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001351 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1352 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1353 }
1354
1355 final int resourceId = mNextResourceId++;
1356 final int ikey = reserveNetId();
1357 final int okey = reserveNetId();
Nathan Harold7be7f452018-04-26 11:47:14 -07001358 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001359
Benedict Wong8edc5572018-01-19 17:36:02 -08001360 try {
1361 // Calls to netd:
1362 // Create VTI
1363 // Add inbound/outbound global policies
1364 // (use reqid = 0)
Aaron Huang2617cf52021-11-29 16:31:32 +08001365 mNetd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001366
paulhu00e34562021-10-26 09:00:50 +00001367 BinderUtils.withCleanCallingIdentity(() -> {
Aaron Huang2617cf52021-11-29 16:31:32 +08001368 NetdUtils.setInterfaceUp(mNetd, intfName);
Benedict Wong529e8aa2020-02-11 23:49:36 -08001369 });
1370
Benedict Wong38e52972018-05-07 20:06:44 -07001371 for (int selAddrFamily : ADDRESS_FAMILIES) {
1372 // Always send down correct local/remote addresses for template.
Aaron Huang2617cf52021-11-29 16:31:32 +08001373 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001374 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001375 selAddrFamily,
1376 IpSecManager.DIRECTION_OUT,
1377 localAddr,
1378 remoteAddr,
1379 0,
1380 okey,
Benedict Wong5d749842018-09-06 11:31:25 -07001381 0xffffffff,
1382 resourceId);
Aaron Huang2617cf52021-11-29 16:31:32 +08001383 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001384 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001385 selAddrFamily,
1386 IpSecManager.DIRECTION_IN,
1387 remoteAddr,
1388 localAddr,
1389 0,
1390 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001391 0xffffffff,
1392 resourceId);
Benedict Wong47b528c2021-05-10 18:26:02 -07001393
1394 // Add a forwarding policy on the tunnel interface. In order to support forwarding
1395 // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
1396 //
1397 // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
1398 // forwarding will be blocked by default (as would be the case if this policy was
1399 // absent).
1400 //
1401 // This is necessary only on the tunnel interface, and not any the interface to
1402 // which traffic will be forwarded to.
Aaron Huang2617cf52021-11-29 16:31:32 +08001403 mNetd.ipSecAddSecurityPolicy(
Benedict Wong908d34e2021-04-15 11:59:16 -07001404 callerUid,
1405 selAddrFamily,
1406 IpSecManager.DIRECTION_FWD,
1407 remoteAddr,
1408 localAddr,
1409 0,
1410 ikey,
1411 0xffffffff,
1412 resourceId);
Benedict Wong8edc5572018-01-19 17:36:02 -08001413 }
1414
1415 userRecord.mTunnelInterfaceRecords.put(
1416 resourceId,
1417 new RefcountedResource<TunnelInterfaceRecord>(
1418 new TunnelInterfaceRecord(
1419 resourceId,
1420 intfName,
1421 underlyingNetwork,
1422 localAddr,
1423 remoteAddr,
1424 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001425 okey,
1426 resourceId),
Benedict Wong8edc5572018-01-19 17:36:02 -08001427 binder));
1428 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1429 } catch (RemoteException e) {
1430 // Release keys if we got an error.
1431 releaseNetId(ikey);
1432 releaseNetId(okey);
1433 throw e.rethrowFromSystemServer();
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001434 } catch (Throwable t) {
1435 // Release keys if we got an error.
1436 releaseNetId(ikey);
1437 releaseNetId(okey);
1438 throw t;
Benedict Wong8edc5572018-01-19 17:36:02 -08001439 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001440 }
1441
1442 /**
1443 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1444 * from multiple local IP addresses over the same tunnel.
1445 */
1446 @Override
Benedict Wong97c3c942018-03-01 18:53:07 -08001447 public synchronized void addAddressToTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001448 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001449 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001450 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1451
1452 // Get tunnelInterface record; if no such interface is found, will throw
1453 // IllegalArgumentException
1454 TunnelInterfaceRecord tunnelInterfaceInfo =
1455 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1456
Benedict Wong97c3c942018-03-01 18:53:07 -08001457 try {
1458 // We can assume general validity of the IP address, since we get them as a
1459 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001460 mNetd.interfaceAddAddress(
1461 tunnelInterfaceInfo.mInterfaceName,
1462 localAddr.getAddress().getHostAddress(),
1463 localAddr.getPrefixLength());
Benedict Wong97c3c942018-03-01 18:53:07 -08001464 } catch (RemoteException e) {
1465 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001466 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001467 }
1468
1469 /**
1470 * Remove a new local address from the tunnel interface. After removal, the address will no
1471 * longer be available to send from, or receive on.
1472 */
1473 @Override
1474 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001475 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001476 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001477
Nathan Harold65ef8432018-03-15 18:06:06 -07001478 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8bc90732018-01-18 18:31:45 -08001479 // Get tunnelInterface record; if no such interface is found, will throw
1480 // IllegalArgumentException
1481 TunnelInterfaceRecord tunnelInterfaceInfo =
1482 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1483
Benedict Wong97c3c942018-03-01 18:53:07 -08001484 try {
1485 // We can assume general validity of the IP address, since we get them as a
1486 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001487 mNetd.interfaceDelAddress(
Benedict Wong97c3c942018-03-01 18:53:07 -08001488 tunnelInterfaceInfo.mInterfaceName,
1489 localAddr.getAddress().getHostAddress(),
1490 localAddr.getPrefixLength());
1491 } catch (RemoteException e) {
1492 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001493 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001494 }
1495
Yan Yana2f3b492020-09-29 23:38:00 -07001496 /** Set TunnelInterface to use a specific underlying network. */
1497 @Override
1498 public synchronized void setNetworkForTunnelInterface(
1499 int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
1500 enforceTunnelFeatureAndPermissions(callingPackage);
1501 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1502
1503 final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1504
1505 // Get tunnelInterface record; if no such interface is found, will throw
1506 // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
1507 final TunnelInterfaceRecord tunnelInterfaceInfo =
1508 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1509
1510 final ConnectivityManager connectivityManager =
1511 mContext.getSystemService(ConnectivityManager.class);
1512 final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
Yan Yanbe3eb3d2022-05-16 17:13:45 -07001513 if (lp == null) {
1514 throw new IllegalArgumentException(
1515 "LinkProperties is null. The underlyingNetwork may not be functional");
1516 }
1517
Yan Yana2f3b492020-09-29 23:38:00 -07001518 if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
1519 throw new IllegalArgumentException(
1520 "Underlying network cannot be the network being exposed by this tunnel");
1521 }
1522
1523 // It is meaningless to check if the network exists or is valid because the network might
1524 // disconnect at any time after it passes the check.
1525
1526 tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
1527 }
1528
Benedict Wong8bc90732018-01-18 18:31:45 -08001529 /**
1530 * Delete a TunnelInterface that has been been allocated by and registered with the system
1531 * server
1532 */
1533 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001534 public synchronized void deleteTunnelInterface(
1535 int resourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001536 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001537 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1538 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1539 }
1540
Benedict Wong70867e52017-11-06 20:49:10 -08001541 @VisibleForTesting
Nathan Harold5a19b952018-01-05 19:25:13 -08001542 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1543 IpSecAlgorithm auth = config.getAuthentication();
1544 IpSecAlgorithm crypt = config.getEncryption();
1545 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong70867e52017-11-06 20:49:10 -08001546
Nathan Harold5a19b952018-01-05 19:25:13 -08001547 // Validate the algorithm set
1548 Preconditions.checkArgument(
1549 aead != null || crypt != null || auth != null,
1550 "No Encryption or Authentication algorithms specified");
1551 Preconditions.checkArgument(
1552 auth == null || auth.isAuthentication(),
1553 "Unsupported algorithm for Authentication");
1554 Preconditions.checkArgument(
Benedict Wong70867e52017-11-06 20:49:10 -08001555 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harold5a19b952018-01-05 19:25:13 -08001556 Preconditions.checkArgument(
1557 aead == null || aead.isAead(),
1558 "Unsupported algorithm for Authenticated Encryption");
1559 Preconditions.checkArgument(
1560 aead == null || (auth == null && crypt == null),
1561 "Authenticated Encryption is mutually exclusive with other Authentication "
1562 + "or Encryption algorithms");
Benedict Wong70867e52017-11-06 20:49:10 -08001563 }
1564
evitayan43d93a02018-03-22 17:53:08 -07001565 private int getFamily(String inetAddress) {
1566 int family = AF_UNSPEC;
Serik Beketayev7f507332020-12-06 22:31:23 -08001567 InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
evitayan43d93a02018-03-22 17:53:08 -07001568 if (checkAddress instanceof Inet4Address) {
1569 family = AF_INET;
1570 } else if (checkAddress instanceof Inet6Address) {
1571 family = AF_INET6;
1572 }
1573 return family;
1574 }
1575
Nathan Harold031acb82017-03-07 13:23:36 -08001576 /**
Chiachang Wang2fea4a72020-08-12 12:23:59 +08001577 * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
Nathan Harold19b99d92017-08-23 13:46:33 -07001578 * IllegalArgumentException if they are not.
1579 */
1580 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001581 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1582
Nathan Harold19b99d92017-08-23 13:46:33 -07001583 switch (config.getEncapType()) {
1584 case IpSecTransform.ENCAP_NONE:
1585 break;
1586 case IpSecTransform.ENCAP_ESPINUDP:
1587 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong6855aee2017-11-16 15:27:22 -08001588 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1589 userRecord.mEncapSocketRecords.getResourceOrThrow(
1590 config.getEncapSocketResourceId());
Nathan Harold19b99d92017-08-23 13:46:33 -07001591
1592 int port = config.getEncapRemotePort();
1593 if (port <= 0 || port > 0xFFFF) {
1594 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1595 }
1596 break;
1597 default:
1598 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1599 }
1600
Nathan Harold5a19b952018-01-05 19:25:13 -08001601 validateAlgorithms(config);
Nathan Harold19b99d92017-08-23 13:46:33 -07001602
Nathan Harold5a19b952018-01-05 19:25:13 -08001603 // Retrieve SPI record; will throw IllegalArgumentException if not found
1604 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1605
Benedict Wong68aac2a2017-12-13 18:26:40 -08001606 // Check to ensure that SPI has not already been used.
1607 if (s.getOwnedByTransform()) {
1608 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1609 }
1610
Nathan Harold5a19b952018-01-05 19:25:13 -08001611 // If no remote address is supplied, then use one from the SPI.
1612 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1613 config.setDestinationAddress(s.getDestinationAddress());
1614 }
1615
1616 // All remote addresses must match
1617 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1618 throw new IllegalArgumentException("Mismatched remote addresseses.");
1619 }
1620
1621 // This check is technically redundant due to the chain of custody between the SPI and
1622 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1623 // the transform, this will prevent us from messing up.
1624 checkInetAddress(config.getDestinationAddress());
1625
1626 // Require a valid source address for all transforms.
1627 checkInetAddress(config.getSourceAddress());
1628
evitayan43d93a02018-03-22 17:53:08 -07001629 // Check to ensure source and destination have the same address family.
1630 String sourceAddress = config.getSourceAddress();
1631 String destinationAddress = config.getDestinationAddress();
1632 int sourceFamily = getFamily(sourceAddress);
1633 int destinationFamily = getFamily(destinationAddress);
1634 if (sourceFamily != destinationFamily) {
1635 throw new IllegalArgumentException(
1636 "Source address ("
1637 + sourceAddress
1638 + ") and destination address ("
1639 + destinationAddress
1640 + ") have different address families.");
1641 }
1642
1643 // Throw an error if UDP Encapsulation is not used in IPv4.
1644 if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
1645 throw new IllegalArgumentException(
1646 "UDP Encapsulation is not supported for this address family");
1647 }
1648
Nathan Harold5a19b952018-01-05 19:25:13 -08001649 switch (config.getMode()) {
1650 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold025aae12018-02-02 18:34:25 -08001651 break;
Nathan Harold5a19b952018-01-05 19:25:13 -08001652 case IpSecTransform.MODE_TUNNEL:
1653 break;
1654 default:
1655 throw new IllegalArgumentException(
1656 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harold19b99d92017-08-23 13:46:33 -07001657 }
Benedict Wong683441d2018-07-25 18:46:19 -07001658
1659 config.setMarkValue(0);
1660 config.setMarkMask(0);
Nathan Harold19b99d92017-08-23 13:46:33 -07001661 }
1662
Benedict Wong2b6a14e2018-09-13 16:45:12 -07001663 private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
Nathan Harold7c250ae2018-05-15 19:18:38 -07001664
Benedict Wonge9763752018-11-08 19:45:34 -08001665 private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1666 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1667 throw new UnsupportedOperationException(
1668 "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1669 }
1670
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001671 Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Benedict Wongc85b7b02019-11-12 22:31:51 -08001672
1673 // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
1674 // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
1675 // permission or is the System Server.
1676 if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
1677 TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1678 return;
Nathan Harold65ef8432018-03-15 18:06:06 -07001679 }
Benedict Wongc85b7b02019-11-12 22:31:51 -08001680 mContext.enforceCallingOrSelfPermission(
1681 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
Nathan Harold025aae12018-02-02 18:34:25 -08001682 }
1683
Benedict Wong8edc5572018-01-19 17:36:02 -08001684 private void createOrUpdateTransform(
1685 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1686 throws RemoteException {
1687
1688 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1689 if (encapType != IpSecTransform.ENCAP_NONE) {
1690 encapLocalPort = socketRecord.getPort();
1691 encapRemotePort = c.getEncapRemotePort();
1692 }
1693
1694 IpSecAlgorithm auth = c.getAuthentication();
1695 IpSecAlgorithm crypt = c.getEncryption();
1696 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1697
Benedict Wong778327e2018-03-15 19:41:41 -07001698 String cryptName;
1699 if (crypt == null) {
1700 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1701 } else {
1702 cryptName = crypt.getName();
1703 }
1704
Aaron Huang2617cf52021-11-29 16:31:32 +08001705 mNetd.ipSecAddSecurityAssociation(
1706 Binder.getCallingUid(),
1707 c.getMode(),
1708 c.getSourceAddress(),
1709 c.getDestinationAddress(),
1710 (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
1711 spiRecord.getSpi(),
1712 c.getMarkValue(),
1713 c.getMarkMask(),
1714 (auth != null) ? auth.getName() : "",
1715 (auth != null) ? auth.getKey() : new byte[] {},
1716 (auth != null) ? auth.getTruncationLengthBits() : 0,
1717 cryptName,
1718 (crypt != null) ? crypt.getKey() : new byte[] {},
1719 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1720 (authCrypt != null) ? authCrypt.getName() : "",
1721 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1722 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1723 encapType,
1724 encapLocalPort,
1725 encapRemotePort,
1726 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001727 }
1728
Nathan Harold19b99d92017-08-23 13:46:33 -07001729 /**
Benedict Wong8edc5572018-01-19 17:36:02 -08001730 * Create a IPsec transform, which represents a single security association in the kernel. The
1731 * transform will be cached by the system server and must be freed when no longer needed. It is
1732 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1733 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold031acb82017-03-07 13:23:36 -08001734 */
1735 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001736 public synchronized IpSecTransformResponse createTransform(
1737 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001738 Objects.requireNonNull(c);
Nathan Harold65ef8432018-03-15 18:06:06 -07001739 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
Benedict Wonge9763752018-11-08 19:45:34 -08001740 enforceTunnelFeatureAndPermissions(callingPackage);
Nathan Harold65ef8432018-03-15 18:06:06 -07001741 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001742 checkIpSecConfig(c);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001743 Objects.requireNonNull(binder, "Null Binder passed to createTransform");
Nathan Haroldfdafce22017-12-13 19:16:33 -08001744 final int resourceId = mNextResourceId++;
Benedict Wong6855aee2017-11-16 15:27:22 -08001745
1746 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wongcbd329b2017-12-13 17:16:53 -08001747 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong6855aee2017-11-16 15:27:22 -08001748
1749 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001750 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1751 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001752
Benedict Wong6855aee2017-11-16 15:27:22 -08001753 EncapSocketRecord socketRecord = null;
Benedict Wong8edc5572018-01-19 17:36:02 -08001754 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001755 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1756 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1757 c.getEncapSocketResourceId());
1758 dependencies.add(refcountedSocketRecord);
Benedict Wong6855aee2017-11-16 15:27:22 -08001759 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold80865392017-04-04 19:37:48 -07001760 }
1761
Nathan Harold5a19b952018-01-05 19:25:13 -08001762 RefcountedResource<SpiRecord> refcountedSpiRecord =
1763 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1764 dependencies.add(refcountedSpiRecord);
1765 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong6855aee2017-11-16 15:27:22 -08001766
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001767 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong8edc5572018-01-19 17:36:02 -08001768
1769 // SA was created successfully, time to construct a record and lock it away
Benedict Wong6855aee2017-11-16 15:27:22 -08001770 userRecord.mTransformRecords.put(
1771 resourceId,
1772 new RefcountedResource<TransformRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001773 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong6855aee2017-11-16 15:27:22 -08001774 binder,
1775 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold80865392017-04-04 19:37:48 -07001776 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001777 }
1778
1779 /**
Yan Yane114b382021-02-16 16:29:48 -08001780 * Migrate an active Tunnel Mode IPsec Transform to new source/destination addresses.
1781 *
1782 * <p>Begins the process of migrating a transform and cache the new addresses. To complete the
1783 * migration once started, callers MUST apply the same transform to the appropriate tunnel using
1784 * {@link #applyTunnelModeTransform}. Otherwise, the address update will not be committed and
1785 * the transform will still only process traffic between the current source and destination
1786 * address. One common use case is that the control plane will start the migration process and
1787 * then hand off the transform to the IPsec caller to perform the actual migration when the
1788 * tunnel is ready.
1789 *
1790 * <p>If this method is called multiple times before {@link #applyTunnelModeTransform} is
1791 * called, when the transform is applied, it will be migrated to the addresses from the last
1792 * call.
1793 *
1794 * <p>The provided source and destination addresses MUST share the same address family, but they
1795 * can have a different family from the current addresses.
1796 *
1797 * <p>Transform migration is only supported for tunnel mode transforms. Calling this method on
1798 * other types of transforms will throw an {@code UnsupportedOperationException}.
1799 */
1800 @Override
1801 public synchronized void migrateTransform(
1802 int transformId,
1803 String newSourceAddress,
1804 String newDestinationAddress,
1805 String callingPackage) {
1806 Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
1807 Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
1808
1809 enforceTunnelFeatureAndPermissions(callingPackage);
1810
1811 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1812 TransformRecord transformInfo =
1813 userRecord.mTransformRecords.getResourceOrThrow(transformId);
1814 transformInfo.startMigration(newSourceAddress, newDestinationAddress);
1815 }
1816
1817 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001818 * Delete a transport mode transform that was previously allocated by + registered with the
1819 * system server. If this is called on an inactive (or non-existent) transform, it will not
1820 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1821 * other reasons.
1822 */
1823 @Override
Benedict Wong0fff56e2018-01-18 14:38:16 -08001824 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001825 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1826 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001827 }
1828
1829 /**
1830 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1831 * association as a correspondent policy to the provided socket
1832 */
1833 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001834 public synchronized void applyTransportModeTransform(
Nathan Harold5a19b952018-01-05 19:25:13 -08001835 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001836 int callingUid = Binder.getCallingUid();
1837 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harold5a19b952018-01-05 19:25:13 -08001838 checkDirection(direction);
Benedict Wong6855aee2017-11-16 15:27:22 -08001839 // Get transform record; if no transform is found, will throw IllegalArgumentException
1840 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001841
Nathan Harold80865392017-04-04 19:37:48 -07001842 // TODO: make this a function.
Aaron Huangfbae3082021-12-06 15:18:42 +08001843 if (info.mPid != getCallingPid() || info.mUid != callingUid) {
Nathan Harold80865392017-04-04 19:37:48 -07001844 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1845 }
1846
Benedict Wong8bc90732018-01-18 18:31:45 -08001847 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold80865392017-04-04 19:37:48 -07001848 IpSecConfig c = info.getConfig();
Benedict Wong8bc90732018-01-18 18:31:45 -08001849 Preconditions.checkArgument(
1850 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1851 "Transform mode was not Transport mode; cannot be applied to a socket");
1852
Aaron Huang2617cf52021-11-29 16:31:32 +08001853 mNetd.ipSecApplyTransportModeTransform(
1854 socket,
1855 callingUid,
1856 direction,
1857 c.getSourceAddress(),
1858 c.getDestinationAddress(),
1859 info.getSpiRecord().getSpi());
Nathan Harold031acb82017-03-07 13:23:36 -08001860 }
Nathan Harold80865392017-04-04 19:37:48 -07001861
Nathan Harold031acb82017-03-07 13:23:36 -08001862 /**
Nathan Harold5a19b952018-01-05 19:25:13 -08001863 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1864 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1865 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1866 * reserved for future improved input validation.
Nathan Harold031acb82017-03-07 13:23:36 -08001867 */
1868 @Override
Nathan Harold0d483b72018-01-17 01:00:20 -08001869 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1870 throws RemoteException {
Aaron Huang2617cf52021-11-29 16:31:32 +08001871 mNetd.ipSecRemoveTransportModeTransform(socket);
Nathan Harold031acb82017-03-07 13:23:36 -08001872 }
1873
Benedict Wong8bc90732018-01-18 18:31:45 -08001874 /**
1875 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
Yan Yane114b382021-02-16 16:29:48 -08001876 * security association as a correspondent policy to the provided interface.
1877 *
1878 * <p>If the transform is migrating, migrate the IPsec security association to new
1879 * source/destination addresses, and mark the migration as finished.
Benedict Wong8bc90732018-01-18 18:31:45 -08001880 */
1881 @Override
1882 public synchronized void applyTunnelModeTransform(
Yan Yane114b382021-02-16 16:29:48 -08001883 int tunnelResourceId, int direction, int transformResourceId, String callingPackage)
1884 throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001885 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001886 checkDirection(direction);
1887
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001888 int callingUid = Binder.getCallingUid();
1889 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001890
1891 // Get transform record; if no transform is found, will throw IllegalArgumentException
1892 TransformRecord transformInfo =
1893 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1894
1895 // Get tunnelInterface record; if no such interface is found, will throw
1896 // IllegalArgumentException
1897 TunnelInterfaceRecord tunnelInterfaceInfo =
1898 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1899
1900 // Get config and check that to-be-applied transform has the correct mode
1901 IpSecConfig c = transformInfo.getConfig();
1902 Preconditions.checkArgument(
1903 c.getMode() == IpSecTransform.MODE_TUNNEL,
1904 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1905
Benedict Wong8edc5572018-01-19 17:36:02 -08001906 EncapSocketRecord socketRecord = null;
1907 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1908 socketRecord =
1909 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1910 }
Benedict Wongec2e2e22019-10-03 11:09:00 -07001911 SpiRecord spiRecord = transformInfo.getSpiRecord();
Benedict Wong8edc5572018-01-19 17:36:02 -08001912
Benedict Wong8bc90732018-01-18 18:31:45 -08001913 int mark =
Benedict Wong38e52972018-05-07 20:06:44 -07001914 (direction == IpSecManager.DIRECTION_OUT)
1915 ? tunnelInterfaceInfo.getOkey()
Benedict Wong908d34e2021-04-15 11:59:16 -07001916 : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
Benedict Wong8bc90732018-01-18 18:31:45 -08001917
Benedict Wong8edc5572018-01-19 17:36:02 -08001918 try {
Benedict Wong5d749842018-09-06 11:31:25 -07001919 // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
1920 // SPI matching as part of the template resolution.
1921 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1922 c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
1923
Benedict Wong683441d2018-07-25 18:46:19 -07001924 // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
1925 // (and backporting) would allow us to narrow the mark space, and ensure that the SA
1926 // and SPs have matching marks (as VTI are meant to be built).
1927 // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
1928 // config matches the actual allocated resources in the kernel.
Benedict Wongc6fcedd2018-11-21 21:24:55 -08001929 // All SAs will have zero marks (from creation time), and any policy that matches the
1930 // same src/dst could match these SAs. Non-IpSecService governed processes that
1931 // establish floating policies with the same src/dst may result in undefined
1932 // behavior. This is generally limited to vendor code due to the permissions
1933 // (CAP_NET_ADMIN) required.
Benedict Wong683441d2018-07-25 18:46:19 -07001934 //
1935 // c.setMarkValue(mark);
1936 // c.setMarkMask(0xffffffff);
Benedict Wong8edc5572018-01-19 17:36:02 -08001937
1938 if (direction == IpSecManager.DIRECTION_OUT) {
1939 // Set output mark via underlying network (output only)
1940 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1941
Benedict Wong5d749842018-09-06 11:31:25 -07001942 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
1943 // but want to guarantee outbound packets are sent over the new SA.
Benedict Wongec2e2e22019-10-03 11:09:00 -07001944 spi = spiRecord.getSpi();
Benedict Wong5d749842018-09-06 11:31:25 -07001945 }
1946
1947 // Always update the policy with the relevant XFRM_IF_ID
1948 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +08001949 mNetd.ipSecUpdateSecurityPolicy(
1950 callingUid,
1951 selAddrFamily,
1952 direction,
1953 transformInfo.getConfig().getSourceAddress(),
1954 transformInfo.getConfig().getDestinationAddress(),
1955 spi, // If outbound, also add SPI to the policy.
1956 mark, // Must always set policy mark; ikey/okey for VTIs
1957 0xffffffff,
1958 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001959 }
1960
1961 // Update SA with tunnel mark (ikey or okey based on direction)
1962 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
Yan Yane114b382021-02-16 16:29:48 -08001963
1964 if (transformInfo.isMigrating()) {
1965 for (int selAddrFamily : ADDRESS_FAMILIES) {
1966 final IpSecMigrateInfoParcel migrateInfo =
1967 new IpSecMigrateInfoParcel(
1968 Binder.getCallingUid(),
1969 selAddrFamily,
1970 direction,
1971 c.getSourceAddress(),
1972 c.getDestinationAddress(),
1973 transformInfo.getNewSourceAddress(),
1974 transformInfo.getNewDestinationAddress(),
1975 c.getXfrmInterfaceId());
1976
1977 mNetd.ipSecMigrate(migrateInfo);
1978 }
1979 transformInfo.finishMigration();
1980 }
Benedict Wong8edc5572018-01-19 17:36:02 -08001981 } catch (ServiceSpecificException e) {
1982 if (e.errorCode == EINVAL) {
1983 throw new IllegalArgumentException(e.toString());
1984 } else {
1985 throw e;
1986 }
1987 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001988 }
1989
Nathan Harold031acb82017-03-07 13:23:36 -08001990 @Override
ludi89194d62017-05-22 10:52:23 -07001991 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001992 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludi89194d62017-05-22 10:52:23 -07001993
1994 pw.println("IpSecService dump:");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001995 pw.println();
ludi89194d62017-05-22 10:52:23 -07001996
Benedict Wong6855aee2017-11-16 15:27:22 -08001997 pw.println("mUserResourceTracker:");
1998 pw.println(mUserResourceTracker);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001999 }
2000}