blob: 9e71eb30904d8ecfc8a170efb3dabe15952a3d22 [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;
Yan Yana48dcd92022-10-18 00:03:20 +000020import static android.net.IpSecManager.FEATURE_IPSEC_TUNNEL_MIGRATION;
Nathan Harold031acb82017-03-07 13:23:36 -080021import static android.net.IpSecManager.INVALID_RESOURCE_ID;
Nathan Harold80865392017-04-04 19:37:48 -070022import static android.system.OsConstants.AF_INET;
evitayan43d93a02018-03-22 17:53:08 -070023import static android.system.OsConstants.AF_INET6;
24import static android.system.OsConstants.AF_UNSPEC;
manojboopathiac927fe2017-11-30 17:11:49 -080025import static android.system.OsConstants.EINVAL;
Nathan Harold80865392017-04-04 19:37:48 -070026import static android.system.OsConstants.IPPROTO_UDP;
27import static android.system.OsConstants.SOCK_DGRAM;
Benedict Wong683441d2018-07-25 18:46:19 -070028
Nathan Harold65ef8432018-03-15 18:06:06 -070029import android.annotation.NonNull;
30import android.app.AppOpsManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080031import android.content.Context;
Benedict Wong4aac3e92019-02-25 12:33:22 -080032import android.content.pm.PackageManager;
Yan Yana2f3b492020-09-29 23:38:00 -070033import android.net.ConnectivityManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080034import android.net.IIpSecService;
35import android.net.INetd;
Serik Beketayev7f507332020-12-06 22:31:23 -080036import android.net.InetAddresses;
Nathan Harold031acb82017-03-07 13:23:36 -080037import android.net.IpSecAlgorithm;
38import android.net.IpSecConfig;
39import android.net.IpSecManager;
Yan Yane114b382021-02-16 16:29:48 -080040import android.net.IpSecMigrateInfoParcel;
Nathan Harold80865392017-04-04 19:37:48 -070041import android.net.IpSecSpiResponse;
Nathan Harold031acb82017-03-07 13:23:36 -080042import android.net.IpSecTransform;
Nathan Harold80865392017-04-04 19:37:48 -070043import android.net.IpSecTransformResponse;
Benedict Wong8bc90732018-01-18 18:31:45 -080044import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold80865392017-04-04 19:37:48 -070045import android.net.IpSecUdpEncapResponse;
Benedict Wong97c3c942018-03-01 18:53:07 -080046import android.net.LinkAddress;
Yan Yana2f3b492020-09-29 23:38:00 -070047import android.net.LinkProperties;
Benedict Wong8bc90732018-01-18 18:31:45 -080048import android.net.Network;
Benedict Wong083faee2017-12-03 19:42:36 -080049import android.net.TrafficStats;
Nathan Harold031acb82017-03-07 13:23:36 -080050import android.os.Binder;
Nathan Harold031acb82017-03-07 13:23:36 -080051import android.os.IBinder;
52import android.os.ParcelFileDescriptor;
Benedict Wong908d34e2021-04-15 11:59:16 -070053import android.os.Process;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080054import android.os.RemoteException;
Nathan Harold031acb82017-03-07 13:23:36 -080055import android.os.ServiceSpecificException;
Nathan Harold80865392017-04-04 19:37:48 -070056import android.system.ErrnoException;
57import android.system.Os;
58import android.system.OsConstants;
Nathan Harold19b99d92017-08-23 13:46:33 -070059import android.text.TextUtils;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080060import android.util.Log;
lucaslin7eb76592021-03-11 17:39:49 +080061import android.util.Range;
Nathan Harold031acb82017-03-07 13:23:36 -080062import android.util.SparseArray;
Benedict Wong8bc90732018-01-18 18:31:45 -080063import android.util.SparseBooleanArray;
Nathan Harold19b99d92017-08-23 13:46:33 -070064
Nathan Harold031acb82017-03-07 13:23:36 -080065import com.android.internal.annotations.GuardedBy;
ludi5e623ea2017-05-12 09:15:00 -070066import com.android.internal.annotations.VisibleForTesting;
Benedict Wong70867e52017-11-06 20:49:10 -080067import com.android.internal.util.Preconditions;
paulhu00e34562021-10-26 09:00:50 +000068import com.android.net.module.util.BinderUtils;
lucaslinff6fe7b2021-02-03 23:59:45 +080069import com.android.net.module.util.NetdUtils;
Benedict Wong908d34e2021-04-15 11:59:16 -070070import com.android.net.module.util.PermissionUtils;
Nathan Harold19b99d92017-08-23 13:46:33 -070071
Benedict Wong683441d2018-07-25 18:46:19 -070072import libcore.io.IoUtils;
73
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080074import java.io.FileDescriptor;
Nathan Harold80865392017-04-04 19:37:48 -070075import java.io.IOException;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080076import java.io.PrintWriter;
evitayan43d93a02018-03-22 17:53:08 -070077import java.net.Inet4Address;
78import java.net.Inet6Address;
Nathan Harold80865392017-04-04 19:37:48 -070079import java.net.InetAddress;
80import java.net.InetSocketAddress;
81import java.net.UnknownHostException;
Benedict Wong02346822017-10-26 19:41:43 -070082import java.util.ArrayList;
83import java.util.List;
Benedict Wong529e8aa2020-02-11 23:49:36 -080084import java.util.Objects;
Nathan Harold19b99d92017-08-23 13:46:33 -070085
Benedict Wong02346822017-10-26 19:41:43 -070086/**
87 * A service to manage multiple clients that want to access the IpSec API. The service is
88 * responsible for maintaining a list of clients and managing the resources (and related quotas)
89 * that each of them own.
90 *
91 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
92 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
93 * thread is ever running at a time.
94 *
95 * @hide
96 */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080097public class IpSecService extends IIpSecService.Stub {
98 private static final String TAG = "IpSecService";
99 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Benedict Wong38e52972018-05-07 20:06:44 -0700100 private static final int[] ADDRESS_FAMILIES =
101 new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800102
ludi5e623ea2017-05-12 09:15:00 -0700103 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold80865392017-04-04 19:37:48 -0700104 private static final InetAddress INADDR_ANY;
105
Benedict Wong29c30772019-03-20 09:44:09 -0700106 @VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
107
Aaron Huang2617cf52021-11-29 16:31:32 +0800108 private final INetd mNetd;
109
Nathan Harold80865392017-04-04 19:37:48 -0700110 static {
111 try {
112 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
113 } catch (UnknownHostException e) {
114 throw new RuntimeException(e);
115 }
116 }
117
118 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
119 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
120
121 /* Binder context for this service */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800122 private final Context mContext;
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800123 private final Dependencies mDeps;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800124
Nathan Haroldfdafce22017-12-13 19:16:33 -0800125 /**
Nathan Harold5a19b952018-01-05 19:25:13 -0800126 * The next non-repeating global ID for tracking resources between users, this service, and
127 * kernel data structures. Accessing this variable is not thread safe, so it is only read or
128 * modified within blocks synchronized on IpSecService.this. We want to avoid -1
129 * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
Nathan Haroldfdafce22017-12-13 19:16:33 -0800130 */
131 @GuardedBy("IpSecService.this")
132 private int mNextResourceId = 1;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800133
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800134 /**
135 * Dependencies of IpSecService, for injection in tests.
136 */
137 @VisibleForTesting
138 public static class Dependencies {
139 /**
140 * Get a reference to INetd.
141 */
142 public INetd getNetdInstance(Context context) throws RemoteException {
143 final INetd netd = INetd.Stub.asInterface((IBinder)
144 context.getSystemService(Context.NETD_SERVICE));
145 if (netd == null) {
146 throw new RemoteException("Failed to Get Netd Instance");
147 }
148 return netd;
149 }
ludi5e623ea2017-05-12 09:15:00 -0700150 }
151
Benedict Wong083faee2017-12-03 19:42:36 -0800152 final UidFdTagger mUidFdTagger;
ludi5e623ea2017-05-12 09:15:00 -0700153
Benedict Wong02346822017-10-26 19:41:43 -0700154 /**
155 * Interface for user-reference and kernel-resource cleanup.
156 *
157 * <p>This interface must be implemented for a resource to be reference counted.
158 */
159 @VisibleForTesting
160 public interface IResource {
161 /**
162 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
163 * objects dependent on it.
164 *
165 * <p>Implementations of this method are expected to remove references to the IResource
166 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
167 * the resource is considered invalid for user access or allocation or use in other
168 * resources.
169 *
170 * <p>References to the IResource object may be held by other RefcountedResource objects,
Benedict Wongcbd329b2017-12-13 17:16:53 -0800171 * and as such, the underlying resources and quota may not be cleaned up.
Benedict Wong02346822017-10-26 19:41:43 -0700172 */
173 void invalidate() throws RemoteException;
174
175 /**
176 * Releases underlying resources and related quotas.
177 *
178 * <p>Implementations of this method are expected to remove all system resources that are
179 * tracked by the IResource object. Due to other RefcountedResource objects potentially
Benedict Wong6855aee2017-11-16 15:27:22 -0800180 * having references to the IResource object, freeUnderlyingResources may not always be
Benedict Wong02346822017-10-26 19:41:43 -0700181 * called from releaseIfUnreferencedRecursively().
182 */
183 void freeUnderlyingResources() throws RemoteException;
184 }
185
186 /**
187 * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
188 *
189 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
190 * RefcountedResource object creates an explicit reference that must be freed by calling
191 * userRelease(). Additionally, adding this object as a child of another RefcountedResource
192 * object will add an implicit reference.
193 *
194 * <p>Resources are cleaned up when all references, both implicit and explicit, are released
195 * (ie, when userRelease() is called and when all parents have called releaseReference() on this
196 * object.)
197 */
198 @VisibleForTesting
199 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000200 private final T mResource;
201 private final List<RefcountedResource> mChildren;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800202 int mRefCount = 1; // starts at 1 for user's reference.
Junyu Lai75eabfe2019-04-26 01:38:04 +0000203 IBinder mBinder;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800204
Junyu Lai75eabfe2019-04-26 01:38:04 +0000205 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
Benedict Wong02346822017-10-26 19:41:43 -0700206 synchronized (IpSecService.this) {
207 this.mResource = resource;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000208 this.mChildren = new ArrayList<>(children.length);
Benedict Wong02346822017-10-26 19:41:43 -0700209 this.mBinder = binder;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000210
Benedict Wong02346822017-10-26 19:41:43 -0700211 for (RefcountedResource child : children) {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000212 mChildren.add(child);
Benedict Wong02346822017-10-26 19:41:43 -0700213 child.mRefCount++;
214 }
215
216 try {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000217 mBinder.linkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700218 } catch (RemoteException e) {
219 binderDied();
Benedict Wong04738f52019-02-28 20:28:48 -0800220 e.rethrowFromSystemServer();
Benedict Wong02346822017-10-26 19:41:43 -0700221 }
222 }
223 }
224
225 /**
226 * If the Binder object dies, this function is called to free the system resources that are
Benedict Wong6855aee2017-11-16 15:27:22 -0800227 * being tracked by this record and to subsequently release this record for garbage
Benedict Wong02346822017-10-26 19:41:43 -0700228 * collection
229 */
230 @Override
231 public void binderDied() {
232 synchronized (IpSecService.this) {
233 try {
234 userRelease();
235 } catch (Exception e) {
236 Log.e(TAG, "Failed to release resource: " + e);
237 }
238 }
239 }
240
241 public T getResource() {
242 return mResource;
243 }
244
245 /**
246 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
247 * arrays)
248 *
249 * <p>If this method has been previously called, the RefcountedResource's binder field will
250 * be null, and the method will return without performing the cleanup a second time.
251 *
252 * <p>Note that calling this function does not imply that kernel resources will be freed at
253 * this time, or that the related quota will be returned. Such actions will only be
254 * performed upon the reference count reaching zero.
255 */
256 @GuardedBy("IpSecService.this")
257 public void userRelease() throws RemoteException {
258 // Prevent users from putting reference counts into a bad state by calling
259 // userRelease() multiple times.
260 if (mBinder == null) {
261 return;
262 }
263
Junyu Lai75eabfe2019-04-26 01:38:04 +0000264 mBinder.unlinkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700265 mBinder = null;
266
267 mResource.invalidate();
Junyu Lai75eabfe2019-04-26 01:38:04 +0000268
Benedict Wong02346822017-10-26 19:41:43 -0700269 releaseReference();
270 }
271
272 /**
273 * Removes a reference to this resource. If the resultant reference count is zero, the
274 * underlying resources are freed, and references to all child resources are also dropped
275 * recursively (resulting in them freeing their resources and children, etcetera)
276 *
277 * <p>This method also sets the reference count to an invalid value (-1) to signify that it
278 * has been fully released. Any subsequent calls to this method will result in an
279 * IllegalStateException being thrown due to resource already having been previously
280 * released
281 */
282 @VisibleForTesting
283 @GuardedBy("IpSecService.this")
284 public void releaseReference() throws RemoteException {
285 mRefCount--;
286
287 if (mRefCount > 0) {
288 return;
289 } else if (mRefCount < 0) {
290 throw new IllegalStateException(
291 "Invalid operation - resource has already been released.");
292 }
293
294 // Cleanup own resources
295 mResource.freeUnderlyingResources();
296
297 // Cleanup child resources as needed
298 for (RefcountedResource<? extends IResource> child : mChildren) {
299 child.releaseReference();
300 }
301
302 // Enforce that resource cleanup can only be called once
303 // By decrementing the refcount (from 0 to -1), the next call will throw an
304 // IllegalStateException - it has already been released fully.
305 mRefCount--;
306 }
307
308 @Override
309 public String toString() {
310 return new StringBuilder()
311 .append("{mResource=")
312 .append(mResource)
313 .append(", mRefCount=")
314 .append(mRefCount)
315 .append(", mChildren=")
316 .append(mChildren)
317 .append("}")
318 .toString();
319 }
320 }
321
Benedict Wongcbd329b2017-12-13 17:16:53 -0800322 /**
323 * Very simple counting class that looks much like a counting semaphore
324 *
325 * <p>This class is not thread-safe, and expects that that users of this class will ensure
326 * synchronization and thread safety by holding the IpSecService.this instance lock.
327 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800328 @VisibleForTesting
329 static class ResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700330 private final int mMax;
331 int mCurrent;
332
333 ResourceTracker(int max) {
334 mMax = max;
335 mCurrent = 0;
336 }
337
Benedict Wong6855aee2017-11-16 15:27:22 -0800338 boolean isAvailable() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700339 return (mCurrent < mMax);
340 }
341
Benedict Wong6855aee2017-11-16 15:27:22 -0800342 void take() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700343 if (!isAvailable()) {
344 Log.wtf(TAG, "Too many resources allocated!");
345 }
346 mCurrent++;
347 }
348
Benedict Wong6855aee2017-11-16 15:27:22 -0800349 void give() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700350 if (mCurrent <= 0) {
351 Log.wtf(TAG, "We've released this resource too many times");
352 }
353 mCurrent--;
354 }
ludi529fdec2017-08-10 15:44:40 -0700355
356 @Override
357 public String toString() {
358 return new StringBuilder()
359 .append("{mCurrent=")
360 .append(mCurrent)
361 .append(", mMax=")
362 .append(mMax)
363 .append("}")
364 .toString();
365 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700366 }
367
Benedict Wong6855aee2017-11-16 15:27:22 -0800368 @VisibleForTesting
369 static final class UserRecord {
Benedict Wong6855aee2017-11-16 15:27:22 -0800370 /* Maximum number of each type of resource that a single UID may possess */
Benedict Wong7bcf9c22020-02-11 23:36:42 -0800371
372 // Up to 4 active VPNs/IWLAN with potential soft handover.
373 public static final int MAX_NUM_TUNNEL_INTERFACES = 8;
374 public static final int MAX_NUM_ENCAP_SOCKETS = 16;
375
376 // SPIs and Transforms are both cheap, and are 1:1 correlated.
377 public static final int MAX_NUM_TRANSFORMS = 64;
378 public static final int MAX_NUM_SPIS = 64;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700379
Benedict Wongcbd329b2017-12-13 17:16:53 -0800380 /**
381 * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
382 * and explicit (user) reference management.
383 *
384 * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
385 *
386 * <p>Resources are removed from this array when the user releases their explicit reference
387 * by calling one of the releaseResource() methods.
388 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800389 final RefcountedResourceArray<SpiRecord> mSpiRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800390 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800391 final RefcountedResourceArray<TransformRecord> mTransformRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800392 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800393 final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800394 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
Benedict Wong8bc90732018-01-18 18:31:45 -0800395 final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
396 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
Benedict Wongcbd329b2017-12-13 17:16:53 -0800397
398 /**
399 * Trackers for quotas for each of the OwnedResource types.
400 *
401 * <p>These trackers are separate from the resource arrays, since they are incremented and
402 * decremented at different points in time. Specifically, quota is only returned upon final
403 * resource deallocation (after all explicit and implicit references are released). Note
404 * that it is possible that calls to releaseResource() will not return the used quota if
405 * there are other resources that depend on (are parents of) the resource being released.
406 */
407 final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
408 final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
Benedict Wong6855aee2017-11-16 15:27:22 -0800409 final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
Benedict Wong8bc90732018-01-18 18:31:45 -0800410 final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
Benedict Wong6855aee2017-11-16 15:27:22 -0800411
412 void removeSpiRecord(int resourceId) {
413 mSpiRecords.remove(resourceId);
Nathan Harold6e4681c2017-04-24 16:16:34 -0700414 }
415
Benedict Wong6855aee2017-11-16 15:27:22 -0800416 void removeTransformRecord(int resourceId) {
417 mTransformRecords.remove(resourceId);
418 }
419
Benedict Wong8bc90732018-01-18 18:31:45 -0800420 void removeTunnelInterfaceRecord(int resourceId) {
421 mTunnelInterfaceRecords.remove(resourceId);
422 }
423
Benedict Wong6855aee2017-11-16 15:27:22 -0800424 void removeEncapSocketRecord(int resourceId) {
425 mEncapSocketRecords.remove(resourceId);
426 }
427
428 @Override
429 public String toString() {
430 return new StringBuilder()
431 .append("{mSpiQuotaTracker=")
432 .append(mSpiQuotaTracker)
433 .append(", mTransformQuotaTracker=")
434 .append(mTransformQuotaTracker)
435 .append(", mSocketQuotaTracker=")
436 .append(mSocketQuotaTracker)
Benedict Wong76603702018-01-24 15:31:39 -0800437 .append(", mTunnelQuotaTracker=")
438 .append(mTunnelQuotaTracker)
Benedict Wong6855aee2017-11-16 15:27:22 -0800439 .append(", mSpiRecords=")
440 .append(mSpiRecords)
441 .append(", mTransformRecords=")
442 .append(mTransformRecords)
443 .append(", mEncapSocketRecords=")
444 .append(mEncapSocketRecords)
Benedict Wong76603702018-01-24 15:31:39 -0800445 .append(", mTunnelInterfaceRecords=")
446 .append(mTunnelInterfaceRecords)
Benedict Wong6855aee2017-11-16 15:27:22 -0800447 .append("}")
448 .toString();
449 }
450 }
451
Benedict Wongcbd329b2017-12-13 17:16:53 -0800452 /**
453 * This class is not thread-safe, and expects that that users of this class will ensure
454 * synchronization and thread safety by holding the IpSecService.this instance lock.
455 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800456 @VisibleForTesting
457 static final class UserResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700458 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
459
Benedict Wongcbd329b2017-12-13 17:16:53 -0800460 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
Benedict Wong6855aee2017-11-16 15:27:22 -0800461 public UserRecord getUserRecord(int uid) {
462 checkCallerUid(uid);
463
Nathan Harold6e4681c2017-04-24 16:16:34 -0700464 UserRecord r = mUserRecords.get(uid);
465 if (r == null) {
466 r = new UserRecord();
467 mUserRecords.put(uid, r);
468 }
469 return r;
470 }
ludi529fdec2017-08-10 15:44:40 -0700471
Benedict Wong6855aee2017-11-16 15:27:22 -0800472 /** Safety method; guards against access of other user's UserRecords */
473 private void checkCallerUid(int uid) {
Benedict Wong908d34e2021-04-15 11:59:16 -0700474 if (uid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800475 throw new SecurityException("Attempted access of unowned resources");
476 }
477 }
478
ludi529fdec2017-08-10 15:44:40 -0700479 @Override
480 public String toString() {
481 return mUserRecords.toString();
482 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700483 }
484
Benedict Wong6855aee2017-11-16 15:27:22 -0800485 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700486
Nathan Harold80865392017-04-04 19:37:48 -0700487 /**
Benedict Wongcbd329b2017-12-13 17:16:53 -0800488 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
Benedict Wong6855aee2017-11-16 15:27:22 -0800489 * resources. It relies on a provided resourceId that should uniquely identify the kernel
490 * resource. To use this class, the user should implement the invalidate() and
491 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
Benedict Wongcbd329b2017-12-13 17:16:53 -0800492 * tracking arrays and kernel resources, respectively.
493 *
494 * <p>This class associates kernel resources with the UID that owns and controls them.
Nathan Harold80865392017-04-04 19:37:48 -0700495 */
Benedict Wongcbd329b2017-12-13 17:16:53 -0800496 private abstract class OwnedResourceRecord implements IResource {
Aaron Huangfbae3082021-12-06 15:18:42 +0800497 final int mPid;
498 final int mUid;
Benedict Wong6855aee2017-11-16 15:27:22 -0800499 protected final int mResourceId;
Nathan Harold031acb82017-03-07 13:23:36 -0800500
Benedict Wongcbd329b2017-12-13 17:16:53 -0800501 OwnedResourceRecord(int resourceId) {
Nathan Harold031acb82017-03-07 13:23:36 -0800502 super();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700503 if (resourceId == INVALID_RESOURCE_ID) {
504 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
505 }
Nathan Harold80865392017-04-04 19:37:48 -0700506 mResourceId = resourceId;
Aaron Huangfbae3082021-12-06 15:18:42 +0800507 mPid = Binder.getCallingPid();
508 mUid = Binder.getCallingUid();
Nathan Harold031acb82017-03-07 13:23:36 -0800509
Nathan Harold6e4681c2017-04-24 16:16:34 -0700510 getResourceTracker().take();
Nathan Harold031acb82017-03-07 13:23:36 -0800511 }
512
Benedict Wong6855aee2017-11-16 15:27:22 -0800513 @Override
514 public abstract void invalidate() throws RemoteException;
515
516 /** Convenience method; retrieves the user resource record for the stored UID. */
517 protected UserRecord getUserRecord() {
Aaron Huangfbae3082021-12-06 15:18:42 +0800518 return mUserResourceTracker.getUserRecord(mUid);
Nathan Harold80865392017-04-04 19:37:48 -0700519 }
520
Benedict Wong6855aee2017-11-16 15:27:22 -0800521 @Override
522 public abstract void freeUnderlyingResources() throws RemoteException;
ludi89194d62017-05-22 10:52:23 -0700523
Nathan Harold6e4681c2017-04-24 16:16:34 -0700524 /** Get the resource tracker for this resource */
525 protected abstract ResourceTracker getResourceTracker();
526
ludi89194d62017-05-22 10:52:23 -0700527 @Override
528 public String toString() {
529 return new StringBuilder()
530 .append("{mResourceId=")
531 .append(mResourceId)
532 .append(", pid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800533 .append(mPid)
ludi89194d62017-05-22 10:52:23 -0700534 .append(", uid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800535 .append(mUid)
ludi89194d62017-05-22 10:52:23 -0700536 .append("}")
537 .toString();
538 }
Nathan Harold031acb82017-03-07 13:23:36 -0800539 };
540
Nathan Harold80865392017-04-04 19:37:48 -0700541 /**
Benedict Wong6855aee2017-11-16 15:27:22 -0800542 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
543 *
544 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
545 * if a key is not found during a retrieval process.
Nathan Harold80865392017-04-04 19:37:48 -0700546 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800547 static class RefcountedResourceArray<T extends IResource> {
548 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
549 private final String mTypeName;
Nathan Harold031acb82017-03-07 13:23:36 -0800550
Aaron Huangfbae3082021-12-06 15:18:42 +0800551 RefcountedResourceArray(String typeName) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800552 this.mTypeName = typeName;
Nathan Harold80865392017-04-04 19:37:48 -0700553 }
554
Benedict Wong6855aee2017-11-16 15:27:22 -0800555 /**
556 * Accessor method to get inner resource object.
557 *
558 * @throws IllegalArgumentException if no resource with provided key is found.
559 */
560 T getResourceOrThrow(int key) {
561 return getRefcountedResourceOrThrow(key).getResource();
562 }
563
564 /**
565 * Accessor method to get reference counting wrapper.
566 *
567 * @throws IllegalArgumentException if no resource with provided key is found.
568 */
569 RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
570 RefcountedResource<T> resource = mArray.get(key);
571 if (resource == null) {
572 throw new IllegalArgumentException(
573 String.format("No such %s found for given id: %d", mTypeName, key));
574 }
575
576 return resource;
577 }
578
579 void put(int key, RefcountedResource<T> obj) {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +0000580 Objects.requireNonNull(obj, "Null resources cannot be added");
Nathan Harold80865392017-04-04 19:37:48 -0700581 mArray.put(key, obj);
582 }
583
584 void remove(int key) {
585 mArray.remove(key);
586 }
ludi89194d62017-05-22 10:52:23 -0700587
588 @Override
589 public String toString() {
590 return mArray.toString();
591 }
Nathan Harold80865392017-04-04 19:37:48 -0700592 }
593
Benedict Wongcbd329b2017-12-13 17:16:53 -0800594 /**
Yan Yane114b382021-02-16 16:29:48 -0800595 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is created, the
596 * SpiRecord that originally tracked the SAs will reliquish the responsibility of freeing the
597 * underlying SA to this class via the mOwnedByTransform flag.
598 *
599 * <p>This class is not thread-safe, and expects that that users of this class will ensure
600 * synchronization and thread safety by holding the IpSecService.this instance lock
Benedict Wongcbd329b2017-12-13 17:16:53 -0800601 */
602 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700603 private final IpSecConfig mConfig;
Nathan Harold5a19b952018-01-05 19:25:13 -0800604 private final SpiRecord mSpi;
Benedict Wong6855aee2017-11-16 15:27:22 -0800605 private final EncapSocketRecord mSocket;
Yan Yane114b382021-02-16 16:29:48 -0800606 private String mNewSourceAddress = null;
607 private String mNewDestinationAddress = null;
Nathan Harold80865392017-04-04 19:37:48 -0700608
609 TransformRecord(
Nathan Harold5a19b952018-01-05 19:25:13 -0800610 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800611 super(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -0800612 mConfig = config;
Nathan Harold5a19b952018-01-05 19:25:13 -0800613 mSpi = spi;
Nathan Harold80865392017-04-04 19:37:48 -0700614 mSocket = socket;
Benedict Wong68aac2a2017-12-13 18:26:40 -0800615
616 spi.setOwnedByTransform();
Nathan Harold031acb82017-03-07 13:23:36 -0800617 }
618
619 public IpSecConfig getConfig() {
620 return mConfig;
621 }
622
Nathan Harold5a19b952018-01-05 19:25:13 -0800623 public SpiRecord getSpiRecord() {
624 return mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700625 }
626
Benedict Wong8edc5572018-01-19 17:36:02 -0800627 public EncapSocketRecord getSocketRecord() {
628 return mSocket;
629 }
630
Yan Yane114b382021-02-16 16:29:48 -0800631 @GuardedBy("IpSecService.this")
632 public String getNewSourceAddress() {
633 return mNewSourceAddress;
634 }
635
636 @GuardedBy("IpSecService.this")
637 public String getNewDestinationAddress() {
638 return mNewDestinationAddress;
639 }
640
641 private void verifyTunnelModeOrThrow() {
642 if (mConfig.getMode() != IpSecTransform.MODE_TUNNEL) {
643 throw new UnsupportedOperationException(
644 "Migration requested/called on non-tunnel-mode transform");
645 }
646 }
647
648 /** Start migrating this transform to new source and destination addresses */
649 @GuardedBy("IpSecService.this")
650 public void startMigration(String newSourceAddress, String newDestinationAddress) {
651 verifyTunnelModeOrThrow();
652 Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
653 Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
654 mNewSourceAddress = newSourceAddress;
655 mNewDestinationAddress = newDestinationAddress;
656 }
657
658 /** Finish migration and update addresses. */
659 @GuardedBy("IpSecService.this")
660 public void finishMigration() {
661 verifyTunnelModeOrThrow();
662 mConfig.setSourceAddress(mNewSourceAddress);
663 mConfig.setDestinationAddress(mNewDestinationAddress);
664 mNewSourceAddress = null;
665 mNewDestinationAddress = null;
666 }
667
668 /** Return if this transform is going to be migrated. */
669 @GuardedBy("IpSecService.this")
670 public boolean isMigrating() {
671 verifyTunnelModeOrThrow();
672
673 return mNewSourceAddress != null;
674 }
675
Nathan Harold80865392017-04-04 19:37:48 -0700676 /** always guarded by IpSecService#this */
Nathan Harold031acb82017-03-07 13:23:36 -0800677 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800678 public void freeUnderlyingResources() {
Nathan Harold5a19b952018-01-05 19:25:13 -0800679 int spi = mSpi.getSpi();
680 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800681 mNetd.ipSecDeleteSecurityAssociation(
682 mUid,
683 mConfig.getSourceAddress(),
684 mConfig.getDestinationAddress(),
685 spi,
686 mConfig.getMarkValue(),
687 mConfig.getMarkMask(),
688 mConfig.getXfrmInterfaceId());
Benedict Wong97c3c942018-03-01 18:53:07 -0800689 } catch (RemoteException | ServiceSpecificException e) {
690 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800691 }
Nathan Harold031acb82017-03-07 13:23:36 -0800692
Benedict Wong6855aee2017-11-16 15:27:22 -0800693 getResourceTracker().give();
Nathan Harold031acb82017-03-07 13:23:36 -0800694 }
ludi89194d62017-05-22 10:52:23 -0700695
Benedict Wong6855aee2017-11-16 15:27:22 -0800696 @Override
697 public void invalidate() throws RemoteException {
698 getUserRecord().removeTransformRecord(mResourceId);
699 }
700
701 @Override
Nathan Harold6e4681c2017-04-24 16:16:34 -0700702 protected ResourceTracker getResourceTracker() {
Benedict Wong6855aee2017-11-16 15:27:22 -0800703 return getUserRecord().mTransformQuotaTracker;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700704 }
705
ludi89194d62017-05-22 10:52:23 -0700706 @Override
707 public String toString() {
708 StringBuilder strBuilder = new StringBuilder();
709 strBuilder
710 .append("{super=")
711 .append(super.toString())
712 .append(", mSocket=")
713 .append(mSocket)
Nathan Harold5a19b952018-01-05 19:25:13 -0800714 .append(", mSpi.mResourceId=")
715 .append(mSpi.mResourceId)
ludi89194d62017-05-22 10:52:23 -0700716 .append(", mConfig=")
717 .append(mConfig)
718 .append("}");
719 return strBuilder.toString();
720 }
Nathan Harold031acb82017-03-07 13:23:36 -0800721 }
722
Benedict Wongcbd329b2017-12-13 17:16:53 -0800723 /**
724 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
725 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
726 * object
727 */
728 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harold5a19b952018-01-05 19:25:13 -0800729 private final String mSourceAddress;
730 private final String mDestinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800731 private int mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700732
733 private boolean mOwnedByTransform = false;
Nathan Harold031acb82017-03-07 13:23:36 -0800734
Aaron Huang2617cf52021-11-29 16:31:32 +0800735 SpiRecord(int resourceId, String sourceAddress,
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800736 String destinationAddress, int spi) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800737 super(resourceId);
Nathan Harold5a19b952018-01-05 19:25:13 -0800738 mSourceAddress = sourceAddress;
739 mDestinationAddress = destinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800740 mSpi = spi;
Nathan Harold031acb82017-03-07 13:23:36 -0800741 }
742
Nathan Harold80865392017-04-04 19:37:48 -0700743 /** always guarded by IpSecService#this */
744 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800745 public void freeUnderlyingResources() {
Nathan Harold031acb82017-03-07 13:23:36 -0800746 try {
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800747 if (!mOwnedByTransform) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800748 mNetd.ipSecDeleteSecurityAssociation(
749 mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
750 0 /* mask */, 0 /* if_id */);
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800751 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800752 } catch (ServiceSpecificException | RemoteException e) {
753 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800754 }
Nathan Harold80865392017-04-04 19:37:48 -0700755
756 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -0800757
Benedict Wong6855aee2017-11-16 15:27:22 -0800758 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700759 }
760
Nathan Harold80865392017-04-04 19:37:48 -0700761 public int getSpi() {
762 return mSpi;
763 }
764
Nathan Harold5a19b952018-01-05 19:25:13 -0800765 public String getDestinationAddress() {
766 return mDestinationAddress;
767 }
768
Nathan Harold80865392017-04-04 19:37:48 -0700769 public void setOwnedByTransform() {
770 if (mOwnedByTransform) {
771 // Programming error
Andreas Gampeafb01e22017-07-11 10:25:09 -0700772 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold80865392017-04-04 19:37:48 -0700773 }
774
775 mOwnedByTransform = true;
Nathan Harold031acb82017-03-07 13:23:36 -0800776 }
ludi89194d62017-05-22 10:52:23 -0700777
Benedict Wong68aac2a2017-12-13 18:26:40 -0800778 public boolean getOwnedByTransform() {
779 return mOwnedByTransform;
780 }
781
ludi89194d62017-05-22 10:52:23 -0700782 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800783 public void invalidate() throws RemoteException {
784 getUserRecord().removeSpiRecord(mResourceId);
785 }
786
787 @Override
788 protected ResourceTracker getResourceTracker() {
789 return getUserRecord().mSpiQuotaTracker;
790 }
791
792 @Override
ludi89194d62017-05-22 10:52:23 -0700793 public String toString() {
794 StringBuilder strBuilder = new StringBuilder();
795 strBuilder
796 .append("{super=")
797 .append(super.toString())
798 .append(", mSpi=")
799 .append(mSpi)
Nathan Harold5a19b952018-01-05 19:25:13 -0800800 .append(", mSourceAddress=")
801 .append(mSourceAddress)
802 .append(", mDestinationAddress=")
803 .append(mDestinationAddress)
ludi89194d62017-05-22 10:52:23 -0700804 .append(", mOwnedByTransform=")
805 .append(mOwnedByTransform)
806 .append("}");
807 return strBuilder.toString();
808 }
Nathan Harold031acb82017-03-07 13:23:36 -0800809 }
810
Benedict Wong8bc90732018-01-18 18:31:45 -0800811 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
lucaslin7eb76592021-03-11 17:39:49 +0800812 final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
813 private int mNextTunnelNetId = mNetIdRange.getLower();
Benedict Wong8bc90732018-01-18 18:31:45 -0800814
815 /**
816 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
817 *
818 * <p>This method should only be called from Binder threads. Do not call this from within the
819 * system server as it will crash the system on failure.
820 *
821 * @return an integer key within the netId range, if successful
822 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
823 */
824 @VisibleForTesting
825 int reserveNetId() {
lucaslin7eb76592021-03-11 17:39:49 +0800826 final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
Benedict Wong8bc90732018-01-18 18:31:45 -0800827 synchronized (mTunnelNetIds) {
lucaslin7eb76592021-03-11 17:39:49 +0800828 for (int i = 0; i < range; i++) {
829 final int netId = mNextTunnelNetId;
830 if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
831 mNextTunnelNetId = mNetIdRange.getLower();
832 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800833 if (!mTunnelNetIds.get(netId)) {
834 mTunnelNetIds.put(netId, true);
835 return netId;
836 }
837 }
838 }
839 throw new IllegalStateException("No free netIds to allocate");
840 }
841
842 @VisibleForTesting
843 void releaseNetId(int netId) {
844 synchronized (mTunnelNetIds) {
845 mTunnelNetIds.delete(netId);
846 }
847 }
848
Yan Yana2f3b492020-09-29 23:38:00 -0700849 /**
850 * Tracks an tunnel interface, and manages cleanup paths.
851 *
852 * <p>This class is not thread-safe, and expects that that users of this class will ensure
853 * synchronization and thread safety by holding the IpSecService.this instance lock
854 */
855 @VisibleForTesting
856 final class TunnelInterfaceRecord extends OwnedResourceRecord {
Benedict Wong8bc90732018-01-18 18:31:45 -0800857 private final String mInterfaceName;
Benedict Wong8bc90732018-01-18 18:31:45 -0800858
859 // outer addresses
860 private final String mLocalAddress;
861 private final String mRemoteAddress;
862
863 private final int mIkey;
864 private final int mOkey;
865
Benedict Wong5d749842018-09-06 11:31:25 -0700866 private final int mIfId;
867
Yan Yana2f3b492020-09-29 23:38:00 -0700868 private Network mUnderlyingNetwork;
869
Benedict Wong8bc90732018-01-18 18:31:45 -0800870 TunnelInterfaceRecord(
871 int resourceId,
872 String interfaceName,
873 Network underlyingNetwork,
874 String localAddr,
875 String remoteAddr,
876 int ikey,
Benedict Wong5d749842018-09-06 11:31:25 -0700877 int okey,
878 int intfId) {
Benedict Wong8bc90732018-01-18 18:31:45 -0800879 super(resourceId);
880
881 mInterfaceName = interfaceName;
882 mUnderlyingNetwork = underlyingNetwork;
883 mLocalAddress = localAddr;
884 mRemoteAddress = remoteAddr;
885 mIkey = ikey;
886 mOkey = okey;
Benedict Wong5d749842018-09-06 11:31:25 -0700887 mIfId = intfId;
Benedict Wong8bc90732018-01-18 18:31:45 -0800888 }
889
890 /** always guarded by IpSecService#this */
891 @Override
892 public void freeUnderlyingResources() {
Benedict Wong8edc5572018-01-19 17:36:02 -0800893 // Calls to netd
Benedict Wong8bc90732018-01-18 18:31:45 -0800894 // Teardown VTI
895 // Delete global policies
Benedict Wong8edc5572018-01-19 17:36:02 -0800896 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800897 mNetd.ipSecRemoveTunnelInterface(mInterfaceName);
Benedict Wong8edc5572018-01-19 17:36:02 -0800898
Benedict Wong38e52972018-05-07 20:06:44 -0700899 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800900 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800901 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700902 selAddrFamily,
903 IpSecManager.DIRECTION_OUT,
904 mOkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700905 0xffffffff,
906 mIfId);
Aaron Huang2617cf52021-11-29 16:31:32 +0800907 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800908 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700909 selAddrFamily,
910 IpSecManager.DIRECTION_IN,
911 mIkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700912 0xffffffff,
913 mIfId);
Yan Yanf4eceba2022-10-31 20:41:13 +0000914 mNetd.ipSecDeleteSecurityPolicy(
915 mUid,
916 selAddrFamily,
917 IpSecManager.DIRECTION_FWD,
918 mIkey,
919 0xffffffff,
920 mIfId);
Benedict Wong8edc5572018-01-19 17:36:02 -0800921 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800922 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong8edc5572018-01-19 17:36:02 -0800923 Log.e(
924 TAG,
925 "Failed to delete VTI with interface name: "
926 + mInterfaceName
927 + " and id: "
Benedict Wong97c3c942018-03-01 18:53:07 -0800928 + mResourceId, e);
Benedict Wong8edc5572018-01-19 17:36:02 -0800929 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800930
931 getResourceTracker().give();
932 releaseNetId(mIkey);
933 releaseNetId(mOkey);
934 }
935
Yan Yana2f3b492020-09-29 23:38:00 -0700936 @GuardedBy("IpSecService.this")
937 public void setUnderlyingNetwork(Network underlyingNetwork) {
938 // When #applyTunnelModeTransform is called, this new underlying network will be used to
939 // update the output mark of the input transform.
940 mUnderlyingNetwork = underlyingNetwork;
Benedict Wong8bc90732018-01-18 18:31:45 -0800941 }
942
Yan Yana2f3b492020-09-29 23:38:00 -0700943 @GuardedBy("IpSecService.this")
Benedict Wong8bc90732018-01-18 18:31:45 -0800944 public Network getUnderlyingNetwork() {
945 return mUnderlyingNetwork;
946 }
947
Yan Yana2f3b492020-09-29 23:38:00 -0700948 public String getInterfaceName() {
949 return mInterfaceName;
950 }
951
Benedict Wong8bc90732018-01-18 18:31:45 -0800952 /** Returns the local, outer address for the tunnelInterface */
953 public String getLocalAddress() {
954 return mLocalAddress;
955 }
956
957 /** Returns the remote, outer address for the tunnelInterface */
958 public String getRemoteAddress() {
959 return mRemoteAddress;
960 }
961
962 public int getIkey() {
963 return mIkey;
964 }
965
966 public int getOkey() {
967 return mOkey;
968 }
969
Benedict Wong5d749842018-09-06 11:31:25 -0700970 public int getIfId() {
971 return mIfId;
972 }
973
Benedict Wong8bc90732018-01-18 18:31:45 -0800974 @Override
975 protected ResourceTracker getResourceTracker() {
976 return getUserRecord().mTunnelQuotaTracker;
977 }
978
979 @Override
980 public void invalidate() {
981 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
982 }
983
984 @Override
985 public String toString() {
986 return new StringBuilder()
987 .append("{super=")
988 .append(super.toString())
989 .append(", mInterfaceName=")
990 .append(mInterfaceName)
991 .append(", mUnderlyingNetwork=")
992 .append(mUnderlyingNetwork)
993 .append(", mLocalAddress=")
994 .append(mLocalAddress)
995 .append(", mRemoteAddress=")
996 .append(mRemoteAddress)
997 .append(", mIkey=")
998 .append(mIkey)
999 .append(", mOkey=")
1000 .append(mOkey)
1001 .append("}")
1002 .toString();
1003 }
1004 }
1005
Benedict Wongcbd329b2017-12-13 17:16:53 -08001006 /**
1007 * Tracks a UDP encap socket, and manages cleanup paths
1008 *
1009 * <p>While this class does not manage non-kernel resources, race conditions around socket
1010 * binding require that the service creates the encap socket, binds it and applies the socket
1011 * policy before handing it to a user.
1012 */
1013 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -07001014 private FileDescriptor mSocket;
1015 private final int mPort;
Nathan Harold031acb82017-03-07 13:23:36 -08001016
Benedict Wong6855aee2017-11-16 15:27:22 -08001017 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
1018 super(resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001019 mSocket = socket;
1020 mPort = port;
1021 }
1022
1023 /** always guarded by IpSecService#this */
1024 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001025 public void freeUnderlyingResources() {
Nathan Harold80865392017-04-04 19:37:48 -07001026 Log.d(TAG, "Closing port " + mPort);
1027 IoUtils.closeQuietly(mSocket);
1028 mSocket = null;
Nathan Harold80865392017-04-04 19:37:48 -07001029
Benedict Wong6855aee2017-11-16 15:27:22 -08001030 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -07001031 }
1032
Nathan Harold80865392017-04-04 19:37:48 -07001033 public int getPort() {
1034 return mPort;
1035 }
1036
Benedict Wonga386e372018-03-27 16:55:48 -07001037 public FileDescriptor getFileDescriptor() {
Nathan Harold80865392017-04-04 19:37:48 -07001038 return mSocket;
1039 }
ludi89194d62017-05-22 10:52:23 -07001040
1041 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001042 protected ResourceTracker getResourceTracker() {
1043 return getUserRecord().mSocketQuotaTracker;
1044 }
1045
1046 @Override
1047 public void invalidate() {
1048 getUserRecord().removeEncapSocketRecord(mResourceId);
1049 }
1050
1051 @Override
ludi89194d62017-05-22 10:52:23 -07001052 public String toString() {
1053 return new StringBuilder()
1054 .append("{super=")
1055 .append(super.toString())
1056 .append(", mSocket=")
1057 .append(mSocket)
1058 .append(", mPort=")
1059 .append(mPort)
1060 .append("}")
1061 .toString();
1062 }
Nathan Harold80865392017-04-04 19:37:48 -07001063 }
Nathan Harold031acb82017-03-07 13:23:36 -08001064
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001065 /**
1066 * Constructs a new IpSecService instance
1067 *
1068 * @param context Binder context for this service
1069 */
Aaron Huangb944ff12022-01-12 15:11:01 +08001070 public IpSecService(Context context) {
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001071 this(context, new Dependencies());
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001072 }
1073
Nathan Harold65ef8432018-03-15 18:06:06 -07001074 @NonNull
1075 private AppOpsManager getAppOpsManager() {
1076 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Aaron Huangfbae3082021-12-06 15:18:42 +08001077 if (appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
Nathan Harold65ef8432018-03-15 18:06:06 -07001078 return appOps;
1079 }
1080
ludi5e623ea2017-05-12 09:15:00 -07001081 /** @hide */
1082 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001083 public IpSecService(Context context, Dependencies deps) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001084 this(
1085 context,
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001086 deps,
Nathan Harold5a19b952018-01-05 19:25:13 -08001087 (fd, uid) -> {
1088 try {
1089 TrafficStats.setThreadStatsUid(uid);
1090 TrafficStats.tagFileDescriptor(fd);
1091 } finally {
1092 TrafficStats.clearThreadStatsUid();
1093 }
1094 });
Benedict Wong083faee2017-12-03 19:42:36 -08001095 }
1096
1097 /** @hide */
1098 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001099 public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
ludi5e623ea2017-05-12 09:15:00 -07001100 mContext = context;
Aaron Huang2617cf52021-11-29 16:31:32 +08001101 mDeps = Objects.requireNonNull(deps, "Missing dependencies.");
Benedict Wong083faee2017-12-03 19:42:36 -08001102 mUidFdTagger = uidFdTagger;
Aaron Huang2617cf52021-11-29 16:31:32 +08001103 try {
1104 mNetd = mDeps.getNetdInstance(mContext);
1105 } catch (RemoteException e) {
1106 throw e.rethrowFromSystemServer();
1107 }
ludi5e623ea2017-05-12 09:15:00 -07001108 }
1109
Nathan Harold19b99d92017-08-23 13:46:33 -07001110 /**
1111 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1112 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1113 */
1114 private static void checkInetAddress(String inetAddress) {
1115 if (TextUtils.isEmpty(inetAddress)) {
1116 throw new IllegalArgumentException("Unspecified address");
1117 }
1118
Serik Beketayev7f507332020-12-06 22:31:23 -08001119 InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
Nathan Harold19b99d92017-08-23 13:46:33 -07001120
1121 if (checkAddr.isAnyLocalAddress()) {
1122 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1123 }
1124 }
1125
1126 /**
1127 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1128 * DIRECTION_IN or DIRECTION_OUT
1129 */
Benedict Wong908d34e2021-04-15 11:59:16 -07001130 private void checkDirection(int direction) {
Nathan Harold19b99d92017-08-23 13:46:33 -07001131 switch (direction) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001132 case IpSecManager.DIRECTION_OUT:
1133 case IpSecManager.DIRECTION_IN:
Nathan Harold19b99d92017-08-23 13:46:33 -07001134 return;
Benedict Wong908d34e2021-04-15 11:59:16 -07001135 case IpSecManager.DIRECTION_FWD:
Benedict Wong47b528c2021-05-10 18:26:02 -07001136 // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
Benedict Wong908d34e2021-04-15 11:59:16 -07001137 PermissionUtils.enforceNetworkStackPermission(mContext);
1138 return;
Nathan Harold19b99d92017-08-23 13:46:33 -07001139 }
1140 throw new IllegalArgumentException("Invalid Direction: " + direction);
1141 }
1142
Nathan Harold031acb82017-03-07 13:23:36 -08001143 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri20e96c52017-11-16 10:58:01 -08001144 @Override
1145 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harold5a19b952018-01-05 19:25:13 -08001146 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1147 checkInetAddress(destinationAddress);
Nathan Harold1b88f0e2018-03-28 08:52:51 -07001148 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1149 if (requestedSpi > 0 && requestedSpi < 256) {
1150 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1151 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001152 Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harold19b99d92017-08-23 13:46:33 -07001153
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001154 int callingUid = Binder.getCallingUid();
1155 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001156 final int resourceId = mNextResourceId++;
Nathan Harold031acb82017-03-07 13:23:36 -08001157
1158 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -08001159 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001160 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001161 return new IpSecSpiResponse(
Nathan Harold19b99d92017-08-23 13:46:33 -07001162 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold6e4681c2017-04-24 16:16:34 -07001163 }
Nathan Harold5a19b952018-01-05 19:25:13 -08001164
Aaron Huang2617cf52021-11-29 16:31:32 +08001165 spi = mNetd.ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold031acb82017-03-07 13:23:36 -08001166 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong6855aee2017-11-16 15:27:22 -08001167 userRecord.mSpiRecords.put(
Nathan Harold80865392017-04-04 19:37:48 -07001168 resourceId,
Benedict Wong6855aee2017-11-16 15:27:22 -08001169 new RefcountedResource<SpiRecord>(
Aaron Huang2617cf52021-11-29 16:31:32 +08001170 new SpiRecord(resourceId, "",
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001171 destinationAddress, spi), binder));
Nathan Harold031acb82017-03-07 13:23:36 -08001172 } catch (ServiceSpecificException e) {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001173 if (e.errorCode == OsConstants.ENOENT) {
1174 return new IpSecSpiResponse(
1175 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1176 }
1177 throw e;
Nathan Harold031acb82017-03-07 13:23:36 -08001178 } catch (RemoteException e) {
1179 throw e.rethrowFromSystemServer();
1180 }
Nathan Harold80865392017-04-04 19:37:48 -07001181 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1182 }
1183
1184 /* This method should only be called from Binder threads. Do not call this from
1185 * within the system server as it will crash the system on failure.
1186 */
Benedict Wong6855aee2017-11-16 15:27:22 -08001187 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold80865392017-04-04 19:37:48 -07001188 throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001189 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold031acb82017-03-07 13:23:36 -08001190 }
1191
1192 /** Release a previously allocated SPI that has been registered with the system server */
1193 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001194 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1195 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1196 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001197 }
1198
1199 /**
1200 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1201 * be unbound.
1202 *
1203 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1204 * a random open port and then bind by number, this function creates a temp socket, binds to a
1205 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1206 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1207 * FileHandle.
1208 *
1209 * <p>The loop in this function handles the inherent race window between un-binding to a port
1210 * and re-binding, during which the system could *technically* hand that port out to someone
1211 * else.
1212 */
Benedict Wongc423cc82017-10-10 20:44:28 -07001213 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold80865392017-04-04 19:37:48 -07001214 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1215 try {
1216 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1217 Os.bind(probeSocket, INADDR_ANY, 0);
1218 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1219 Os.close(probeSocket);
1220 Log.v(TAG, "Binding to port " + port);
1221 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongc423cc82017-10-10 20:44:28 -07001222 return port;
Nathan Harold80865392017-04-04 19:37:48 -07001223 } catch (ErrnoException e) {
1224 // Someone miraculously claimed the port just after we closed probeSocket.
1225 if (e.errno == OsConstants.EADDRINUSE) {
1226 continue;
1227 }
1228 throw e.rethrowAsIOException();
1229 }
1230 }
1231 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1232 }
Nathan Harold031acb82017-03-07 13:23:36 -08001233
1234 /**
Benedict Wong083faee2017-12-03 19:42:36 -08001235 * Functional interface to do traffic tagging of given sockets to UIDs.
1236 *
1237 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1238 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1239 *
1240 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1241 * methods that cannot be easily mocked/tested.
1242 */
1243 @VisibleForTesting
1244 public interface UidFdTagger {
1245 /**
1246 * Sets socket tag to assign all traffic to the provided UID.
1247 *
1248 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1249 * should be accounted to the UID of the unprivileged application.
1250 */
Aaron Huangfbae3082021-12-06 15:18:42 +08001251 void tag(FileDescriptor fd, int uid) throws IOException;
Benedict Wong083faee2017-12-03 19:42:36 -08001252 }
1253
1254 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001255 * Open a socket via the system server and bind it to the specified port (random if port=0).
1256 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1257 * cache the socket and a record of its owner so that it can and must be freed when no longer
1258 * needed.
1259 */
1260 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001261 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1262 throws RemoteException {
1263 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1264 throw new IllegalArgumentException(
1265 "Specified port number must be a valid non-reserved UDP port");
1266 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001267 Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
Nathan Harold19b99d92017-08-23 13:46:33 -07001268
Benedict Wong083faee2017-12-03 19:42:36 -08001269 int callingUid = Binder.getCallingUid();
1270 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001271 final int resourceId = mNextResourceId++;
Aaron Huangb01254f2021-12-23 10:47:05 +08001272
1273 ParcelFileDescriptor pFd = null;
Nathan Harold80865392017-04-04 19:37:48 -07001274 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001275 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001276 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1277 }
1278
Aaron Huangb01254f2021-12-23 10:47:05 +08001279 FileDescriptor sockFd = null;
1280 try {
1281 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1282 pFd = ParcelFileDescriptor.dup(sockFd);
1283 } finally {
1284 IoUtils.closeQuietly(sockFd);
1285 }
Nathan Harold80865392017-04-04 19:37:48 -07001286
Aaron Huangb01254f2021-12-23 10:47:05 +08001287 mUidFdTagger.tag(pFd.getFileDescriptor(), callingUid);
Nathan Harold80865392017-04-04 19:37:48 -07001288 // This code is common to both the unspecified and specified port cases
1289 Os.setsockoptInt(
Aaron Huangb01254f2021-12-23 10:47:05 +08001290 pFd.getFileDescriptor(),
Nathan Harold80865392017-04-04 19:37:48 -07001291 OsConstants.IPPROTO_UDP,
1292 OsConstants.UDP_ENCAP,
1293 OsConstants.UDP_ENCAP_ESPINUDP);
1294
Aaron Huangb01254f2021-12-23 10:47:05 +08001295 mNetd.ipSecSetEncapSocketOwner(pFd, callingUid);
Benedict Wong17687442017-12-06 21:56:35 -08001296 if (port != 0) {
1297 Log.v(TAG, "Binding to port " + port);
Aaron Huangb01254f2021-12-23 10:47:05 +08001298 Os.bind(pFd.getFileDescriptor(), INADDR_ANY, port);
Benedict Wong17687442017-12-06 21:56:35 -08001299 } else {
Aaron Huangb01254f2021-12-23 10:47:05 +08001300 port = bindToRandomPort(pFd.getFileDescriptor());
Benedict Wong17687442017-12-06 21:56:35 -08001301 }
1302
Benedict Wong6855aee2017-11-16 15:27:22 -08001303 userRecord.mEncapSocketRecords.put(
1304 resourceId,
1305 new RefcountedResource<EncapSocketRecord>(
Aaron Huangb01254f2021-12-23 10:47:05 +08001306 new EncapSocketRecord(resourceId, pFd.getFileDescriptor(), port),
1307 binder));
1308 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port,
1309 pFd.getFileDescriptor());
Nathan Harold80865392017-04-04 19:37:48 -07001310 } catch (IOException | ErrnoException e) {
Aaron Huangb01254f2021-12-23 10:47:05 +08001311 try {
1312 if (pFd != null) {
1313 pFd.close();
1314 }
1315 } catch (IOException ex) {
1316 // Nothing can be done at this point
1317 Log.e(TAG, "Failed to close pFd.");
1318 }
Nathan Harold80865392017-04-04 19:37:48 -07001319 }
1320 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1321 // The only reasonable condition that would cause that is resource unavailable.
1322 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold031acb82017-03-07 13:23:36 -08001323 }
1324
1325 /** close a socket that has been been allocated by and registered with the system server */
1326 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001327 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1328 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1329 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001330 }
Nathan Harold031acb82017-03-07 13:23:36 -08001331
Benedict Wong8bc90732018-01-18 18:31:45 -08001332 /**
1333 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1334 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1335 * needed.
1336 */
1337 @Override
1338 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001339 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1340 String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001341 enforceTunnelFeatureAndPermissions(callingPackage);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001342 Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
1343 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
Benedict Wong8bc90732018-01-18 18:31:45 -08001344 checkInetAddress(localAddr);
1345 checkInetAddress(remoteAddr);
1346
1347 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1348 // network (b/72316676).
1349
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001350 int callerUid = Binder.getCallingUid();
1351 UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001352 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1353 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1354 }
1355
1356 final int resourceId = mNextResourceId++;
1357 final int ikey = reserveNetId();
1358 final int okey = reserveNetId();
Nathan Harold7be7f452018-04-26 11:47:14 -07001359 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001360
Benedict Wong8edc5572018-01-19 17:36:02 -08001361 try {
1362 // Calls to netd:
1363 // Create VTI
1364 // Add inbound/outbound global policies
1365 // (use reqid = 0)
Aaron Huang2617cf52021-11-29 16:31:32 +08001366 mNetd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001367
paulhu00e34562021-10-26 09:00:50 +00001368 BinderUtils.withCleanCallingIdentity(() -> {
Aaron Huang2617cf52021-11-29 16:31:32 +08001369 NetdUtils.setInterfaceUp(mNetd, intfName);
Benedict Wong529e8aa2020-02-11 23:49:36 -08001370 });
1371
Benedict Wong38e52972018-05-07 20:06:44 -07001372 for (int selAddrFamily : ADDRESS_FAMILIES) {
1373 // Always send down correct local/remote addresses for template.
Aaron Huang2617cf52021-11-29 16:31:32 +08001374 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001375 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001376 selAddrFamily,
1377 IpSecManager.DIRECTION_OUT,
1378 localAddr,
1379 remoteAddr,
1380 0,
1381 okey,
Benedict Wong5d749842018-09-06 11:31:25 -07001382 0xffffffff,
1383 resourceId);
Aaron Huang2617cf52021-11-29 16:31:32 +08001384 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001385 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001386 selAddrFamily,
1387 IpSecManager.DIRECTION_IN,
1388 remoteAddr,
1389 localAddr,
1390 0,
1391 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001392 0xffffffff,
1393 resourceId);
Benedict Wong47b528c2021-05-10 18:26:02 -07001394
1395 // Add a forwarding policy on the tunnel interface. In order to support forwarding
1396 // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
1397 //
1398 // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
1399 // forwarding will be blocked by default (as would be the case if this policy was
1400 // absent).
1401 //
1402 // This is necessary only on the tunnel interface, and not any the interface to
1403 // which traffic will be forwarded to.
Aaron Huang2617cf52021-11-29 16:31:32 +08001404 mNetd.ipSecAddSecurityPolicy(
Benedict Wong908d34e2021-04-15 11:59:16 -07001405 callerUid,
1406 selAddrFamily,
1407 IpSecManager.DIRECTION_FWD,
1408 remoteAddr,
1409 localAddr,
1410 0,
1411 ikey,
1412 0xffffffff,
1413 resourceId);
Benedict Wong8edc5572018-01-19 17:36:02 -08001414 }
1415
1416 userRecord.mTunnelInterfaceRecords.put(
1417 resourceId,
1418 new RefcountedResource<TunnelInterfaceRecord>(
1419 new TunnelInterfaceRecord(
1420 resourceId,
1421 intfName,
1422 underlyingNetwork,
1423 localAddr,
1424 remoteAddr,
1425 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001426 okey,
1427 resourceId),
Benedict Wong8edc5572018-01-19 17:36:02 -08001428 binder));
1429 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1430 } catch (RemoteException e) {
1431 // Release keys if we got an error.
1432 releaseNetId(ikey);
1433 releaseNetId(okey);
1434 throw e.rethrowFromSystemServer();
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001435 } catch (Throwable t) {
1436 // Release keys if we got an error.
1437 releaseNetId(ikey);
1438 releaseNetId(okey);
1439 throw t;
Benedict Wong8edc5572018-01-19 17:36:02 -08001440 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001441 }
1442
1443 /**
1444 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1445 * from multiple local IP addresses over the same tunnel.
1446 */
1447 @Override
Benedict Wong97c3c942018-03-01 18:53:07 -08001448 public synchronized void addAddressToTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001449 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001450 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001451 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1452
1453 // Get tunnelInterface record; if no such interface is found, will throw
1454 // IllegalArgumentException
1455 TunnelInterfaceRecord tunnelInterfaceInfo =
1456 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1457
Benedict Wong97c3c942018-03-01 18:53:07 -08001458 try {
1459 // We can assume general validity of the IP address, since we get them as a
1460 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001461 mNetd.interfaceAddAddress(
1462 tunnelInterfaceInfo.mInterfaceName,
1463 localAddr.getAddress().getHostAddress(),
1464 localAddr.getPrefixLength());
Benedict Wong97c3c942018-03-01 18:53:07 -08001465 } catch (RemoteException e) {
1466 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001467 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001468 }
1469
1470 /**
1471 * Remove a new local address from the tunnel interface. After removal, the address will no
1472 * longer be available to send from, or receive on.
1473 */
1474 @Override
1475 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001476 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001477 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001478
Nathan Harold65ef8432018-03-15 18:06:06 -07001479 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8bc90732018-01-18 18:31:45 -08001480 // Get tunnelInterface record; if no such interface is found, will throw
1481 // IllegalArgumentException
1482 TunnelInterfaceRecord tunnelInterfaceInfo =
1483 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1484
Benedict Wong97c3c942018-03-01 18:53:07 -08001485 try {
1486 // We can assume general validity of the IP address, since we get them as a
1487 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001488 mNetd.interfaceDelAddress(
Benedict Wong97c3c942018-03-01 18:53:07 -08001489 tunnelInterfaceInfo.mInterfaceName,
1490 localAddr.getAddress().getHostAddress(),
1491 localAddr.getPrefixLength());
1492 } catch (RemoteException e) {
1493 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001494 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001495 }
1496
Yan Yana2f3b492020-09-29 23:38:00 -07001497 /** Set TunnelInterface to use a specific underlying network. */
1498 @Override
1499 public synchronized void setNetworkForTunnelInterface(
1500 int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
1501 enforceTunnelFeatureAndPermissions(callingPackage);
1502 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1503
1504 final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1505
1506 // Get tunnelInterface record; if no such interface is found, will throw
1507 // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
1508 final TunnelInterfaceRecord tunnelInterfaceInfo =
1509 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1510
1511 final ConnectivityManager connectivityManager =
1512 mContext.getSystemService(ConnectivityManager.class);
1513 final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
Yan Yanbe3eb3d2022-05-16 17:13:45 -07001514 if (lp == null) {
1515 throw new IllegalArgumentException(
1516 "LinkProperties is null. The underlyingNetwork may not be functional");
1517 }
1518
Yan Yana2f3b492020-09-29 23:38:00 -07001519 if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
1520 throw new IllegalArgumentException(
1521 "Underlying network cannot be the network being exposed by this tunnel");
1522 }
1523
1524 // It is meaningless to check if the network exists or is valid because the network might
1525 // disconnect at any time after it passes the check.
1526
1527 tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
1528 }
1529
Benedict Wong8bc90732018-01-18 18:31:45 -08001530 /**
1531 * Delete a TunnelInterface that has been been allocated by and registered with the system
1532 * server
1533 */
1534 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001535 public synchronized void deleteTunnelInterface(
1536 int resourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001537 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001538 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1539 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1540 }
1541
Benedict Wong70867e52017-11-06 20:49:10 -08001542 @VisibleForTesting
Nathan Harold5a19b952018-01-05 19:25:13 -08001543 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1544 IpSecAlgorithm auth = config.getAuthentication();
1545 IpSecAlgorithm crypt = config.getEncryption();
1546 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong70867e52017-11-06 20:49:10 -08001547
Nathan Harold5a19b952018-01-05 19:25:13 -08001548 // Validate the algorithm set
1549 Preconditions.checkArgument(
1550 aead != null || crypt != null || auth != null,
1551 "No Encryption or Authentication algorithms specified");
1552 Preconditions.checkArgument(
1553 auth == null || auth.isAuthentication(),
1554 "Unsupported algorithm for Authentication");
1555 Preconditions.checkArgument(
Benedict Wong70867e52017-11-06 20:49:10 -08001556 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harold5a19b952018-01-05 19:25:13 -08001557 Preconditions.checkArgument(
1558 aead == null || aead.isAead(),
1559 "Unsupported algorithm for Authenticated Encryption");
1560 Preconditions.checkArgument(
1561 aead == null || (auth == null && crypt == null),
1562 "Authenticated Encryption is mutually exclusive with other Authentication "
1563 + "or Encryption algorithms");
Benedict Wong70867e52017-11-06 20:49:10 -08001564 }
1565
evitayan43d93a02018-03-22 17:53:08 -07001566 private int getFamily(String inetAddress) {
1567 int family = AF_UNSPEC;
Serik Beketayev7f507332020-12-06 22:31:23 -08001568 InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
evitayan43d93a02018-03-22 17:53:08 -07001569 if (checkAddress instanceof Inet4Address) {
1570 family = AF_INET;
1571 } else if (checkAddress instanceof Inet6Address) {
1572 family = AF_INET6;
1573 }
1574 return family;
1575 }
1576
Nathan Harold031acb82017-03-07 13:23:36 -08001577 /**
Chiachang Wang2fea4a72020-08-12 12:23:59 +08001578 * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
Nathan Harold19b99d92017-08-23 13:46:33 -07001579 * IllegalArgumentException if they are not.
1580 */
1581 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001582 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1583
Nathan Harold19b99d92017-08-23 13:46:33 -07001584 switch (config.getEncapType()) {
1585 case IpSecTransform.ENCAP_NONE:
1586 break;
1587 case IpSecTransform.ENCAP_ESPINUDP:
1588 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong6855aee2017-11-16 15:27:22 -08001589 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1590 userRecord.mEncapSocketRecords.getResourceOrThrow(
1591 config.getEncapSocketResourceId());
Nathan Harold19b99d92017-08-23 13:46:33 -07001592
1593 int port = config.getEncapRemotePort();
1594 if (port <= 0 || port > 0xFFFF) {
1595 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1596 }
1597 break;
1598 default:
1599 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1600 }
1601
Nathan Harold5a19b952018-01-05 19:25:13 -08001602 validateAlgorithms(config);
Nathan Harold19b99d92017-08-23 13:46:33 -07001603
Nathan Harold5a19b952018-01-05 19:25:13 -08001604 // Retrieve SPI record; will throw IllegalArgumentException if not found
1605 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1606
Benedict Wong68aac2a2017-12-13 18:26:40 -08001607 // Check to ensure that SPI has not already been used.
1608 if (s.getOwnedByTransform()) {
1609 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1610 }
1611
Nathan Harold5a19b952018-01-05 19:25:13 -08001612 // If no remote address is supplied, then use one from the SPI.
1613 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1614 config.setDestinationAddress(s.getDestinationAddress());
1615 }
1616
1617 // All remote addresses must match
1618 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1619 throw new IllegalArgumentException("Mismatched remote addresseses.");
1620 }
1621
1622 // This check is technically redundant due to the chain of custody between the SPI and
1623 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1624 // the transform, this will prevent us from messing up.
1625 checkInetAddress(config.getDestinationAddress());
1626
1627 // Require a valid source address for all transforms.
1628 checkInetAddress(config.getSourceAddress());
1629
evitayan43d93a02018-03-22 17:53:08 -07001630 // Check to ensure source and destination have the same address family.
1631 String sourceAddress = config.getSourceAddress();
1632 String destinationAddress = config.getDestinationAddress();
1633 int sourceFamily = getFamily(sourceAddress);
1634 int destinationFamily = getFamily(destinationAddress);
1635 if (sourceFamily != destinationFamily) {
1636 throw new IllegalArgumentException(
1637 "Source address ("
1638 + sourceAddress
1639 + ") and destination address ("
1640 + destinationAddress
1641 + ") have different address families.");
1642 }
1643
1644 // Throw an error if UDP Encapsulation is not used in IPv4.
1645 if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
1646 throw new IllegalArgumentException(
1647 "UDP Encapsulation is not supported for this address family");
1648 }
1649
Nathan Harold5a19b952018-01-05 19:25:13 -08001650 switch (config.getMode()) {
1651 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold025aae12018-02-02 18:34:25 -08001652 break;
Nathan Harold5a19b952018-01-05 19:25:13 -08001653 case IpSecTransform.MODE_TUNNEL:
1654 break;
1655 default:
1656 throw new IllegalArgumentException(
1657 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harold19b99d92017-08-23 13:46:33 -07001658 }
Benedict Wong683441d2018-07-25 18:46:19 -07001659
1660 config.setMarkValue(0);
1661 config.setMarkMask(0);
Nathan Harold19b99d92017-08-23 13:46:33 -07001662 }
1663
Benedict Wong2b6a14e2018-09-13 16:45:12 -07001664 private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
Nathan Harold7c250ae2018-05-15 19:18:38 -07001665
Benedict Wonge9763752018-11-08 19:45:34 -08001666 private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1667 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1668 throw new UnsupportedOperationException(
1669 "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1670 }
1671
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001672 Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Benedict Wongc85b7b02019-11-12 22:31:51 -08001673
1674 // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
1675 // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
1676 // permission or is the System Server.
1677 if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
1678 TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1679 return;
Nathan Harold65ef8432018-03-15 18:06:06 -07001680 }
Benedict Wongc85b7b02019-11-12 22:31:51 -08001681 mContext.enforceCallingOrSelfPermission(
1682 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
Nathan Harold025aae12018-02-02 18:34:25 -08001683 }
1684
Yan Yana48dcd92022-10-18 00:03:20 +00001685 private void enforceMigrateFeature() {
1686 if (!mContext.getPackageManager().hasSystemFeature(FEATURE_IPSEC_TUNNEL_MIGRATION)) {
1687 throw new UnsupportedOperationException(
1688 "IPsec Tunnel migration requires"
1689 + " PackageManager.FEATURE_IPSEC_TUNNEL_MIGRATION");
1690 }
1691 }
1692
Benedict Wong8edc5572018-01-19 17:36:02 -08001693 private void createOrUpdateTransform(
1694 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1695 throws RemoteException {
1696
1697 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1698 if (encapType != IpSecTransform.ENCAP_NONE) {
1699 encapLocalPort = socketRecord.getPort();
1700 encapRemotePort = c.getEncapRemotePort();
1701 }
1702
1703 IpSecAlgorithm auth = c.getAuthentication();
1704 IpSecAlgorithm crypt = c.getEncryption();
1705 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1706
Benedict Wong778327e2018-03-15 19:41:41 -07001707 String cryptName;
1708 if (crypt == null) {
1709 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1710 } else {
1711 cryptName = crypt.getName();
1712 }
1713
Aaron Huang2617cf52021-11-29 16:31:32 +08001714 mNetd.ipSecAddSecurityAssociation(
1715 Binder.getCallingUid(),
1716 c.getMode(),
1717 c.getSourceAddress(),
1718 c.getDestinationAddress(),
1719 (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
1720 spiRecord.getSpi(),
1721 c.getMarkValue(),
1722 c.getMarkMask(),
1723 (auth != null) ? auth.getName() : "",
1724 (auth != null) ? auth.getKey() : new byte[] {},
1725 (auth != null) ? auth.getTruncationLengthBits() : 0,
1726 cryptName,
1727 (crypt != null) ? crypt.getKey() : new byte[] {},
1728 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1729 (authCrypt != null) ? authCrypt.getName() : "",
1730 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1731 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1732 encapType,
1733 encapLocalPort,
1734 encapRemotePort,
1735 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001736 }
1737
Nathan Harold19b99d92017-08-23 13:46:33 -07001738 /**
Benedict Wong8edc5572018-01-19 17:36:02 -08001739 * Create a IPsec transform, which represents a single security association in the kernel. The
1740 * transform will be cached by the system server and must be freed when no longer needed. It is
1741 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1742 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold031acb82017-03-07 13:23:36 -08001743 */
1744 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001745 public synchronized IpSecTransformResponse createTransform(
1746 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001747 Objects.requireNonNull(c);
Nathan Harold65ef8432018-03-15 18:06:06 -07001748 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
Benedict Wonge9763752018-11-08 19:45:34 -08001749 enforceTunnelFeatureAndPermissions(callingPackage);
Nathan Harold65ef8432018-03-15 18:06:06 -07001750 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001751 checkIpSecConfig(c);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001752 Objects.requireNonNull(binder, "Null Binder passed to createTransform");
Nathan Haroldfdafce22017-12-13 19:16:33 -08001753 final int resourceId = mNextResourceId++;
Benedict Wong6855aee2017-11-16 15:27:22 -08001754
1755 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wongcbd329b2017-12-13 17:16:53 -08001756 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong6855aee2017-11-16 15:27:22 -08001757
1758 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001759 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1760 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001761
Benedict Wong6855aee2017-11-16 15:27:22 -08001762 EncapSocketRecord socketRecord = null;
Benedict Wong8edc5572018-01-19 17:36:02 -08001763 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001764 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1765 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1766 c.getEncapSocketResourceId());
1767 dependencies.add(refcountedSocketRecord);
Benedict Wong6855aee2017-11-16 15:27:22 -08001768 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold80865392017-04-04 19:37:48 -07001769 }
1770
Nathan Harold5a19b952018-01-05 19:25:13 -08001771 RefcountedResource<SpiRecord> refcountedSpiRecord =
1772 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1773 dependencies.add(refcountedSpiRecord);
1774 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong6855aee2017-11-16 15:27:22 -08001775
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001776 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong8edc5572018-01-19 17:36:02 -08001777
1778 // SA was created successfully, time to construct a record and lock it away
Benedict Wong6855aee2017-11-16 15:27:22 -08001779 userRecord.mTransformRecords.put(
1780 resourceId,
1781 new RefcountedResource<TransformRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001782 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong6855aee2017-11-16 15:27:22 -08001783 binder,
1784 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold80865392017-04-04 19:37:48 -07001785 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001786 }
1787
1788 /**
Yan Yane114b382021-02-16 16:29:48 -08001789 * Migrate an active Tunnel Mode IPsec Transform to new source/destination addresses.
1790 *
1791 * <p>Begins the process of migrating a transform and cache the new addresses. To complete the
1792 * migration once started, callers MUST apply the same transform to the appropriate tunnel using
1793 * {@link #applyTunnelModeTransform}. Otherwise, the address update will not be committed and
1794 * the transform will still only process traffic between the current source and destination
1795 * address. One common use case is that the control plane will start the migration process and
1796 * then hand off the transform to the IPsec caller to perform the actual migration when the
1797 * tunnel is ready.
1798 *
1799 * <p>If this method is called multiple times before {@link #applyTunnelModeTransform} is
1800 * called, when the transform is applied, it will be migrated to the addresses from the last
1801 * call.
1802 *
1803 * <p>The provided source and destination addresses MUST share the same address family, but they
1804 * can have a different family from the current addresses.
1805 *
1806 * <p>Transform migration is only supported for tunnel mode transforms. Calling this method on
1807 * other types of transforms will throw an {@code UnsupportedOperationException}.
1808 */
1809 @Override
1810 public synchronized void migrateTransform(
1811 int transformId,
1812 String newSourceAddress,
1813 String newDestinationAddress,
1814 String callingPackage) {
1815 Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
1816 Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
1817
1818 enforceTunnelFeatureAndPermissions(callingPackage);
Yan Yana48dcd92022-10-18 00:03:20 +00001819 enforceMigrateFeature();
Yan Yane114b382021-02-16 16:29:48 -08001820
1821 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1822 TransformRecord transformInfo =
1823 userRecord.mTransformRecords.getResourceOrThrow(transformId);
1824 transformInfo.startMigration(newSourceAddress, newDestinationAddress);
1825 }
1826
1827 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001828 * Delete a transport mode transform that was previously allocated by + registered with the
1829 * system server. If this is called on an inactive (or non-existent) transform, it will not
1830 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1831 * other reasons.
1832 */
1833 @Override
Benedict Wong0fff56e2018-01-18 14:38:16 -08001834 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001835 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1836 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001837 }
1838
1839 /**
1840 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1841 * association as a correspondent policy to the provided socket
1842 */
1843 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001844 public synchronized void applyTransportModeTransform(
Nathan Harold5a19b952018-01-05 19:25:13 -08001845 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001846 int callingUid = Binder.getCallingUid();
1847 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harold5a19b952018-01-05 19:25:13 -08001848 checkDirection(direction);
Benedict Wong6855aee2017-11-16 15:27:22 -08001849 // Get transform record; if no transform is found, will throw IllegalArgumentException
1850 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001851
Nathan Harold80865392017-04-04 19:37:48 -07001852 // TODO: make this a function.
Aaron Huangfbae3082021-12-06 15:18:42 +08001853 if (info.mPid != getCallingPid() || info.mUid != callingUid) {
Nathan Harold80865392017-04-04 19:37:48 -07001854 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1855 }
1856
Benedict Wong8bc90732018-01-18 18:31:45 -08001857 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold80865392017-04-04 19:37:48 -07001858 IpSecConfig c = info.getConfig();
Benedict Wong8bc90732018-01-18 18:31:45 -08001859 Preconditions.checkArgument(
1860 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1861 "Transform mode was not Transport mode; cannot be applied to a socket");
1862
Aaron Huang2617cf52021-11-29 16:31:32 +08001863 mNetd.ipSecApplyTransportModeTransform(
1864 socket,
1865 callingUid,
1866 direction,
1867 c.getSourceAddress(),
1868 c.getDestinationAddress(),
1869 info.getSpiRecord().getSpi());
Nathan Harold031acb82017-03-07 13:23:36 -08001870 }
Nathan Harold80865392017-04-04 19:37:48 -07001871
Nathan Harold031acb82017-03-07 13:23:36 -08001872 /**
Nathan Harold5a19b952018-01-05 19:25:13 -08001873 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1874 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1875 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1876 * reserved for future improved input validation.
Nathan Harold031acb82017-03-07 13:23:36 -08001877 */
1878 @Override
Nathan Harold0d483b72018-01-17 01:00:20 -08001879 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1880 throws RemoteException {
Aaron Huang2617cf52021-11-29 16:31:32 +08001881 mNetd.ipSecRemoveTransportModeTransform(socket);
Nathan Harold031acb82017-03-07 13:23:36 -08001882 }
1883
Benedict Wong8bc90732018-01-18 18:31:45 -08001884 /**
1885 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
Yan Yane114b382021-02-16 16:29:48 -08001886 * security association as a correspondent policy to the provided interface.
1887 *
1888 * <p>If the transform is migrating, migrate the IPsec security association to new
1889 * source/destination addresses, and mark the migration as finished.
Benedict Wong8bc90732018-01-18 18:31:45 -08001890 */
1891 @Override
1892 public synchronized void applyTunnelModeTransform(
Yan Yane114b382021-02-16 16:29:48 -08001893 int tunnelResourceId, int direction, int transformResourceId, String callingPackage)
1894 throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001895 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001896 checkDirection(direction);
1897
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001898 int callingUid = Binder.getCallingUid();
1899 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001900
1901 // Get transform record; if no transform is found, will throw IllegalArgumentException
1902 TransformRecord transformInfo =
1903 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1904
1905 // Get tunnelInterface record; if no such interface is found, will throw
1906 // IllegalArgumentException
1907 TunnelInterfaceRecord tunnelInterfaceInfo =
1908 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1909
1910 // Get config and check that to-be-applied transform has the correct mode
1911 IpSecConfig c = transformInfo.getConfig();
1912 Preconditions.checkArgument(
1913 c.getMode() == IpSecTransform.MODE_TUNNEL,
1914 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1915
Benedict Wong8edc5572018-01-19 17:36:02 -08001916 EncapSocketRecord socketRecord = null;
1917 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1918 socketRecord =
1919 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1920 }
Benedict Wongec2e2e22019-10-03 11:09:00 -07001921 SpiRecord spiRecord = transformInfo.getSpiRecord();
Benedict Wong8edc5572018-01-19 17:36:02 -08001922
Benedict Wong8bc90732018-01-18 18:31:45 -08001923 int mark =
Benedict Wong38e52972018-05-07 20:06:44 -07001924 (direction == IpSecManager.DIRECTION_OUT)
1925 ? tunnelInterfaceInfo.getOkey()
Benedict Wong908d34e2021-04-15 11:59:16 -07001926 : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
Benedict Wong8bc90732018-01-18 18:31:45 -08001927
Benedict Wong8edc5572018-01-19 17:36:02 -08001928 try {
Benedict Wong5d749842018-09-06 11:31:25 -07001929 // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
1930 // SPI matching as part of the template resolution.
1931 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1932 c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
1933
Benedict Wong683441d2018-07-25 18:46:19 -07001934 // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
1935 // (and backporting) would allow us to narrow the mark space, and ensure that the SA
1936 // and SPs have matching marks (as VTI are meant to be built).
1937 // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
1938 // config matches the actual allocated resources in the kernel.
Benedict Wongc6fcedd2018-11-21 21:24:55 -08001939 // All SAs will have zero marks (from creation time), and any policy that matches the
1940 // same src/dst could match these SAs. Non-IpSecService governed processes that
1941 // establish floating policies with the same src/dst may result in undefined
1942 // behavior. This is generally limited to vendor code due to the permissions
1943 // (CAP_NET_ADMIN) required.
Benedict Wong683441d2018-07-25 18:46:19 -07001944 //
1945 // c.setMarkValue(mark);
1946 // c.setMarkMask(0xffffffff);
Benedict Wong8edc5572018-01-19 17:36:02 -08001947
1948 if (direction == IpSecManager.DIRECTION_OUT) {
1949 // Set output mark via underlying network (output only)
1950 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1951
Benedict Wong5d749842018-09-06 11:31:25 -07001952 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
1953 // but want to guarantee outbound packets are sent over the new SA.
Benedict Wongec2e2e22019-10-03 11:09:00 -07001954 spi = spiRecord.getSpi();
Benedict Wong5d749842018-09-06 11:31:25 -07001955 }
1956
1957 // Always update the policy with the relevant XFRM_IF_ID
1958 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +08001959 mNetd.ipSecUpdateSecurityPolicy(
1960 callingUid,
1961 selAddrFamily,
1962 direction,
1963 transformInfo.getConfig().getSourceAddress(),
1964 transformInfo.getConfig().getDestinationAddress(),
1965 spi, // If outbound, also add SPI to the policy.
1966 mark, // Must always set policy mark; ikey/okey for VTIs
1967 0xffffffff,
1968 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001969 }
1970
1971 // Update SA with tunnel mark (ikey or okey based on direction)
1972 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
Yan Yane114b382021-02-16 16:29:48 -08001973
1974 if (transformInfo.isMigrating()) {
Yan Yana48dcd92022-10-18 00:03:20 +00001975 if (!mContext.getPackageManager()
1976 .hasSystemFeature(FEATURE_IPSEC_TUNNEL_MIGRATION)) {
1977 Log.wtf(
1978 TAG,
1979 "Attempted to migrate a transform without"
1980 + " FEATURE_IPSEC_TUNNEL_MIGRATION");
1981 }
1982
Yan Yane114b382021-02-16 16:29:48 -08001983 for (int selAddrFamily : ADDRESS_FAMILIES) {
1984 final IpSecMigrateInfoParcel migrateInfo =
1985 new IpSecMigrateInfoParcel(
1986 Binder.getCallingUid(),
1987 selAddrFamily,
1988 direction,
1989 c.getSourceAddress(),
1990 c.getDestinationAddress(),
1991 transformInfo.getNewSourceAddress(),
1992 transformInfo.getNewDestinationAddress(),
1993 c.getXfrmInterfaceId());
1994
1995 mNetd.ipSecMigrate(migrateInfo);
1996 }
1997 transformInfo.finishMigration();
1998 }
Benedict Wong8edc5572018-01-19 17:36:02 -08001999 } catch (ServiceSpecificException e) {
2000 if (e.errorCode == EINVAL) {
2001 throw new IllegalArgumentException(e.toString());
2002 } else {
2003 throw e;
2004 }
2005 }
Benedict Wong8bc90732018-01-18 18:31:45 -08002006 }
2007
Nathan Harold031acb82017-03-07 13:23:36 -08002008 @Override
ludi89194d62017-05-22 10:52:23 -07002009 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08002010 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludi89194d62017-05-22 10:52:23 -07002011
2012 pw.println("IpSecService dump:");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08002013 pw.println();
ludi89194d62017-05-22 10:52:23 -07002014
Benedict Wong6855aee2017-11-16 15:27:22 -08002015 pw.println("mUserResourceTracker:");
2016 pw.println(mUserResourceTracker);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08002017 }
2018}