blob: 4bc40eae44048c841030fee726d9e6d5aa8ad266 [file] [log] [blame]
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static android.Manifest.permission.DUMP;
Nathan Harold031acb82017-03-07 13:23:36 -080020import static android.net.IpSecManager.INVALID_RESOURCE_ID;
Nathan Harold80865392017-04-04 19:37:48 -070021import static android.system.OsConstants.AF_INET;
evitayan43d93a02018-03-22 17:53:08 -070022import static android.system.OsConstants.AF_INET6;
23import static android.system.OsConstants.AF_UNSPEC;
manojboopathiac927fe2017-11-30 17:11:49 -080024import static android.system.OsConstants.EINVAL;
Nathan Harold80865392017-04-04 19:37:48 -070025import static android.system.OsConstants.IPPROTO_UDP;
26import static android.system.OsConstants.SOCK_DGRAM;
Benedict Wong683441d2018-07-25 18:46:19 -070027
Nathan Harold65ef8432018-03-15 18:06:06 -070028import android.annotation.NonNull;
29import android.app.AppOpsManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080030import android.content.Context;
Benedict Wong4aac3e92019-02-25 12:33:22 -080031import android.content.pm.PackageManager;
Yan Yana2f3b492020-09-29 23:38:00 -070032import android.net.ConnectivityManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080033import android.net.IIpSecService;
34import android.net.INetd;
Serik Beketayev7f507332020-12-06 22:31:23 -080035import android.net.InetAddresses;
Nathan Harold031acb82017-03-07 13:23:36 -080036import android.net.IpSecAlgorithm;
37import android.net.IpSecConfig;
38import android.net.IpSecManager;
Nathan Harold80865392017-04-04 19:37:48 -070039import android.net.IpSecSpiResponse;
Nathan Harold031acb82017-03-07 13:23:36 -080040import android.net.IpSecTransform;
Nathan Harold80865392017-04-04 19:37:48 -070041import android.net.IpSecTransformResponse;
Benedict Wong8bc90732018-01-18 18:31:45 -080042import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold80865392017-04-04 19:37:48 -070043import android.net.IpSecUdpEncapResponse;
Benedict Wong97c3c942018-03-01 18:53:07 -080044import android.net.LinkAddress;
Yan Yana2f3b492020-09-29 23:38:00 -070045import android.net.LinkProperties;
Benedict Wong8bc90732018-01-18 18:31:45 -080046import android.net.Network;
Benedict Wong083faee2017-12-03 19:42:36 -080047import android.net.TrafficStats;
Nathan Harold031acb82017-03-07 13:23:36 -080048import android.os.Binder;
Nathan Harold031acb82017-03-07 13:23:36 -080049import android.os.IBinder;
50import android.os.ParcelFileDescriptor;
Benedict Wong908d34e2021-04-15 11:59:16 -070051import android.os.Process;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080052import android.os.RemoteException;
Nathan Harold031acb82017-03-07 13:23:36 -080053import android.os.ServiceSpecificException;
Nathan Harold80865392017-04-04 19:37:48 -070054import android.system.ErrnoException;
55import android.system.Os;
56import android.system.OsConstants;
Nathan Harold19b99d92017-08-23 13:46:33 -070057import android.text.TextUtils;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080058import android.util.Log;
lucaslin7eb76592021-03-11 17:39:49 +080059import android.util.Range;
Nathan Harold031acb82017-03-07 13:23:36 -080060import android.util.SparseArray;
Benedict Wong8bc90732018-01-18 18:31:45 -080061import android.util.SparseBooleanArray;
Nathan Harold19b99d92017-08-23 13:46:33 -070062
Nathan Harold031acb82017-03-07 13:23:36 -080063import com.android.internal.annotations.GuardedBy;
ludi5e623ea2017-05-12 09:15:00 -070064import com.android.internal.annotations.VisibleForTesting;
Benedict Wong70867e52017-11-06 20:49:10 -080065import com.android.internal.util.Preconditions;
paulhu00e34562021-10-26 09:00:50 +000066import com.android.net.module.util.BinderUtils;
lucaslinff6fe7b2021-02-03 23:59:45 +080067import com.android.net.module.util.NetdUtils;
Benedict Wong908d34e2021-04-15 11:59:16 -070068import com.android.net.module.util.PermissionUtils;
Nathan Harold19b99d92017-08-23 13:46:33 -070069
Benedict Wong683441d2018-07-25 18:46:19 -070070import libcore.io.IoUtils;
71
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080072import java.io.FileDescriptor;
Nathan Harold80865392017-04-04 19:37:48 -070073import java.io.IOException;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080074import java.io.PrintWriter;
evitayan43d93a02018-03-22 17:53:08 -070075import java.net.Inet4Address;
76import java.net.Inet6Address;
Nathan Harold80865392017-04-04 19:37:48 -070077import java.net.InetAddress;
78import java.net.InetSocketAddress;
79import java.net.UnknownHostException;
Benedict Wong02346822017-10-26 19:41:43 -070080import java.util.ArrayList;
81import java.util.List;
Benedict Wong529e8aa2020-02-11 23:49:36 -080082import java.util.Objects;
Nathan Harold19b99d92017-08-23 13:46:33 -070083
Benedict Wong02346822017-10-26 19:41:43 -070084/**
85 * A service to manage multiple clients that want to access the IpSec API. The service is
86 * responsible for maintaining a list of clients and managing the resources (and related quotas)
87 * that each of them own.
88 *
89 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
90 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
91 * thread is ever running at a time.
92 *
93 * @hide
94 */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080095public class IpSecService extends IIpSecService.Stub {
96 private static final String TAG = "IpSecService";
97 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Benedict Wong38e52972018-05-07 20:06:44 -070098 private static final int[] ADDRESS_FAMILIES =
99 new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800100
ludi5e623ea2017-05-12 09:15:00 -0700101 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold80865392017-04-04 19:37:48 -0700102 private static final InetAddress INADDR_ANY;
103
Benedict Wong29c30772019-03-20 09:44:09 -0700104 @VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
105
Aaron Huang2617cf52021-11-29 16:31:32 +0800106 private final INetd mNetd;
107
Nathan Harold80865392017-04-04 19:37:48 -0700108 static {
109 try {
110 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
111 } catch (UnknownHostException e) {
112 throw new RuntimeException(e);
113 }
114 }
115
116 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
117 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
118
119 /* Binder context for this service */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800120 private final Context mContext;
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800121 private final Dependencies mDeps;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800122
Nathan Haroldfdafce22017-12-13 19:16:33 -0800123 /**
Nathan Harold5a19b952018-01-05 19:25:13 -0800124 * The next non-repeating global ID for tracking resources between users, this service, and
125 * kernel data structures. Accessing this variable is not thread safe, so it is only read or
126 * modified within blocks synchronized on IpSecService.this. We want to avoid -1
127 * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
Nathan Haroldfdafce22017-12-13 19:16:33 -0800128 */
129 @GuardedBy("IpSecService.this")
130 private int mNextResourceId = 1;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800131
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800132 /**
133 * Dependencies of IpSecService, for injection in tests.
134 */
135 @VisibleForTesting
136 public static class Dependencies {
137 /**
138 * Get a reference to INetd.
139 */
140 public INetd getNetdInstance(Context context) throws RemoteException {
141 final INetd netd = INetd.Stub.asInterface((IBinder)
142 context.getSystemService(Context.NETD_SERVICE));
143 if (netd == null) {
144 throw new RemoteException("Failed to Get Netd Instance");
145 }
146 return netd;
147 }
ludi5e623ea2017-05-12 09:15:00 -0700148 }
149
Benedict Wong083faee2017-12-03 19:42:36 -0800150 final UidFdTagger mUidFdTagger;
ludi5e623ea2017-05-12 09:15:00 -0700151
Benedict Wong02346822017-10-26 19:41:43 -0700152 /**
153 * Interface for user-reference and kernel-resource cleanup.
154 *
155 * <p>This interface must be implemented for a resource to be reference counted.
156 */
157 @VisibleForTesting
158 public interface IResource {
159 /**
160 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
161 * objects dependent on it.
162 *
163 * <p>Implementations of this method are expected to remove references to the IResource
164 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
165 * the resource is considered invalid for user access or allocation or use in other
166 * resources.
167 *
168 * <p>References to the IResource object may be held by other RefcountedResource objects,
Benedict Wongcbd329b2017-12-13 17:16:53 -0800169 * and as such, the underlying resources and quota may not be cleaned up.
Benedict Wong02346822017-10-26 19:41:43 -0700170 */
171 void invalidate() throws RemoteException;
172
173 /**
174 * Releases underlying resources and related quotas.
175 *
176 * <p>Implementations of this method are expected to remove all system resources that are
177 * tracked by the IResource object. Due to other RefcountedResource objects potentially
Benedict Wong6855aee2017-11-16 15:27:22 -0800178 * having references to the IResource object, freeUnderlyingResources may not always be
Benedict Wong02346822017-10-26 19:41:43 -0700179 * called from releaseIfUnreferencedRecursively().
180 */
181 void freeUnderlyingResources() throws RemoteException;
182 }
183
184 /**
185 * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
186 *
187 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
188 * RefcountedResource object creates an explicit reference that must be freed by calling
189 * userRelease(). Additionally, adding this object as a child of another RefcountedResource
190 * object will add an implicit reference.
191 *
192 * <p>Resources are cleaned up when all references, both implicit and explicit, are released
193 * (ie, when userRelease() is called and when all parents have called releaseReference() on this
194 * object.)
195 */
196 @VisibleForTesting
197 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000198 private final T mResource;
199 private final List<RefcountedResource> mChildren;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800200 int mRefCount = 1; // starts at 1 for user's reference.
Junyu Lai75eabfe2019-04-26 01:38:04 +0000201 IBinder mBinder;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800202
Junyu Lai75eabfe2019-04-26 01:38:04 +0000203 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
Benedict Wong02346822017-10-26 19:41:43 -0700204 synchronized (IpSecService.this) {
205 this.mResource = resource;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000206 this.mChildren = new ArrayList<>(children.length);
Benedict Wong02346822017-10-26 19:41:43 -0700207 this.mBinder = binder;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000208
Benedict Wong02346822017-10-26 19:41:43 -0700209 for (RefcountedResource child : children) {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000210 mChildren.add(child);
Benedict Wong02346822017-10-26 19:41:43 -0700211 child.mRefCount++;
212 }
213
214 try {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000215 mBinder.linkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700216 } catch (RemoteException e) {
217 binderDied();
Benedict Wong04738f52019-02-28 20:28:48 -0800218 e.rethrowFromSystemServer();
Benedict Wong02346822017-10-26 19:41:43 -0700219 }
220 }
221 }
222
223 /**
224 * If the Binder object dies, this function is called to free the system resources that are
Benedict Wong6855aee2017-11-16 15:27:22 -0800225 * being tracked by this record and to subsequently release this record for garbage
Benedict Wong02346822017-10-26 19:41:43 -0700226 * collection
227 */
228 @Override
229 public void binderDied() {
230 synchronized (IpSecService.this) {
231 try {
232 userRelease();
233 } catch (Exception e) {
234 Log.e(TAG, "Failed to release resource: " + e);
235 }
236 }
237 }
238
239 public T getResource() {
240 return mResource;
241 }
242
243 /**
244 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
245 * arrays)
246 *
247 * <p>If this method has been previously called, the RefcountedResource's binder field will
248 * be null, and the method will return without performing the cleanup a second time.
249 *
250 * <p>Note that calling this function does not imply that kernel resources will be freed at
251 * this time, or that the related quota will be returned. Such actions will only be
252 * performed upon the reference count reaching zero.
253 */
254 @GuardedBy("IpSecService.this")
255 public void userRelease() throws RemoteException {
256 // Prevent users from putting reference counts into a bad state by calling
257 // userRelease() multiple times.
258 if (mBinder == null) {
259 return;
260 }
261
Junyu Lai75eabfe2019-04-26 01:38:04 +0000262 mBinder.unlinkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700263 mBinder = null;
264
265 mResource.invalidate();
Junyu Lai75eabfe2019-04-26 01:38:04 +0000266
Benedict Wong02346822017-10-26 19:41:43 -0700267 releaseReference();
268 }
269
270 /**
271 * Removes a reference to this resource. If the resultant reference count is zero, the
272 * underlying resources are freed, and references to all child resources are also dropped
273 * recursively (resulting in them freeing their resources and children, etcetera)
274 *
275 * <p>This method also sets the reference count to an invalid value (-1) to signify that it
276 * has been fully released. Any subsequent calls to this method will result in an
277 * IllegalStateException being thrown due to resource already having been previously
278 * released
279 */
280 @VisibleForTesting
281 @GuardedBy("IpSecService.this")
282 public void releaseReference() throws RemoteException {
283 mRefCount--;
284
285 if (mRefCount > 0) {
286 return;
287 } else if (mRefCount < 0) {
288 throw new IllegalStateException(
289 "Invalid operation - resource has already been released.");
290 }
291
292 // Cleanup own resources
293 mResource.freeUnderlyingResources();
294
295 // Cleanup child resources as needed
296 for (RefcountedResource<? extends IResource> child : mChildren) {
297 child.releaseReference();
298 }
299
300 // Enforce that resource cleanup can only be called once
301 // By decrementing the refcount (from 0 to -1), the next call will throw an
302 // IllegalStateException - it has already been released fully.
303 mRefCount--;
304 }
305
306 @Override
307 public String toString() {
308 return new StringBuilder()
309 .append("{mResource=")
310 .append(mResource)
311 .append(", mRefCount=")
312 .append(mRefCount)
313 .append(", mChildren=")
314 .append(mChildren)
315 .append("}")
316 .toString();
317 }
318 }
319
Benedict Wongcbd329b2017-12-13 17:16:53 -0800320 /**
321 * Very simple counting class that looks much like a counting semaphore
322 *
323 * <p>This class is not thread-safe, and expects that that users of this class will ensure
324 * synchronization and thread safety by holding the IpSecService.this instance lock.
325 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800326 @VisibleForTesting
327 static class ResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700328 private final int mMax;
329 int mCurrent;
330
331 ResourceTracker(int max) {
332 mMax = max;
333 mCurrent = 0;
334 }
335
Benedict Wong6855aee2017-11-16 15:27:22 -0800336 boolean isAvailable() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700337 return (mCurrent < mMax);
338 }
339
Benedict Wong6855aee2017-11-16 15:27:22 -0800340 void take() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700341 if (!isAvailable()) {
342 Log.wtf(TAG, "Too many resources allocated!");
343 }
344 mCurrent++;
345 }
346
Benedict Wong6855aee2017-11-16 15:27:22 -0800347 void give() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700348 if (mCurrent <= 0) {
349 Log.wtf(TAG, "We've released this resource too many times");
350 }
351 mCurrent--;
352 }
ludi529fdec2017-08-10 15:44:40 -0700353
354 @Override
355 public String toString() {
356 return new StringBuilder()
357 .append("{mCurrent=")
358 .append(mCurrent)
359 .append(", mMax=")
360 .append(mMax)
361 .append("}")
362 .toString();
363 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700364 }
365
Benedict Wong6855aee2017-11-16 15:27:22 -0800366 @VisibleForTesting
367 static final class UserRecord {
Benedict Wong6855aee2017-11-16 15:27:22 -0800368 /* Maximum number of each type of resource that a single UID may possess */
Benedict Wong7bcf9c22020-02-11 23:36:42 -0800369
370 // Up to 4 active VPNs/IWLAN with potential soft handover.
371 public static final int MAX_NUM_TUNNEL_INTERFACES = 8;
372 public static final int MAX_NUM_ENCAP_SOCKETS = 16;
373
374 // SPIs and Transforms are both cheap, and are 1:1 correlated.
375 public static final int MAX_NUM_TRANSFORMS = 64;
376 public static final int MAX_NUM_SPIS = 64;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700377
Benedict Wongcbd329b2017-12-13 17:16:53 -0800378 /**
379 * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
380 * and explicit (user) reference management.
381 *
382 * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
383 *
384 * <p>Resources are removed from this array when the user releases their explicit reference
385 * by calling one of the releaseResource() methods.
386 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800387 final RefcountedResourceArray<SpiRecord> mSpiRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800388 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800389 final RefcountedResourceArray<TransformRecord> mTransformRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800390 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800391 final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800392 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
Benedict Wong8bc90732018-01-18 18:31:45 -0800393 final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
394 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
Benedict Wongcbd329b2017-12-13 17:16:53 -0800395
396 /**
397 * Trackers for quotas for each of the OwnedResource types.
398 *
399 * <p>These trackers are separate from the resource arrays, since they are incremented and
400 * decremented at different points in time. Specifically, quota is only returned upon final
401 * resource deallocation (after all explicit and implicit references are released). Note
402 * that it is possible that calls to releaseResource() will not return the used quota if
403 * there are other resources that depend on (are parents of) the resource being released.
404 */
405 final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
406 final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
Benedict Wong6855aee2017-11-16 15:27:22 -0800407 final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
Benedict Wong8bc90732018-01-18 18:31:45 -0800408 final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
Benedict Wong6855aee2017-11-16 15:27:22 -0800409
410 void removeSpiRecord(int resourceId) {
411 mSpiRecords.remove(resourceId);
Nathan Harold6e4681c2017-04-24 16:16:34 -0700412 }
413
Benedict Wong6855aee2017-11-16 15:27:22 -0800414 void removeTransformRecord(int resourceId) {
415 mTransformRecords.remove(resourceId);
416 }
417
Benedict Wong8bc90732018-01-18 18:31:45 -0800418 void removeTunnelInterfaceRecord(int resourceId) {
419 mTunnelInterfaceRecords.remove(resourceId);
420 }
421
Benedict Wong6855aee2017-11-16 15:27:22 -0800422 void removeEncapSocketRecord(int resourceId) {
423 mEncapSocketRecords.remove(resourceId);
424 }
425
426 @Override
427 public String toString() {
428 return new StringBuilder()
429 .append("{mSpiQuotaTracker=")
430 .append(mSpiQuotaTracker)
431 .append(", mTransformQuotaTracker=")
432 .append(mTransformQuotaTracker)
433 .append(", mSocketQuotaTracker=")
434 .append(mSocketQuotaTracker)
Benedict Wong76603702018-01-24 15:31:39 -0800435 .append(", mTunnelQuotaTracker=")
436 .append(mTunnelQuotaTracker)
Benedict Wong6855aee2017-11-16 15:27:22 -0800437 .append(", mSpiRecords=")
438 .append(mSpiRecords)
439 .append(", mTransformRecords=")
440 .append(mTransformRecords)
441 .append(", mEncapSocketRecords=")
442 .append(mEncapSocketRecords)
Benedict Wong76603702018-01-24 15:31:39 -0800443 .append(", mTunnelInterfaceRecords=")
444 .append(mTunnelInterfaceRecords)
Benedict Wong6855aee2017-11-16 15:27:22 -0800445 .append("}")
446 .toString();
447 }
448 }
449
Benedict Wongcbd329b2017-12-13 17:16:53 -0800450 /**
451 * This class is not thread-safe, and expects that that users of this class will ensure
452 * synchronization and thread safety by holding the IpSecService.this instance lock.
453 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800454 @VisibleForTesting
455 static final class UserResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700456 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
457
Benedict Wongcbd329b2017-12-13 17:16:53 -0800458 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
Benedict Wong6855aee2017-11-16 15:27:22 -0800459 public UserRecord getUserRecord(int uid) {
460 checkCallerUid(uid);
461
Nathan Harold6e4681c2017-04-24 16:16:34 -0700462 UserRecord r = mUserRecords.get(uid);
463 if (r == null) {
464 r = new UserRecord();
465 mUserRecords.put(uid, r);
466 }
467 return r;
468 }
ludi529fdec2017-08-10 15:44:40 -0700469
Benedict Wong6855aee2017-11-16 15:27:22 -0800470 /** Safety method; guards against access of other user's UserRecords */
471 private void checkCallerUid(int uid) {
Benedict Wong908d34e2021-04-15 11:59:16 -0700472 if (uid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800473 throw new SecurityException("Attempted access of unowned resources");
474 }
475 }
476
ludi529fdec2017-08-10 15:44:40 -0700477 @Override
478 public String toString() {
479 return mUserRecords.toString();
480 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700481 }
482
Benedict Wong6855aee2017-11-16 15:27:22 -0800483 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700484
Nathan Harold80865392017-04-04 19:37:48 -0700485 /**
Benedict Wongcbd329b2017-12-13 17:16:53 -0800486 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
Benedict Wong6855aee2017-11-16 15:27:22 -0800487 * resources. It relies on a provided resourceId that should uniquely identify the kernel
488 * resource. To use this class, the user should implement the invalidate() and
489 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
Benedict Wongcbd329b2017-12-13 17:16:53 -0800490 * tracking arrays and kernel resources, respectively.
491 *
492 * <p>This class associates kernel resources with the UID that owns and controls them.
Nathan Harold80865392017-04-04 19:37:48 -0700493 */
Benedict Wongcbd329b2017-12-13 17:16:53 -0800494 private abstract class OwnedResourceRecord implements IResource {
Aaron Huangfbae3082021-12-06 15:18:42 +0800495 final int mPid;
496 final int mUid;
Benedict Wong6855aee2017-11-16 15:27:22 -0800497 protected final int mResourceId;
Nathan Harold031acb82017-03-07 13:23:36 -0800498
Benedict Wongcbd329b2017-12-13 17:16:53 -0800499 OwnedResourceRecord(int resourceId) {
Nathan Harold031acb82017-03-07 13:23:36 -0800500 super();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700501 if (resourceId == INVALID_RESOURCE_ID) {
502 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
503 }
Nathan Harold80865392017-04-04 19:37:48 -0700504 mResourceId = resourceId;
Aaron Huangfbae3082021-12-06 15:18:42 +0800505 mPid = Binder.getCallingPid();
506 mUid = Binder.getCallingUid();
Nathan Harold031acb82017-03-07 13:23:36 -0800507
Nathan Harold6e4681c2017-04-24 16:16:34 -0700508 getResourceTracker().take();
Nathan Harold031acb82017-03-07 13:23:36 -0800509 }
510
Benedict Wong6855aee2017-11-16 15:27:22 -0800511 @Override
512 public abstract void invalidate() throws RemoteException;
513
514 /** Convenience method; retrieves the user resource record for the stored UID. */
515 protected UserRecord getUserRecord() {
Aaron Huangfbae3082021-12-06 15:18:42 +0800516 return mUserResourceTracker.getUserRecord(mUid);
Nathan Harold80865392017-04-04 19:37:48 -0700517 }
518
Benedict Wong6855aee2017-11-16 15:27:22 -0800519 @Override
520 public abstract void freeUnderlyingResources() throws RemoteException;
ludi89194d62017-05-22 10:52:23 -0700521
Nathan Harold6e4681c2017-04-24 16:16:34 -0700522 /** Get the resource tracker for this resource */
523 protected abstract ResourceTracker getResourceTracker();
524
ludi89194d62017-05-22 10:52:23 -0700525 @Override
526 public String toString() {
527 return new StringBuilder()
528 .append("{mResourceId=")
529 .append(mResourceId)
530 .append(", pid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800531 .append(mPid)
ludi89194d62017-05-22 10:52:23 -0700532 .append(", uid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800533 .append(mUid)
ludi89194d62017-05-22 10:52:23 -0700534 .append("}")
535 .toString();
536 }
Nathan Harold031acb82017-03-07 13:23:36 -0800537 };
538
Nathan Harold80865392017-04-04 19:37:48 -0700539 /**
Benedict Wong6855aee2017-11-16 15:27:22 -0800540 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
541 *
542 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
543 * if a key is not found during a retrieval process.
Nathan Harold80865392017-04-04 19:37:48 -0700544 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800545 static class RefcountedResourceArray<T extends IResource> {
546 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
547 private final String mTypeName;
Nathan Harold031acb82017-03-07 13:23:36 -0800548
Aaron Huangfbae3082021-12-06 15:18:42 +0800549 RefcountedResourceArray(String typeName) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800550 this.mTypeName = typeName;
Nathan Harold80865392017-04-04 19:37:48 -0700551 }
552
Benedict Wong6855aee2017-11-16 15:27:22 -0800553 /**
554 * Accessor method to get inner resource object.
555 *
556 * @throws IllegalArgumentException if no resource with provided key is found.
557 */
558 T getResourceOrThrow(int key) {
559 return getRefcountedResourceOrThrow(key).getResource();
560 }
561
562 /**
563 * Accessor method to get reference counting wrapper.
564 *
565 * @throws IllegalArgumentException if no resource with provided key is found.
566 */
567 RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
568 RefcountedResource<T> resource = mArray.get(key);
569 if (resource == null) {
570 throw new IllegalArgumentException(
571 String.format("No such %s found for given id: %d", mTypeName, key));
572 }
573
574 return resource;
575 }
576
577 void put(int key, RefcountedResource<T> obj) {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +0000578 Objects.requireNonNull(obj, "Null resources cannot be added");
Nathan Harold80865392017-04-04 19:37:48 -0700579 mArray.put(key, obj);
580 }
581
582 void remove(int key) {
583 mArray.remove(key);
584 }
ludi89194d62017-05-22 10:52:23 -0700585
586 @Override
587 public String toString() {
588 return mArray.toString();
589 }
Nathan Harold80865392017-04-04 19:37:48 -0700590 }
591
Benedict Wongcbd329b2017-12-13 17:16:53 -0800592 /**
593 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
594 * created, the SpiRecord that originally tracked the SAs will reliquish the
595 * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
596 */
597 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700598 private final IpSecConfig mConfig;
Nathan Harold5a19b952018-01-05 19:25:13 -0800599 private final SpiRecord mSpi;
Benedict Wong6855aee2017-11-16 15:27:22 -0800600 private final EncapSocketRecord mSocket;
Nathan Harold80865392017-04-04 19:37:48 -0700601
602 TransformRecord(
Nathan Harold5a19b952018-01-05 19:25:13 -0800603 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800604 super(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -0800605 mConfig = config;
Nathan Harold5a19b952018-01-05 19:25:13 -0800606 mSpi = spi;
Nathan Harold80865392017-04-04 19:37:48 -0700607 mSocket = socket;
Benedict Wong68aac2a2017-12-13 18:26:40 -0800608
609 spi.setOwnedByTransform();
Nathan Harold031acb82017-03-07 13:23:36 -0800610 }
611
612 public IpSecConfig getConfig() {
613 return mConfig;
614 }
615
Nathan Harold5a19b952018-01-05 19:25:13 -0800616 public SpiRecord getSpiRecord() {
617 return mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700618 }
619
Benedict Wong8edc5572018-01-19 17:36:02 -0800620 public EncapSocketRecord getSocketRecord() {
621 return mSocket;
622 }
623
Nathan Harold80865392017-04-04 19:37:48 -0700624 /** always guarded by IpSecService#this */
Nathan Harold031acb82017-03-07 13:23:36 -0800625 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800626 public void freeUnderlyingResources() {
Nathan Harold5a19b952018-01-05 19:25:13 -0800627 int spi = mSpi.getSpi();
628 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800629 mNetd.ipSecDeleteSecurityAssociation(
630 mUid,
631 mConfig.getSourceAddress(),
632 mConfig.getDestinationAddress(),
633 spi,
634 mConfig.getMarkValue(),
635 mConfig.getMarkMask(),
636 mConfig.getXfrmInterfaceId());
Benedict Wong97c3c942018-03-01 18:53:07 -0800637 } catch (RemoteException | ServiceSpecificException e) {
638 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800639 }
Nathan Harold031acb82017-03-07 13:23:36 -0800640
Benedict Wong6855aee2017-11-16 15:27:22 -0800641 getResourceTracker().give();
Nathan Harold031acb82017-03-07 13:23:36 -0800642 }
ludi89194d62017-05-22 10:52:23 -0700643
Benedict Wong6855aee2017-11-16 15:27:22 -0800644 @Override
645 public void invalidate() throws RemoteException {
646 getUserRecord().removeTransformRecord(mResourceId);
647 }
648
649 @Override
Nathan Harold6e4681c2017-04-24 16:16:34 -0700650 protected ResourceTracker getResourceTracker() {
Benedict Wong6855aee2017-11-16 15:27:22 -0800651 return getUserRecord().mTransformQuotaTracker;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700652 }
653
ludi89194d62017-05-22 10:52:23 -0700654 @Override
655 public String toString() {
656 StringBuilder strBuilder = new StringBuilder();
657 strBuilder
658 .append("{super=")
659 .append(super.toString())
660 .append(", mSocket=")
661 .append(mSocket)
Nathan Harold5a19b952018-01-05 19:25:13 -0800662 .append(", mSpi.mResourceId=")
663 .append(mSpi.mResourceId)
ludi89194d62017-05-22 10:52:23 -0700664 .append(", mConfig=")
665 .append(mConfig)
666 .append("}");
667 return strBuilder.toString();
668 }
Nathan Harold031acb82017-03-07 13:23:36 -0800669 }
670
Benedict Wongcbd329b2017-12-13 17:16:53 -0800671 /**
672 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
673 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
674 * object
675 */
676 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harold5a19b952018-01-05 19:25:13 -0800677 private final String mSourceAddress;
678 private final String mDestinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800679 private int mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700680
681 private boolean mOwnedByTransform = false;
Nathan Harold031acb82017-03-07 13:23:36 -0800682
Aaron Huang2617cf52021-11-29 16:31:32 +0800683 SpiRecord(int resourceId, String sourceAddress,
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800684 String destinationAddress, int spi) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800685 super(resourceId);
Nathan Harold5a19b952018-01-05 19:25:13 -0800686 mSourceAddress = sourceAddress;
687 mDestinationAddress = destinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800688 mSpi = spi;
Nathan Harold031acb82017-03-07 13:23:36 -0800689 }
690
Nathan Harold80865392017-04-04 19:37:48 -0700691 /** always guarded by IpSecService#this */
692 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800693 public void freeUnderlyingResources() {
Nathan Harold031acb82017-03-07 13:23:36 -0800694 try {
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800695 if (!mOwnedByTransform) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800696 mNetd.ipSecDeleteSecurityAssociation(
697 mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
698 0 /* mask */, 0 /* if_id */);
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800699 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800700 } catch (ServiceSpecificException | RemoteException e) {
701 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800702 }
Nathan Harold80865392017-04-04 19:37:48 -0700703
704 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -0800705
Benedict Wong6855aee2017-11-16 15:27:22 -0800706 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700707 }
708
Nathan Harold80865392017-04-04 19:37:48 -0700709 public int getSpi() {
710 return mSpi;
711 }
712
Nathan Harold5a19b952018-01-05 19:25:13 -0800713 public String getDestinationAddress() {
714 return mDestinationAddress;
715 }
716
Nathan Harold80865392017-04-04 19:37:48 -0700717 public void setOwnedByTransform() {
718 if (mOwnedByTransform) {
719 // Programming error
Andreas Gampeafb01e22017-07-11 10:25:09 -0700720 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold80865392017-04-04 19:37:48 -0700721 }
722
723 mOwnedByTransform = true;
Nathan Harold031acb82017-03-07 13:23:36 -0800724 }
ludi89194d62017-05-22 10:52:23 -0700725
Benedict Wong68aac2a2017-12-13 18:26:40 -0800726 public boolean getOwnedByTransform() {
727 return mOwnedByTransform;
728 }
729
ludi89194d62017-05-22 10:52:23 -0700730 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800731 public void invalidate() throws RemoteException {
732 getUserRecord().removeSpiRecord(mResourceId);
733 }
734
735 @Override
736 protected ResourceTracker getResourceTracker() {
737 return getUserRecord().mSpiQuotaTracker;
738 }
739
740 @Override
ludi89194d62017-05-22 10:52:23 -0700741 public String toString() {
742 StringBuilder strBuilder = new StringBuilder();
743 strBuilder
744 .append("{super=")
745 .append(super.toString())
746 .append(", mSpi=")
747 .append(mSpi)
Nathan Harold5a19b952018-01-05 19:25:13 -0800748 .append(", mSourceAddress=")
749 .append(mSourceAddress)
750 .append(", mDestinationAddress=")
751 .append(mDestinationAddress)
ludi89194d62017-05-22 10:52:23 -0700752 .append(", mOwnedByTransform=")
753 .append(mOwnedByTransform)
754 .append("}");
755 return strBuilder.toString();
756 }
Nathan Harold031acb82017-03-07 13:23:36 -0800757 }
758
Benedict Wong8bc90732018-01-18 18:31:45 -0800759 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
lucaslin7eb76592021-03-11 17:39:49 +0800760 final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
761 private int mNextTunnelNetId = mNetIdRange.getLower();
Benedict Wong8bc90732018-01-18 18:31:45 -0800762
763 /**
764 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
765 *
766 * <p>This method should only be called from Binder threads. Do not call this from within the
767 * system server as it will crash the system on failure.
768 *
769 * @return an integer key within the netId range, if successful
770 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
771 */
772 @VisibleForTesting
773 int reserveNetId() {
lucaslin7eb76592021-03-11 17:39:49 +0800774 final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
Benedict Wong8bc90732018-01-18 18:31:45 -0800775 synchronized (mTunnelNetIds) {
lucaslin7eb76592021-03-11 17:39:49 +0800776 for (int i = 0; i < range; i++) {
777 final int netId = mNextTunnelNetId;
778 if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
779 mNextTunnelNetId = mNetIdRange.getLower();
780 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800781 if (!mTunnelNetIds.get(netId)) {
782 mTunnelNetIds.put(netId, true);
783 return netId;
784 }
785 }
786 }
787 throw new IllegalStateException("No free netIds to allocate");
788 }
789
790 @VisibleForTesting
791 void releaseNetId(int netId) {
792 synchronized (mTunnelNetIds) {
793 mTunnelNetIds.delete(netId);
794 }
795 }
796
Yan Yana2f3b492020-09-29 23:38:00 -0700797 /**
798 * Tracks an tunnel interface, and manages cleanup paths.
799 *
800 * <p>This class is not thread-safe, and expects that that users of this class will ensure
801 * synchronization and thread safety by holding the IpSecService.this instance lock
802 */
803 @VisibleForTesting
804 final class TunnelInterfaceRecord extends OwnedResourceRecord {
Benedict Wong8bc90732018-01-18 18:31:45 -0800805 private final String mInterfaceName;
Benedict Wong8bc90732018-01-18 18:31:45 -0800806
807 // outer addresses
808 private final String mLocalAddress;
809 private final String mRemoteAddress;
810
811 private final int mIkey;
812 private final int mOkey;
813
Benedict Wong5d749842018-09-06 11:31:25 -0700814 private final int mIfId;
815
Yan Yana2f3b492020-09-29 23:38:00 -0700816 private Network mUnderlyingNetwork;
817
Benedict Wong8bc90732018-01-18 18:31:45 -0800818 TunnelInterfaceRecord(
819 int resourceId,
820 String interfaceName,
821 Network underlyingNetwork,
822 String localAddr,
823 String remoteAddr,
824 int ikey,
Benedict Wong5d749842018-09-06 11:31:25 -0700825 int okey,
826 int intfId) {
Benedict Wong8bc90732018-01-18 18:31:45 -0800827 super(resourceId);
828
829 mInterfaceName = interfaceName;
830 mUnderlyingNetwork = underlyingNetwork;
831 mLocalAddress = localAddr;
832 mRemoteAddress = remoteAddr;
833 mIkey = ikey;
834 mOkey = okey;
Benedict Wong5d749842018-09-06 11:31:25 -0700835 mIfId = intfId;
Benedict Wong8bc90732018-01-18 18:31:45 -0800836 }
837
838 /** always guarded by IpSecService#this */
839 @Override
840 public void freeUnderlyingResources() {
Benedict Wong8edc5572018-01-19 17:36:02 -0800841 // Calls to netd
Benedict Wong8bc90732018-01-18 18:31:45 -0800842 // Teardown VTI
843 // Delete global policies
Benedict Wong8edc5572018-01-19 17:36:02 -0800844 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800845 mNetd.ipSecRemoveTunnelInterface(mInterfaceName);
Benedict Wong8edc5572018-01-19 17:36:02 -0800846
Benedict Wong38e52972018-05-07 20:06:44 -0700847 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800848 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800849 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700850 selAddrFamily,
851 IpSecManager.DIRECTION_OUT,
852 mOkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700853 0xffffffff,
854 mIfId);
Aaron Huang2617cf52021-11-29 16:31:32 +0800855 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800856 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700857 selAddrFamily,
858 IpSecManager.DIRECTION_IN,
859 mIkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700860 0xffffffff,
861 mIfId);
Benedict Wong8edc5572018-01-19 17:36:02 -0800862 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800863 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong8edc5572018-01-19 17:36:02 -0800864 Log.e(
865 TAG,
866 "Failed to delete VTI with interface name: "
867 + mInterfaceName
868 + " and id: "
Benedict Wong97c3c942018-03-01 18:53:07 -0800869 + mResourceId, e);
Benedict Wong8edc5572018-01-19 17:36:02 -0800870 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800871
872 getResourceTracker().give();
873 releaseNetId(mIkey);
874 releaseNetId(mOkey);
875 }
876
Yan Yana2f3b492020-09-29 23:38:00 -0700877 @GuardedBy("IpSecService.this")
878 public void setUnderlyingNetwork(Network underlyingNetwork) {
879 // When #applyTunnelModeTransform is called, this new underlying network will be used to
880 // update the output mark of the input transform.
881 mUnderlyingNetwork = underlyingNetwork;
Benedict Wong8bc90732018-01-18 18:31:45 -0800882 }
883
Yan Yana2f3b492020-09-29 23:38:00 -0700884 @GuardedBy("IpSecService.this")
Benedict Wong8bc90732018-01-18 18:31:45 -0800885 public Network getUnderlyingNetwork() {
886 return mUnderlyingNetwork;
887 }
888
Yan Yana2f3b492020-09-29 23:38:00 -0700889 public String getInterfaceName() {
890 return mInterfaceName;
891 }
892
Benedict Wong8bc90732018-01-18 18:31:45 -0800893 /** Returns the local, outer address for the tunnelInterface */
894 public String getLocalAddress() {
895 return mLocalAddress;
896 }
897
898 /** Returns the remote, outer address for the tunnelInterface */
899 public String getRemoteAddress() {
900 return mRemoteAddress;
901 }
902
903 public int getIkey() {
904 return mIkey;
905 }
906
907 public int getOkey() {
908 return mOkey;
909 }
910
Benedict Wong5d749842018-09-06 11:31:25 -0700911 public int getIfId() {
912 return mIfId;
913 }
914
Benedict Wong8bc90732018-01-18 18:31:45 -0800915 @Override
916 protected ResourceTracker getResourceTracker() {
917 return getUserRecord().mTunnelQuotaTracker;
918 }
919
920 @Override
921 public void invalidate() {
922 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
923 }
924
925 @Override
926 public String toString() {
927 return new StringBuilder()
928 .append("{super=")
929 .append(super.toString())
930 .append(", mInterfaceName=")
931 .append(mInterfaceName)
932 .append(", mUnderlyingNetwork=")
933 .append(mUnderlyingNetwork)
934 .append(", mLocalAddress=")
935 .append(mLocalAddress)
936 .append(", mRemoteAddress=")
937 .append(mRemoteAddress)
938 .append(", mIkey=")
939 .append(mIkey)
940 .append(", mOkey=")
941 .append(mOkey)
942 .append("}")
943 .toString();
944 }
945 }
946
Benedict Wongcbd329b2017-12-13 17:16:53 -0800947 /**
948 * Tracks a UDP encap socket, and manages cleanup paths
949 *
950 * <p>While this class does not manage non-kernel resources, race conditions around socket
951 * binding require that the service creates the encap socket, binds it and applies the socket
952 * policy before handing it to a user.
953 */
954 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700955 private FileDescriptor mSocket;
956 private final int mPort;
Nathan Harold031acb82017-03-07 13:23:36 -0800957
Benedict Wong6855aee2017-11-16 15:27:22 -0800958 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
959 super(resourceId);
Nathan Harold80865392017-04-04 19:37:48 -0700960 mSocket = socket;
961 mPort = port;
962 }
963
964 /** always guarded by IpSecService#this */
965 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800966 public void freeUnderlyingResources() {
Nathan Harold80865392017-04-04 19:37:48 -0700967 Log.d(TAG, "Closing port " + mPort);
968 IoUtils.closeQuietly(mSocket);
969 mSocket = null;
Nathan Harold80865392017-04-04 19:37:48 -0700970
Benedict Wong6855aee2017-11-16 15:27:22 -0800971 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700972 }
973
Nathan Harold80865392017-04-04 19:37:48 -0700974 public int getPort() {
975 return mPort;
976 }
977
Benedict Wonga386e372018-03-27 16:55:48 -0700978 public FileDescriptor getFileDescriptor() {
Nathan Harold80865392017-04-04 19:37:48 -0700979 return mSocket;
980 }
ludi89194d62017-05-22 10:52:23 -0700981
982 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800983 protected ResourceTracker getResourceTracker() {
984 return getUserRecord().mSocketQuotaTracker;
985 }
986
987 @Override
988 public void invalidate() {
989 getUserRecord().removeEncapSocketRecord(mResourceId);
990 }
991
992 @Override
ludi89194d62017-05-22 10:52:23 -0700993 public String toString() {
994 return new StringBuilder()
995 .append("{super=")
996 .append(super.toString())
997 .append(", mSocket=")
998 .append(mSocket)
999 .append(", mPort=")
1000 .append(mPort)
1001 .append("}")
1002 .toString();
1003 }
Nathan Harold80865392017-04-04 19:37:48 -07001004 }
Nathan Harold031acb82017-03-07 13:23:36 -08001005
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001006 /**
1007 * Constructs a new IpSecService instance
1008 *
1009 * @param context Binder context for this service
1010 */
Aaron Huangb944ff12022-01-12 15:11:01 +08001011 public IpSecService(Context context) {
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001012 this(context, new Dependencies());
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001013 }
1014
Nathan Harold65ef8432018-03-15 18:06:06 -07001015 @NonNull
1016 private AppOpsManager getAppOpsManager() {
1017 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Aaron Huangfbae3082021-12-06 15:18:42 +08001018 if (appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
Nathan Harold65ef8432018-03-15 18:06:06 -07001019 return appOps;
1020 }
1021
ludi5e623ea2017-05-12 09:15:00 -07001022 /** @hide */
1023 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001024 public IpSecService(Context context, Dependencies deps) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001025 this(
1026 context,
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001027 deps,
Nathan Harold5a19b952018-01-05 19:25:13 -08001028 (fd, uid) -> {
1029 try {
1030 TrafficStats.setThreadStatsUid(uid);
1031 TrafficStats.tagFileDescriptor(fd);
1032 } finally {
1033 TrafficStats.clearThreadStatsUid();
1034 }
1035 });
Benedict Wong083faee2017-12-03 19:42:36 -08001036 }
1037
1038 /** @hide */
1039 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001040 public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
ludi5e623ea2017-05-12 09:15:00 -07001041 mContext = context;
Aaron Huang2617cf52021-11-29 16:31:32 +08001042 mDeps = Objects.requireNonNull(deps, "Missing dependencies.");
Benedict Wong083faee2017-12-03 19:42:36 -08001043 mUidFdTagger = uidFdTagger;
Aaron Huang2617cf52021-11-29 16:31:32 +08001044 try {
1045 mNetd = mDeps.getNetdInstance(mContext);
1046 } catch (RemoteException e) {
1047 throw e.rethrowFromSystemServer();
1048 }
ludi5e623ea2017-05-12 09:15:00 -07001049 }
1050
Nathan Harold19b99d92017-08-23 13:46:33 -07001051 /**
1052 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1053 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1054 */
1055 private static void checkInetAddress(String inetAddress) {
1056 if (TextUtils.isEmpty(inetAddress)) {
1057 throw new IllegalArgumentException("Unspecified address");
1058 }
1059
Serik Beketayev7f507332020-12-06 22:31:23 -08001060 InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
Nathan Harold19b99d92017-08-23 13:46:33 -07001061
1062 if (checkAddr.isAnyLocalAddress()) {
1063 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1064 }
1065 }
1066
1067 /**
1068 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1069 * DIRECTION_IN or DIRECTION_OUT
1070 */
Benedict Wong908d34e2021-04-15 11:59:16 -07001071 private void checkDirection(int direction) {
Nathan Harold19b99d92017-08-23 13:46:33 -07001072 switch (direction) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001073 case IpSecManager.DIRECTION_OUT:
1074 case IpSecManager.DIRECTION_IN:
Nathan Harold19b99d92017-08-23 13:46:33 -07001075 return;
Benedict Wong908d34e2021-04-15 11:59:16 -07001076 case IpSecManager.DIRECTION_FWD:
Benedict Wong47b528c2021-05-10 18:26:02 -07001077 // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
Benedict Wong908d34e2021-04-15 11:59:16 -07001078 PermissionUtils.enforceNetworkStackPermission(mContext);
1079 return;
Nathan Harold19b99d92017-08-23 13:46:33 -07001080 }
1081 throw new IllegalArgumentException("Invalid Direction: " + direction);
1082 }
1083
Nathan Harold031acb82017-03-07 13:23:36 -08001084 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri20e96c52017-11-16 10:58:01 -08001085 @Override
1086 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harold5a19b952018-01-05 19:25:13 -08001087 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1088 checkInetAddress(destinationAddress);
Nathan Harold1b88f0e2018-03-28 08:52:51 -07001089 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1090 if (requestedSpi > 0 && requestedSpi < 256) {
1091 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1092 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001093 Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harold19b99d92017-08-23 13:46:33 -07001094
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001095 int callingUid = Binder.getCallingUid();
1096 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001097 final int resourceId = mNextResourceId++;
Nathan Harold031acb82017-03-07 13:23:36 -08001098
1099 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -08001100 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001101 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001102 return new IpSecSpiResponse(
Nathan Harold19b99d92017-08-23 13:46:33 -07001103 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold6e4681c2017-04-24 16:16:34 -07001104 }
Nathan Harold5a19b952018-01-05 19:25:13 -08001105
Aaron Huang2617cf52021-11-29 16:31:32 +08001106 spi = mNetd.ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold031acb82017-03-07 13:23:36 -08001107 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong6855aee2017-11-16 15:27:22 -08001108 userRecord.mSpiRecords.put(
Nathan Harold80865392017-04-04 19:37:48 -07001109 resourceId,
Benedict Wong6855aee2017-11-16 15:27:22 -08001110 new RefcountedResource<SpiRecord>(
Aaron Huang2617cf52021-11-29 16:31:32 +08001111 new SpiRecord(resourceId, "",
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001112 destinationAddress, spi), binder));
Nathan Harold031acb82017-03-07 13:23:36 -08001113 } catch (ServiceSpecificException e) {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001114 if (e.errorCode == OsConstants.ENOENT) {
1115 return new IpSecSpiResponse(
1116 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1117 }
1118 throw e;
Nathan Harold031acb82017-03-07 13:23:36 -08001119 } catch (RemoteException e) {
1120 throw e.rethrowFromSystemServer();
1121 }
Nathan Harold80865392017-04-04 19:37:48 -07001122 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1123 }
1124
1125 /* This method should only be called from Binder threads. Do not call this from
1126 * within the system server as it will crash the system on failure.
1127 */
Benedict Wong6855aee2017-11-16 15:27:22 -08001128 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold80865392017-04-04 19:37:48 -07001129 throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001130 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold031acb82017-03-07 13:23:36 -08001131 }
1132
1133 /** Release a previously allocated SPI that has been registered with the system server */
1134 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001135 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1136 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1137 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001138 }
1139
1140 /**
1141 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1142 * be unbound.
1143 *
1144 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1145 * a random open port and then bind by number, this function creates a temp socket, binds to a
1146 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1147 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1148 * FileHandle.
1149 *
1150 * <p>The loop in this function handles the inherent race window between un-binding to a port
1151 * and re-binding, during which the system could *technically* hand that port out to someone
1152 * else.
1153 */
Benedict Wongc423cc82017-10-10 20:44:28 -07001154 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold80865392017-04-04 19:37:48 -07001155 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1156 try {
1157 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1158 Os.bind(probeSocket, INADDR_ANY, 0);
1159 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1160 Os.close(probeSocket);
1161 Log.v(TAG, "Binding to port " + port);
1162 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongc423cc82017-10-10 20:44:28 -07001163 return port;
Nathan Harold80865392017-04-04 19:37:48 -07001164 } catch (ErrnoException e) {
1165 // Someone miraculously claimed the port just after we closed probeSocket.
1166 if (e.errno == OsConstants.EADDRINUSE) {
1167 continue;
1168 }
1169 throw e.rethrowAsIOException();
1170 }
1171 }
1172 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1173 }
Nathan Harold031acb82017-03-07 13:23:36 -08001174
1175 /**
Benedict Wong083faee2017-12-03 19:42:36 -08001176 * Functional interface to do traffic tagging of given sockets to UIDs.
1177 *
1178 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1179 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1180 *
1181 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1182 * methods that cannot be easily mocked/tested.
1183 */
1184 @VisibleForTesting
1185 public interface UidFdTagger {
1186 /**
1187 * Sets socket tag to assign all traffic to the provided UID.
1188 *
1189 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1190 * should be accounted to the UID of the unprivileged application.
1191 */
Aaron Huangfbae3082021-12-06 15:18:42 +08001192 void tag(FileDescriptor fd, int uid) throws IOException;
Benedict Wong083faee2017-12-03 19:42:36 -08001193 }
1194
1195 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001196 * Open a socket via the system server and bind it to the specified port (random if port=0).
1197 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1198 * cache the socket and a record of its owner so that it can and must be freed when no longer
1199 * needed.
1200 */
1201 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001202 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1203 throws RemoteException {
1204 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1205 throw new IllegalArgumentException(
1206 "Specified port number must be a valid non-reserved UDP port");
1207 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001208 Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
Nathan Harold19b99d92017-08-23 13:46:33 -07001209
Benedict Wong083faee2017-12-03 19:42:36 -08001210 int callingUid = Binder.getCallingUid();
1211 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001212 final int resourceId = mNextResourceId++;
Aaron Huangb01254f2021-12-23 10:47:05 +08001213
1214 ParcelFileDescriptor pFd = null;
Nathan Harold80865392017-04-04 19:37:48 -07001215 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001216 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001217 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1218 }
1219
Aaron Huangb01254f2021-12-23 10:47:05 +08001220 FileDescriptor sockFd = null;
1221 try {
1222 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1223 pFd = ParcelFileDescriptor.dup(sockFd);
1224 } finally {
1225 IoUtils.closeQuietly(sockFd);
1226 }
Nathan Harold80865392017-04-04 19:37:48 -07001227
Aaron Huangb01254f2021-12-23 10:47:05 +08001228 mUidFdTagger.tag(pFd.getFileDescriptor(), callingUid);
Nathan Harold80865392017-04-04 19:37:48 -07001229 // This code is common to both the unspecified and specified port cases
1230 Os.setsockoptInt(
Aaron Huangb01254f2021-12-23 10:47:05 +08001231 pFd.getFileDescriptor(),
Nathan Harold80865392017-04-04 19:37:48 -07001232 OsConstants.IPPROTO_UDP,
1233 OsConstants.UDP_ENCAP,
1234 OsConstants.UDP_ENCAP_ESPINUDP);
1235
Aaron Huangb01254f2021-12-23 10:47:05 +08001236 mNetd.ipSecSetEncapSocketOwner(pFd, callingUid);
Benedict Wong17687442017-12-06 21:56:35 -08001237 if (port != 0) {
1238 Log.v(TAG, "Binding to port " + port);
Aaron Huangb01254f2021-12-23 10:47:05 +08001239 Os.bind(pFd.getFileDescriptor(), INADDR_ANY, port);
Benedict Wong17687442017-12-06 21:56:35 -08001240 } else {
Aaron Huangb01254f2021-12-23 10:47:05 +08001241 port = bindToRandomPort(pFd.getFileDescriptor());
Benedict Wong17687442017-12-06 21:56:35 -08001242 }
1243
Benedict Wong6855aee2017-11-16 15:27:22 -08001244 userRecord.mEncapSocketRecords.put(
1245 resourceId,
1246 new RefcountedResource<EncapSocketRecord>(
Aaron Huangb01254f2021-12-23 10:47:05 +08001247 new EncapSocketRecord(resourceId, pFd.getFileDescriptor(), port),
1248 binder));
1249 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port,
1250 pFd.getFileDescriptor());
Nathan Harold80865392017-04-04 19:37:48 -07001251 } catch (IOException | ErrnoException e) {
Aaron Huangb01254f2021-12-23 10:47:05 +08001252 try {
1253 if (pFd != null) {
1254 pFd.close();
1255 }
1256 } catch (IOException ex) {
1257 // Nothing can be done at this point
1258 Log.e(TAG, "Failed to close pFd.");
1259 }
Nathan Harold80865392017-04-04 19:37:48 -07001260 }
1261 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1262 // The only reasonable condition that would cause that is resource unavailable.
1263 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold031acb82017-03-07 13:23:36 -08001264 }
1265
1266 /** close a socket that has been been allocated by and registered with the system server */
1267 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001268 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1269 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1270 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001271 }
Nathan Harold031acb82017-03-07 13:23:36 -08001272
Benedict Wong8bc90732018-01-18 18:31:45 -08001273 /**
1274 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1275 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1276 * needed.
1277 */
1278 @Override
1279 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001280 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1281 String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001282 enforceTunnelFeatureAndPermissions(callingPackage);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001283 Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
1284 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
Benedict Wong8bc90732018-01-18 18:31:45 -08001285 checkInetAddress(localAddr);
1286 checkInetAddress(remoteAddr);
1287
1288 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1289 // network (b/72316676).
1290
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001291 int callerUid = Binder.getCallingUid();
1292 UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001293 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1294 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1295 }
1296
1297 final int resourceId = mNextResourceId++;
1298 final int ikey = reserveNetId();
1299 final int okey = reserveNetId();
Nathan Harold7be7f452018-04-26 11:47:14 -07001300 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001301
Benedict Wong8edc5572018-01-19 17:36:02 -08001302 try {
1303 // Calls to netd:
1304 // Create VTI
1305 // Add inbound/outbound global policies
1306 // (use reqid = 0)
Aaron Huang2617cf52021-11-29 16:31:32 +08001307 mNetd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001308
paulhu00e34562021-10-26 09:00:50 +00001309 BinderUtils.withCleanCallingIdentity(() -> {
Aaron Huang2617cf52021-11-29 16:31:32 +08001310 NetdUtils.setInterfaceUp(mNetd, intfName);
Benedict Wong529e8aa2020-02-11 23:49:36 -08001311 });
1312
Benedict Wong38e52972018-05-07 20:06:44 -07001313 for (int selAddrFamily : ADDRESS_FAMILIES) {
1314 // Always send down correct local/remote addresses for template.
Aaron Huang2617cf52021-11-29 16:31:32 +08001315 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001316 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001317 selAddrFamily,
1318 IpSecManager.DIRECTION_OUT,
1319 localAddr,
1320 remoteAddr,
1321 0,
1322 okey,
Benedict Wong5d749842018-09-06 11:31:25 -07001323 0xffffffff,
1324 resourceId);
Aaron Huang2617cf52021-11-29 16:31:32 +08001325 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001326 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001327 selAddrFamily,
1328 IpSecManager.DIRECTION_IN,
1329 remoteAddr,
1330 localAddr,
1331 0,
1332 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001333 0xffffffff,
1334 resourceId);
Benedict Wong47b528c2021-05-10 18:26:02 -07001335
1336 // Add a forwarding policy on the tunnel interface. In order to support forwarding
1337 // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
1338 //
1339 // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
1340 // forwarding will be blocked by default (as would be the case if this policy was
1341 // absent).
1342 //
1343 // This is necessary only on the tunnel interface, and not any the interface to
1344 // which traffic will be forwarded to.
Aaron Huang2617cf52021-11-29 16:31:32 +08001345 mNetd.ipSecAddSecurityPolicy(
Benedict Wong908d34e2021-04-15 11:59:16 -07001346 callerUid,
1347 selAddrFamily,
1348 IpSecManager.DIRECTION_FWD,
1349 remoteAddr,
1350 localAddr,
1351 0,
1352 ikey,
1353 0xffffffff,
1354 resourceId);
Benedict Wong8edc5572018-01-19 17:36:02 -08001355 }
1356
1357 userRecord.mTunnelInterfaceRecords.put(
1358 resourceId,
1359 new RefcountedResource<TunnelInterfaceRecord>(
1360 new TunnelInterfaceRecord(
1361 resourceId,
1362 intfName,
1363 underlyingNetwork,
1364 localAddr,
1365 remoteAddr,
1366 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001367 okey,
1368 resourceId),
Benedict Wong8edc5572018-01-19 17:36:02 -08001369 binder));
1370 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1371 } catch (RemoteException e) {
1372 // Release keys if we got an error.
1373 releaseNetId(ikey);
1374 releaseNetId(okey);
1375 throw e.rethrowFromSystemServer();
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001376 } catch (Throwable t) {
1377 // Release keys if we got an error.
1378 releaseNetId(ikey);
1379 releaseNetId(okey);
1380 throw t;
Benedict Wong8edc5572018-01-19 17:36:02 -08001381 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001382 }
1383
1384 /**
1385 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1386 * from multiple local IP addresses over the same tunnel.
1387 */
1388 @Override
Benedict Wong97c3c942018-03-01 18:53:07 -08001389 public synchronized void addAddressToTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001390 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001391 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001392 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1393
1394 // Get tunnelInterface record; if no such interface is found, will throw
1395 // IllegalArgumentException
1396 TunnelInterfaceRecord tunnelInterfaceInfo =
1397 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1398
Benedict Wong97c3c942018-03-01 18:53:07 -08001399 try {
1400 // We can assume general validity of the IP address, since we get them as a
1401 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001402 mNetd.interfaceAddAddress(
1403 tunnelInterfaceInfo.mInterfaceName,
1404 localAddr.getAddress().getHostAddress(),
1405 localAddr.getPrefixLength());
Benedict Wong97c3c942018-03-01 18:53:07 -08001406 } catch (RemoteException e) {
1407 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001408 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001409 }
1410
1411 /**
1412 * Remove a new local address from the tunnel interface. After removal, the address will no
1413 * longer be available to send from, or receive on.
1414 */
1415 @Override
1416 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001417 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001418 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001419
Nathan Harold65ef8432018-03-15 18:06:06 -07001420 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8bc90732018-01-18 18:31:45 -08001421 // Get tunnelInterface record; if no such interface is found, will throw
1422 // IllegalArgumentException
1423 TunnelInterfaceRecord tunnelInterfaceInfo =
1424 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1425
Benedict Wong97c3c942018-03-01 18:53:07 -08001426 try {
1427 // We can assume general validity of the IP address, since we get them as a
1428 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001429 mNetd.interfaceDelAddress(
Benedict Wong97c3c942018-03-01 18:53:07 -08001430 tunnelInterfaceInfo.mInterfaceName,
1431 localAddr.getAddress().getHostAddress(),
1432 localAddr.getPrefixLength());
1433 } catch (RemoteException e) {
1434 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001435 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001436 }
1437
Yan Yana2f3b492020-09-29 23:38:00 -07001438 /** Set TunnelInterface to use a specific underlying network. */
1439 @Override
1440 public synchronized void setNetworkForTunnelInterface(
1441 int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
1442 enforceTunnelFeatureAndPermissions(callingPackage);
1443 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1444
1445 final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1446
1447 // Get tunnelInterface record; if no such interface is found, will throw
1448 // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
1449 final TunnelInterfaceRecord tunnelInterfaceInfo =
1450 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1451
1452 final ConnectivityManager connectivityManager =
1453 mContext.getSystemService(ConnectivityManager.class);
1454 final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
1455 if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
1456 throw new IllegalArgumentException(
1457 "Underlying network cannot be the network being exposed by this tunnel");
1458 }
1459
1460 // It is meaningless to check if the network exists or is valid because the network might
1461 // disconnect at any time after it passes the check.
1462
1463 tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
1464 }
1465
Benedict Wong8bc90732018-01-18 18:31:45 -08001466 /**
1467 * Delete a TunnelInterface that has been been allocated by and registered with the system
1468 * server
1469 */
1470 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001471 public synchronized void deleteTunnelInterface(
1472 int resourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001473 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001474 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1475 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1476 }
1477
Benedict Wong70867e52017-11-06 20:49:10 -08001478 @VisibleForTesting
Nathan Harold5a19b952018-01-05 19:25:13 -08001479 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1480 IpSecAlgorithm auth = config.getAuthentication();
1481 IpSecAlgorithm crypt = config.getEncryption();
1482 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong70867e52017-11-06 20:49:10 -08001483
Nathan Harold5a19b952018-01-05 19:25:13 -08001484 // Validate the algorithm set
1485 Preconditions.checkArgument(
1486 aead != null || crypt != null || auth != null,
1487 "No Encryption or Authentication algorithms specified");
1488 Preconditions.checkArgument(
1489 auth == null || auth.isAuthentication(),
1490 "Unsupported algorithm for Authentication");
1491 Preconditions.checkArgument(
Benedict Wong70867e52017-11-06 20:49:10 -08001492 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harold5a19b952018-01-05 19:25:13 -08001493 Preconditions.checkArgument(
1494 aead == null || aead.isAead(),
1495 "Unsupported algorithm for Authenticated Encryption");
1496 Preconditions.checkArgument(
1497 aead == null || (auth == null && crypt == null),
1498 "Authenticated Encryption is mutually exclusive with other Authentication "
1499 + "or Encryption algorithms");
Benedict Wong70867e52017-11-06 20:49:10 -08001500 }
1501
evitayan43d93a02018-03-22 17:53:08 -07001502 private int getFamily(String inetAddress) {
1503 int family = AF_UNSPEC;
Serik Beketayev7f507332020-12-06 22:31:23 -08001504 InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
evitayan43d93a02018-03-22 17:53:08 -07001505 if (checkAddress instanceof Inet4Address) {
1506 family = AF_INET;
1507 } else if (checkAddress instanceof Inet6Address) {
1508 family = AF_INET6;
1509 }
1510 return family;
1511 }
1512
Nathan Harold031acb82017-03-07 13:23:36 -08001513 /**
Chiachang Wang2fea4a72020-08-12 12:23:59 +08001514 * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
Nathan Harold19b99d92017-08-23 13:46:33 -07001515 * IllegalArgumentException if they are not.
1516 */
1517 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001518 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1519
Nathan Harold19b99d92017-08-23 13:46:33 -07001520 switch (config.getEncapType()) {
1521 case IpSecTransform.ENCAP_NONE:
1522 break;
1523 case IpSecTransform.ENCAP_ESPINUDP:
1524 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong6855aee2017-11-16 15:27:22 -08001525 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1526 userRecord.mEncapSocketRecords.getResourceOrThrow(
1527 config.getEncapSocketResourceId());
Nathan Harold19b99d92017-08-23 13:46:33 -07001528
1529 int port = config.getEncapRemotePort();
1530 if (port <= 0 || port > 0xFFFF) {
1531 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1532 }
1533 break;
1534 default:
1535 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1536 }
1537
Nathan Harold5a19b952018-01-05 19:25:13 -08001538 validateAlgorithms(config);
Nathan Harold19b99d92017-08-23 13:46:33 -07001539
Nathan Harold5a19b952018-01-05 19:25:13 -08001540 // Retrieve SPI record; will throw IllegalArgumentException if not found
1541 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1542
Benedict Wong68aac2a2017-12-13 18:26:40 -08001543 // Check to ensure that SPI has not already been used.
1544 if (s.getOwnedByTransform()) {
1545 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1546 }
1547
Nathan Harold5a19b952018-01-05 19:25:13 -08001548 // If no remote address is supplied, then use one from the SPI.
1549 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1550 config.setDestinationAddress(s.getDestinationAddress());
1551 }
1552
1553 // All remote addresses must match
1554 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1555 throw new IllegalArgumentException("Mismatched remote addresseses.");
1556 }
1557
1558 // This check is technically redundant due to the chain of custody between the SPI and
1559 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1560 // the transform, this will prevent us from messing up.
1561 checkInetAddress(config.getDestinationAddress());
1562
1563 // Require a valid source address for all transforms.
1564 checkInetAddress(config.getSourceAddress());
1565
evitayan43d93a02018-03-22 17:53:08 -07001566 // Check to ensure source and destination have the same address family.
1567 String sourceAddress = config.getSourceAddress();
1568 String destinationAddress = config.getDestinationAddress();
1569 int sourceFamily = getFamily(sourceAddress);
1570 int destinationFamily = getFamily(destinationAddress);
1571 if (sourceFamily != destinationFamily) {
1572 throw new IllegalArgumentException(
1573 "Source address ("
1574 + sourceAddress
1575 + ") and destination address ("
1576 + destinationAddress
1577 + ") have different address families.");
1578 }
1579
1580 // Throw an error if UDP Encapsulation is not used in IPv4.
1581 if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
1582 throw new IllegalArgumentException(
1583 "UDP Encapsulation is not supported for this address family");
1584 }
1585
Nathan Harold5a19b952018-01-05 19:25:13 -08001586 switch (config.getMode()) {
1587 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold025aae12018-02-02 18:34:25 -08001588 break;
Nathan Harold5a19b952018-01-05 19:25:13 -08001589 case IpSecTransform.MODE_TUNNEL:
1590 break;
1591 default:
1592 throw new IllegalArgumentException(
1593 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harold19b99d92017-08-23 13:46:33 -07001594 }
Benedict Wong683441d2018-07-25 18:46:19 -07001595
1596 config.setMarkValue(0);
1597 config.setMarkMask(0);
Nathan Harold19b99d92017-08-23 13:46:33 -07001598 }
1599
Benedict Wong2b6a14e2018-09-13 16:45:12 -07001600 private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
Nathan Harold7c250ae2018-05-15 19:18:38 -07001601
Benedict Wonge9763752018-11-08 19:45:34 -08001602 private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1603 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1604 throw new UnsupportedOperationException(
1605 "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1606 }
1607
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001608 Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Benedict Wongc85b7b02019-11-12 22:31:51 -08001609
1610 // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
1611 // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
1612 // permission or is the System Server.
1613 if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
1614 TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1615 return;
Nathan Harold65ef8432018-03-15 18:06:06 -07001616 }
Benedict Wongc85b7b02019-11-12 22:31:51 -08001617 mContext.enforceCallingOrSelfPermission(
1618 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
Nathan Harold025aae12018-02-02 18:34:25 -08001619 }
1620
Benedict Wong8edc5572018-01-19 17:36:02 -08001621 private void createOrUpdateTransform(
1622 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1623 throws RemoteException {
1624
1625 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1626 if (encapType != IpSecTransform.ENCAP_NONE) {
1627 encapLocalPort = socketRecord.getPort();
1628 encapRemotePort = c.getEncapRemotePort();
1629 }
1630
1631 IpSecAlgorithm auth = c.getAuthentication();
1632 IpSecAlgorithm crypt = c.getEncryption();
1633 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1634
Benedict Wong778327e2018-03-15 19:41:41 -07001635 String cryptName;
1636 if (crypt == null) {
1637 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1638 } else {
1639 cryptName = crypt.getName();
1640 }
1641
Aaron Huang2617cf52021-11-29 16:31:32 +08001642 mNetd.ipSecAddSecurityAssociation(
1643 Binder.getCallingUid(),
1644 c.getMode(),
1645 c.getSourceAddress(),
1646 c.getDestinationAddress(),
1647 (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
1648 spiRecord.getSpi(),
1649 c.getMarkValue(),
1650 c.getMarkMask(),
1651 (auth != null) ? auth.getName() : "",
1652 (auth != null) ? auth.getKey() : new byte[] {},
1653 (auth != null) ? auth.getTruncationLengthBits() : 0,
1654 cryptName,
1655 (crypt != null) ? crypt.getKey() : new byte[] {},
1656 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1657 (authCrypt != null) ? authCrypt.getName() : "",
1658 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1659 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1660 encapType,
1661 encapLocalPort,
1662 encapRemotePort,
1663 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001664 }
1665
Nathan Harold19b99d92017-08-23 13:46:33 -07001666 /**
Benedict Wong8edc5572018-01-19 17:36:02 -08001667 * Create a IPsec transform, which represents a single security association in the kernel. The
1668 * transform will be cached by the system server and must be freed when no longer needed. It is
1669 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1670 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold031acb82017-03-07 13:23:36 -08001671 */
1672 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001673 public synchronized IpSecTransformResponse createTransform(
1674 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001675 Objects.requireNonNull(c);
Nathan Harold65ef8432018-03-15 18:06:06 -07001676 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
Benedict Wonge9763752018-11-08 19:45:34 -08001677 enforceTunnelFeatureAndPermissions(callingPackage);
Nathan Harold65ef8432018-03-15 18:06:06 -07001678 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001679 checkIpSecConfig(c);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001680 Objects.requireNonNull(binder, "Null Binder passed to createTransform");
Nathan Haroldfdafce22017-12-13 19:16:33 -08001681 final int resourceId = mNextResourceId++;
Benedict Wong6855aee2017-11-16 15:27:22 -08001682
1683 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wongcbd329b2017-12-13 17:16:53 -08001684 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong6855aee2017-11-16 15:27:22 -08001685
1686 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001687 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1688 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001689
Benedict Wong6855aee2017-11-16 15:27:22 -08001690 EncapSocketRecord socketRecord = null;
Benedict Wong8edc5572018-01-19 17:36:02 -08001691 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001692 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1693 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1694 c.getEncapSocketResourceId());
1695 dependencies.add(refcountedSocketRecord);
Benedict Wong6855aee2017-11-16 15:27:22 -08001696 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold80865392017-04-04 19:37:48 -07001697 }
1698
Nathan Harold5a19b952018-01-05 19:25:13 -08001699 RefcountedResource<SpiRecord> refcountedSpiRecord =
1700 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1701 dependencies.add(refcountedSpiRecord);
1702 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong6855aee2017-11-16 15:27:22 -08001703
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001704 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong8edc5572018-01-19 17:36:02 -08001705
1706 // SA was created successfully, time to construct a record and lock it away
Benedict Wong6855aee2017-11-16 15:27:22 -08001707 userRecord.mTransformRecords.put(
1708 resourceId,
1709 new RefcountedResource<TransformRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001710 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong6855aee2017-11-16 15:27:22 -08001711 binder,
1712 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold80865392017-04-04 19:37:48 -07001713 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001714 }
1715
1716 /**
1717 * Delete a transport mode transform that was previously allocated by + registered with the
1718 * system server. If this is called on an inactive (or non-existent) transform, it will not
1719 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1720 * other reasons.
1721 */
1722 @Override
Benedict Wong0fff56e2018-01-18 14:38:16 -08001723 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001724 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1725 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001726 }
1727
1728 /**
1729 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1730 * association as a correspondent policy to the provided socket
1731 */
1732 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001733 public synchronized void applyTransportModeTransform(
Nathan Harold5a19b952018-01-05 19:25:13 -08001734 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001735 int callingUid = Binder.getCallingUid();
1736 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harold5a19b952018-01-05 19:25:13 -08001737 checkDirection(direction);
Benedict Wong6855aee2017-11-16 15:27:22 -08001738 // Get transform record; if no transform is found, will throw IllegalArgumentException
1739 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001740
Nathan Harold80865392017-04-04 19:37:48 -07001741 // TODO: make this a function.
Aaron Huangfbae3082021-12-06 15:18:42 +08001742 if (info.mPid != getCallingPid() || info.mUid != callingUid) {
Nathan Harold80865392017-04-04 19:37:48 -07001743 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1744 }
1745
Benedict Wong8bc90732018-01-18 18:31:45 -08001746 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold80865392017-04-04 19:37:48 -07001747 IpSecConfig c = info.getConfig();
Benedict Wong8bc90732018-01-18 18:31:45 -08001748 Preconditions.checkArgument(
1749 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1750 "Transform mode was not Transport mode; cannot be applied to a socket");
1751
Aaron Huang2617cf52021-11-29 16:31:32 +08001752 mNetd.ipSecApplyTransportModeTransform(
1753 socket,
1754 callingUid,
1755 direction,
1756 c.getSourceAddress(),
1757 c.getDestinationAddress(),
1758 info.getSpiRecord().getSpi());
Nathan Harold031acb82017-03-07 13:23:36 -08001759 }
Nathan Harold80865392017-04-04 19:37:48 -07001760
Nathan Harold031acb82017-03-07 13:23:36 -08001761 /**
Nathan Harold5a19b952018-01-05 19:25:13 -08001762 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1763 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1764 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1765 * reserved for future improved input validation.
Nathan Harold031acb82017-03-07 13:23:36 -08001766 */
1767 @Override
Nathan Harold0d483b72018-01-17 01:00:20 -08001768 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1769 throws RemoteException {
Aaron Huang2617cf52021-11-29 16:31:32 +08001770 mNetd.ipSecRemoveTransportModeTransform(socket);
Nathan Harold031acb82017-03-07 13:23:36 -08001771 }
1772
Benedict Wong8bc90732018-01-18 18:31:45 -08001773 /**
1774 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1775 * security association as a correspondent policy to the provided interface
1776 */
1777 @Override
1778 public synchronized void applyTunnelModeTransform(
Nathan Harold65ef8432018-03-15 18:06:06 -07001779 int tunnelResourceId, int direction,
1780 int transformResourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001781 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001782 checkDirection(direction);
1783
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001784 int callingUid = Binder.getCallingUid();
1785 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001786
1787 // Get transform record; if no transform is found, will throw IllegalArgumentException
1788 TransformRecord transformInfo =
1789 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1790
1791 // Get tunnelInterface record; if no such interface is found, will throw
1792 // IllegalArgumentException
1793 TunnelInterfaceRecord tunnelInterfaceInfo =
1794 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1795
1796 // Get config and check that to-be-applied transform has the correct mode
1797 IpSecConfig c = transformInfo.getConfig();
1798 Preconditions.checkArgument(
1799 c.getMode() == IpSecTransform.MODE_TUNNEL,
1800 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1801
Benedict Wong8edc5572018-01-19 17:36:02 -08001802 EncapSocketRecord socketRecord = null;
1803 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1804 socketRecord =
1805 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1806 }
Benedict Wongec2e2e22019-10-03 11:09:00 -07001807 SpiRecord spiRecord = transformInfo.getSpiRecord();
Benedict Wong8edc5572018-01-19 17:36:02 -08001808
Benedict Wong8bc90732018-01-18 18:31:45 -08001809 int mark =
Benedict Wong38e52972018-05-07 20:06:44 -07001810 (direction == IpSecManager.DIRECTION_OUT)
1811 ? tunnelInterfaceInfo.getOkey()
Benedict Wong908d34e2021-04-15 11:59:16 -07001812 : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
Benedict Wong8bc90732018-01-18 18:31:45 -08001813
Benedict Wong8edc5572018-01-19 17:36:02 -08001814 try {
Benedict Wong5d749842018-09-06 11:31:25 -07001815 // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
1816 // SPI matching as part of the template resolution.
1817 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1818 c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
1819
Benedict Wong683441d2018-07-25 18:46:19 -07001820 // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
1821 // (and backporting) would allow us to narrow the mark space, and ensure that the SA
1822 // and SPs have matching marks (as VTI are meant to be built).
1823 // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
1824 // config matches the actual allocated resources in the kernel.
Benedict Wongc6fcedd2018-11-21 21:24:55 -08001825 // All SAs will have zero marks (from creation time), and any policy that matches the
1826 // same src/dst could match these SAs. Non-IpSecService governed processes that
1827 // establish floating policies with the same src/dst may result in undefined
1828 // behavior. This is generally limited to vendor code due to the permissions
1829 // (CAP_NET_ADMIN) required.
Benedict Wong683441d2018-07-25 18:46:19 -07001830 //
1831 // c.setMarkValue(mark);
1832 // c.setMarkMask(0xffffffff);
Benedict Wong8edc5572018-01-19 17:36:02 -08001833
1834 if (direction == IpSecManager.DIRECTION_OUT) {
1835 // Set output mark via underlying network (output only)
1836 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1837
Benedict Wong5d749842018-09-06 11:31:25 -07001838 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
1839 // but want to guarantee outbound packets are sent over the new SA.
Benedict Wongec2e2e22019-10-03 11:09:00 -07001840 spi = spiRecord.getSpi();
Benedict Wong5d749842018-09-06 11:31:25 -07001841 }
1842
1843 // Always update the policy with the relevant XFRM_IF_ID
1844 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +08001845 mNetd.ipSecUpdateSecurityPolicy(
1846 callingUid,
1847 selAddrFamily,
1848 direction,
1849 transformInfo.getConfig().getSourceAddress(),
1850 transformInfo.getConfig().getDestinationAddress(),
1851 spi, // If outbound, also add SPI to the policy.
1852 mark, // Must always set policy mark; ikey/okey for VTIs
1853 0xffffffff,
1854 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001855 }
1856
1857 // Update SA with tunnel mark (ikey or okey based on direction)
1858 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1859 } catch (ServiceSpecificException e) {
1860 if (e.errorCode == EINVAL) {
1861 throw new IllegalArgumentException(e.toString());
1862 } else {
1863 throw e;
1864 }
1865 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001866 }
1867
Nathan Harold031acb82017-03-07 13:23:36 -08001868 @Override
ludi89194d62017-05-22 10:52:23 -07001869 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001870 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludi89194d62017-05-22 10:52:23 -07001871
1872 pw.println("IpSecService dump:");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001873 pw.println();
ludi89194d62017-05-22 10:52:23 -07001874
Benedict Wong6855aee2017-11-16 15:27:22 -08001875 pw.println("mUserResourceTracker:");
1876 pw.println(mUserResourceTracker);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001877 }
1878}