blob: 179d9459fd84baf074056615ac535ebf021a36e6 [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 */
lucaslind66e6082021-01-18 13:06:39 +08001011 private IpSecService(Context context) {
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001012 this(context, new Dependencies());
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001013 }
1014
lucaslind66e6082021-01-18 13:06:39 +08001015 static IpSecService create(Context context)
Benedict Wong529e8aa2020-02-11 23:49:36 -08001016 throws InterruptedException {
lucaslind66e6082021-01-18 13:06:39 +08001017 final IpSecService service = new IpSecService(context);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001018 return service;
1019 }
1020
Nathan Harold65ef8432018-03-15 18:06:06 -07001021 @NonNull
1022 private AppOpsManager getAppOpsManager() {
1023 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Aaron Huangfbae3082021-12-06 15:18:42 +08001024 if (appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
Nathan Harold65ef8432018-03-15 18:06:06 -07001025 return appOps;
1026 }
1027
ludi5e623ea2017-05-12 09:15:00 -07001028 /** @hide */
1029 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001030 public IpSecService(Context context, Dependencies deps) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001031 this(
1032 context,
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001033 deps,
Nathan Harold5a19b952018-01-05 19:25:13 -08001034 (fd, uid) -> {
1035 try {
1036 TrafficStats.setThreadStatsUid(uid);
1037 TrafficStats.tagFileDescriptor(fd);
1038 } finally {
1039 TrafficStats.clearThreadStatsUid();
1040 }
1041 });
Benedict Wong083faee2017-12-03 19:42:36 -08001042 }
1043
1044 /** @hide */
1045 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001046 public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
ludi5e623ea2017-05-12 09:15:00 -07001047 mContext = context;
Aaron Huang2617cf52021-11-29 16:31:32 +08001048 mDeps = Objects.requireNonNull(deps, "Missing dependencies.");
Benedict Wong083faee2017-12-03 19:42:36 -08001049 mUidFdTagger = uidFdTagger;
Aaron Huang2617cf52021-11-29 16:31:32 +08001050 try {
1051 mNetd = mDeps.getNetdInstance(mContext);
1052 } catch (RemoteException e) {
1053 throw e.rethrowFromSystemServer();
1054 }
ludi5e623ea2017-05-12 09:15:00 -07001055 }
1056
Aaron Huangfbae3082021-12-06 15:18:42 +08001057 /** Called by system server when system is ready. */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001058 public void systemReady() {
1059 if (isNetdAlive()) {
paulhu00e34562021-10-26 09:00:50 +00001060 Log.d(TAG, "IpSecService is ready");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001061 } else {
paulhu00e34562021-10-26 09:00:50 +00001062 Log.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001063 }
1064 }
1065
Nathan Harold80865392017-04-04 19:37:48 -07001066 synchronized boolean isNetdAlive() {
1067 try {
Aaron Huang2617cf52021-11-29 16:31:32 +08001068 if (mNetd == null) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001069 return false;
1070 }
Aaron Huang2617cf52021-11-29 16:31:32 +08001071 return mNetd.isAlive();
Nathan Harold80865392017-04-04 19:37:48 -07001072 } catch (RemoteException re) {
1073 return false;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001074 }
1075 }
1076
Nathan Harold19b99d92017-08-23 13:46:33 -07001077 /**
1078 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1079 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1080 */
1081 private static void checkInetAddress(String inetAddress) {
1082 if (TextUtils.isEmpty(inetAddress)) {
1083 throw new IllegalArgumentException("Unspecified address");
1084 }
1085
Serik Beketayev7f507332020-12-06 22:31:23 -08001086 InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
Nathan Harold19b99d92017-08-23 13:46:33 -07001087
1088 if (checkAddr.isAnyLocalAddress()) {
1089 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1090 }
1091 }
1092
1093 /**
1094 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1095 * DIRECTION_IN or DIRECTION_OUT
1096 */
Benedict Wong908d34e2021-04-15 11:59:16 -07001097 private void checkDirection(int direction) {
Nathan Harold19b99d92017-08-23 13:46:33 -07001098 switch (direction) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001099 case IpSecManager.DIRECTION_OUT:
1100 case IpSecManager.DIRECTION_IN:
Nathan Harold19b99d92017-08-23 13:46:33 -07001101 return;
Benedict Wong908d34e2021-04-15 11:59:16 -07001102 case IpSecManager.DIRECTION_FWD:
Benedict Wong47b528c2021-05-10 18:26:02 -07001103 // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
Benedict Wong908d34e2021-04-15 11:59:16 -07001104 PermissionUtils.enforceNetworkStackPermission(mContext);
1105 return;
Nathan Harold19b99d92017-08-23 13:46:33 -07001106 }
1107 throw new IllegalArgumentException("Invalid Direction: " + direction);
1108 }
1109
Nathan Harold031acb82017-03-07 13:23:36 -08001110 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri20e96c52017-11-16 10:58:01 -08001111 @Override
1112 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harold5a19b952018-01-05 19:25:13 -08001113 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1114 checkInetAddress(destinationAddress);
Nathan Harold1b88f0e2018-03-28 08:52:51 -07001115 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1116 if (requestedSpi > 0 && requestedSpi < 256) {
1117 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1118 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001119 Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harold19b99d92017-08-23 13:46:33 -07001120
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001121 int callingUid = Binder.getCallingUid();
1122 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001123 final int resourceId = mNextResourceId++;
Nathan Harold031acb82017-03-07 13:23:36 -08001124
1125 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -08001126 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001127 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001128 return new IpSecSpiResponse(
Nathan Harold19b99d92017-08-23 13:46:33 -07001129 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold6e4681c2017-04-24 16:16:34 -07001130 }
Nathan Harold5a19b952018-01-05 19:25:13 -08001131
Aaron Huang2617cf52021-11-29 16:31:32 +08001132 spi = mNetd.ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold031acb82017-03-07 13:23:36 -08001133 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong6855aee2017-11-16 15:27:22 -08001134 userRecord.mSpiRecords.put(
Nathan Harold80865392017-04-04 19:37:48 -07001135 resourceId,
Benedict Wong6855aee2017-11-16 15:27:22 -08001136 new RefcountedResource<SpiRecord>(
Aaron Huang2617cf52021-11-29 16:31:32 +08001137 new SpiRecord(resourceId, "",
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001138 destinationAddress, spi), binder));
Nathan Harold031acb82017-03-07 13:23:36 -08001139 } catch (ServiceSpecificException e) {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001140 if (e.errorCode == OsConstants.ENOENT) {
1141 return new IpSecSpiResponse(
1142 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1143 }
1144 throw e;
Nathan Harold031acb82017-03-07 13:23:36 -08001145 } catch (RemoteException e) {
1146 throw e.rethrowFromSystemServer();
1147 }
Nathan Harold80865392017-04-04 19:37:48 -07001148 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1149 }
1150
1151 /* This method should only be called from Binder threads. Do not call this from
1152 * within the system server as it will crash the system on failure.
1153 */
Benedict Wong6855aee2017-11-16 15:27:22 -08001154 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold80865392017-04-04 19:37:48 -07001155 throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001156 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold031acb82017-03-07 13:23:36 -08001157 }
1158
1159 /** Release a previously allocated SPI that has been registered with the system server */
1160 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001161 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1162 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1163 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001164 }
1165
1166 /**
1167 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1168 * be unbound.
1169 *
1170 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1171 * a random open port and then bind by number, this function creates a temp socket, binds to a
1172 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1173 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1174 * FileHandle.
1175 *
1176 * <p>The loop in this function handles the inherent race window between un-binding to a port
1177 * and re-binding, during which the system could *technically* hand that port out to someone
1178 * else.
1179 */
Benedict Wongc423cc82017-10-10 20:44:28 -07001180 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold80865392017-04-04 19:37:48 -07001181 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1182 try {
1183 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1184 Os.bind(probeSocket, INADDR_ANY, 0);
1185 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1186 Os.close(probeSocket);
1187 Log.v(TAG, "Binding to port " + port);
1188 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongc423cc82017-10-10 20:44:28 -07001189 return port;
Nathan Harold80865392017-04-04 19:37:48 -07001190 } catch (ErrnoException e) {
1191 // Someone miraculously claimed the port just after we closed probeSocket.
1192 if (e.errno == OsConstants.EADDRINUSE) {
1193 continue;
1194 }
1195 throw e.rethrowAsIOException();
1196 }
1197 }
1198 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1199 }
Nathan Harold031acb82017-03-07 13:23:36 -08001200
1201 /**
Benedict Wong083faee2017-12-03 19:42:36 -08001202 * Functional interface to do traffic tagging of given sockets to UIDs.
1203 *
1204 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1205 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1206 *
1207 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1208 * methods that cannot be easily mocked/tested.
1209 */
1210 @VisibleForTesting
1211 public interface UidFdTagger {
1212 /**
1213 * Sets socket tag to assign all traffic to the provided UID.
1214 *
1215 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1216 * should be accounted to the UID of the unprivileged application.
1217 */
Aaron Huangfbae3082021-12-06 15:18:42 +08001218 void tag(FileDescriptor fd, int uid) throws IOException;
Benedict Wong083faee2017-12-03 19:42:36 -08001219 }
1220
1221 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001222 * Open a socket via the system server and bind it to the specified port (random if port=0).
1223 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1224 * cache the socket and a record of its owner so that it can and must be freed when no longer
1225 * needed.
1226 */
1227 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001228 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1229 throws RemoteException {
1230 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1231 throw new IllegalArgumentException(
1232 "Specified port number must be a valid non-reserved UDP port");
1233 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001234 Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
Nathan Harold19b99d92017-08-23 13:46:33 -07001235
Benedict Wong083faee2017-12-03 19:42:36 -08001236 int callingUid = Binder.getCallingUid();
1237 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001238 final int resourceId = mNextResourceId++;
Aaron Huangb01254f2021-12-23 10:47:05 +08001239
1240 ParcelFileDescriptor pFd = null;
Nathan Harold80865392017-04-04 19:37:48 -07001241 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001242 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001243 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1244 }
1245
Aaron Huangb01254f2021-12-23 10:47:05 +08001246 FileDescriptor sockFd = null;
1247 try {
1248 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1249 pFd = ParcelFileDescriptor.dup(sockFd);
1250 } finally {
1251 IoUtils.closeQuietly(sockFd);
1252 }
Nathan Harold80865392017-04-04 19:37:48 -07001253
Aaron Huangb01254f2021-12-23 10:47:05 +08001254 mUidFdTagger.tag(pFd.getFileDescriptor(), callingUid);
Nathan Harold80865392017-04-04 19:37:48 -07001255 // This code is common to both the unspecified and specified port cases
1256 Os.setsockoptInt(
Aaron Huangb01254f2021-12-23 10:47:05 +08001257 pFd.getFileDescriptor(),
Nathan Harold80865392017-04-04 19:37:48 -07001258 OsConstants.IPPROTO_UDP,
1259 OsConstants.UDP_ENCAP,
1260 OsConstants.UDP_ENCAP_ESPINUDP);
1261
Aaron Huangb01254f2021-12-23 10:47:05 +08001262 mNetd.ipSecSetEncapSocketOwner(pFd, callingUid);
Benedict Wong17687442017-12-06 21:56:35 -08001263 if (port != 0) {
1264 Log.v(TAG, "Binding to port " + port);
Aaron Huangb01254f2021-12-23 10:47:05 +08001265 Os.bind(pFd.getFileDescriptor(), INADDR_ANY, port);
Benedict Wong17687442017-12-06 21:56:35 -08001266 } else {
Aaron Huangb01254f2021-12-23 10:47:05 +08001267 port = bindToRandomPort(pFd.getFileDescriptor());
Benedict Wong17687442017-12-06 21:56:35 -08001268 }
1269
Benedict Wong6855aee2017-11-16 15:27:22 -08001270 userRecord.mEncapSocketRecords.put(
1271 resourceId,
1272 new RefcountedResource<EncapSocketRecord>(
Aaron Huangb01254f2021-12-23 10:47:05 +08001273 new EncapSocketRecord(resourceId, pFd.getFileDescriptor(), port),
1274 binder));
1275 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port,
1276 pFd.getFileDescriptor());
Nathan Harold80865392017-04-04 19:37:48 -07001277 } catch (IOException | ErrnoException e) {
Aaron Huangb01254f2021-12-23 10:47:05 +08001278 try {
1279 if (pFd != null) {
1280 pFd.close();
1281 }
1282 } catch (IOException ex) {
1283 // Nothing can be done at this point
1284 Log.e(TAG, "Failed to close pFd.");
1285 }
Nathan Harold80865392017-04-04 19:37:48 -07001286 }
1287 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1288 // The only reasonable condition that would cause that is resource unavailable.
1289 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold031acb82017-03-07 13:23:36 -08001290 }
1291
1292 /** close a socket that has been been allocated by and registered with the system server */
1293 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001294 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1295 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1296 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001297 }
Nathan Harold031acb82017-03-07 13:23:36 -08001298
Benedict Wong8bc90732018-01-18 18:31:45 -08001299 /**
1300 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1301 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1302 * needed.
1303 */
1304 @Override
1305 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001306 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1307 String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001308 enforceTunnelFeatureAndPermissions(callingPackage);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001309 Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
1310 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
Benedict Wong8bc90732018-01-18 18:31:45 -08001311 checkInetAddress(localAddr);
1312 checkInetAddress(remoteAddr);
1313
1314 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1315 // network (b/72316676).
1316
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001317 int callerUid = Binder.getCallingUid();
1318 UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001319 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1320 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1321 }
1322
1323 final int resourceId = mNextResourceId++;
1324 final int ikey = reserveNetId();
1325 final int okey = reserveNetId();
Nathan Harold7be7f452018-04-26 11:47:14 -07001326 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001327
Benedict Wong8edc5572018-01-19 17:36:02 -08001328 try {
1329 // Calls to netd:
1330 // Create VTI
1331 // Add inbound/outbound global policies
1332 // (use reqid = 0)
Aaron Huang2617cf52021-11-29 16:31:32 +08001333 mNetd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001334
paulhu00e34562021-10-26 09:00:50 +00001335 BinderUtils.withCleanCallingIdentity(() -> {
Aaron Huang2617cf52021-11-29 16:31:32 +08001336 NetdUtils.setInterfaceUp(mNetd, intfName);
Benedict Wong529e8aa2020-02-11 23:49:36 -08001337 });
1338
Benedict Wong38e52972018-05-07 20:06:44 -07001339 for (int selAddrFamily : ADDRESS_FAMILIES) {
1340 // Always send down correct local/remote addresses for template.
Aaron Huang2617cf52021-11-29 16:31:32 +08001341 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001342 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001343 selAddrFamily,
1344 IpSecManager.DIRECTION_OUT,
1345 localAddr,
1346 remoteAddr,
1347 0,
1348 okey,
Benedict Wong5d749842018-09-06 11:31:25 -07001349 0xffffffff,
1350 resourceId);
Aaron Huang2617cf52021-11-29 16:31:32 +08001351 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001352 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001353 selAddrFamily,
1354 IpSecManager.DIRECTION_IN,
1355 remoteAddr,
1356 localAddr,
1357 0,
1358 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001359 0xffffffff,
1360 resourceId);
Benedict Wong47b528c2021-05-10 18:26:02 -07001361
1362 // Add a forwarding policy on the tunnel interface. In order to support forwarding
1363 // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
1364 //
1365 // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
1366 // forwarding will be blocked by default (as would be the case if this policy was
1367 // absent).
1368 //
1369 // This is necessary only on the tunnel interface, and not any the interface to
1370 // which traffic will be forwarded to.
Aaron Huang2617cf52021-11-29 16:31:32 +08001371 mNetd.ipSecAddSecurityPolicy(
Benedict Wong908d34e2021-04-15 11:59:16 -07001372 callerUid,
1373 selAddrFamily,
1374 IpSecManager.DIRECTION_FWD,
1375 remoteAddr,
1376 localAddr,
1377 0,
1378 ikey,
1379 0xffffffff,
1380 resourceId);
Benedict Wong8edc5572018-01-19 17:36:02 -08001381 }
1382
1383 userRecord.mTunnelInterfaceRecords.put(
1384 resourceId,
1385 new RefcountedResource<TunnelInterfaceRecord>(
1386 new TunnelInterfaceRecord(
1387 resourceId,
1388 intfName,
1389 underlyingNetwork,
1390 localAddr,
1391 remoteAddr,
1392 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001393 okey,
1394 resourceId),
Benedict Wong8edc5572018-01-19 17:36:02 -08001395 binder));
1396 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1397 } catch (RemoteException e) {
1398 // Release keys if we got an error.
1399 releaseNetId(ikey);
1400 releaseNetId(okey);
1401 throw e.rethrowFromSystemServer();
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001402 } catch (Throwable t) {
1403 // Release keys if we got an error.
1404 releaseNetId(ikey);
1405 releaseNetId(okey);
1406 throw t;
Benedict Wong8edc5572018-01-19 17:36:02 -08001407 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001408 }
1409
1410 /**
1411 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1412 * from multiple local IP addresses over the same tunnel.
1413 */
1414 @Override
Benedict Wong97c3c942018-03-01 18:53:07 -08001415 public synchronized void addAddressToTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001416 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001417 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001418 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1419
1420 // Get tunnelInterface record; if no such interface is found, will throw
1421 // IllegalArgumentException
1422 TunnelInterfaceRecord tunnelInterfaceInfo =
1423 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1424
Benedict Wong97c3c942018-03-01 18:53:07 -08001425 try {
1426 // We can assume general validity of the IP address, since we get them as a
1427 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001428 mNetd.interfaceAddAddress(
1429 tunnelInterfaceInfo.mInterfaceName,
1430 localAddr.getAddress().getHostAddress(),
1431 localAddr.getPrefixLength());
Benedict Wong97c3c942018-03-01 18:53:07 -08001432 } catch (RemoteException e) {
1433 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001434 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001435 }
1436
1437 /**
1438 * Remove a new local address from the tunnel interface. After removal, the address will no
1439 * longer be available to send from, or receive on.
1440 */
1441 @Override
1442 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001443 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001444 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001445
Nathan Harold65ef8432018-03-15 18:06:06 -07001446 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8bc90732018-01-18 18:31:45 -08001447 // Get tunnelInterface record; if no such interface is found, will throw
1448 // IllegalArgumentException
1449 TunnelInterfaceRecord tunnelInterfaceInfo =
1450 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1451
Benedict Wong97c3c942018-03-01 18:53:07 -08001452 try {
1453 // We can assume general validity of the IP address, since we get them as a
1454 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001455 mNetd.interfaceDelAddress(
Benedict Wong97c3c942018-03-01 18:53:07 -08001456 tunnelInterfaceInfo.mInterfaceName,
1457 localAddr.getAddress().getHostAddress(),
1458 localAddr.getPrefixLength());
1459 } catch (RemoteException e) {
1460 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001461 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001462 }
1463
Yan Yana2f3b492020-09-29 23:38:00 -07001464 /** Set TunnelInterface to use a specific underlying network. */
1465 @Override
1466 public synchronized void setNetworkForTunnelInterface(
1467 int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
1468 enforceTunnelFeatureAndPermissions(callingPackage);
1469 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1470
1471 final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1472
1473 // Get tunnelInterface record; if no such interface is found, will throw
1474 // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
1475 final TunnelInterfaceRecord tunnelInterfaceInfo =
1476 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1477
1478 final ConnectivityManager connectivityManager =
1479 mContext.getSystemService(ConnectivityManager.class);
1480 final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
1481 if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
1482 throw new IllegalArgumentException(
1483 "Underlying network cannot be the network being exposed by this tunnel");
1484 }
1485
1486 // It is meaningless to check if the network exists or is valid because the network might
1487 // disconnect at any time after it passes the check.
1488
1489 tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
1490 }
1491
Benedict Wong8bc90732018-01-18 18:31:45 -08001492 /**
1493 * Delete a TunnelInterface that has been been allocated by and registered with the system
1494 * server
1495 */
1496 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001497 public synchronized void deleteTunnelInterface(
1498 int resourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001499 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001500 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1501 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1502 }
1503
Benedict Wong70867e52017-11-06 20:49:10 -08001504 @VisibleForTesting
Nathan Harold5a19b952018-01-05 19:25:13 -08001505 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1506 IpSecAlgorithm auth = config.getAuthentication();
1507 IpSecAlgorithm crypt = config.getEncryption();
1508 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong70867e52017-11-06 20:49:10 -08001509
Nathan Harold5a19b952018-01-05 19:25:13 -08001510 // Validate the algorithm set
1511 Preconditions.checkArgument(
1512 aead != null || crypt != null || auth != null,
1513 "No Encryption or Authentication algorithms specified");
1514 Preconditions.checkArgument(
1515 auth == null || auth.isAuthentication(),
1516 "Unsupported algorithm for Authentication");
1517 Preconditions.checkArgument(
Benedict Wong70867e52017-11-06 20:49:10 -08001518 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harold5a19b952018-01-05 19:25:13 -08001519 Preconditions.checkArgument(
1520 aead == null || aead.isAead(),
1521 "Unsupported algorithm for Authenticated Encryption");
1522 Preconditions.checkArgument(
1523 aead == null || (auth == null && crypt == null),
1524 "Authenticated Encryption is mutually exclusive with other Authentication "
1525 + "or Encryption algorithms");
Benedict Wong70867e52017-11-06 20:49:10 -08001526 }
1527
evitayan43d93a02018-03-22 17:53:08 -07001528 private int getFamily(String inetAddress) {
1529 int family = AF_UNSPEC;
Serik Beketayev7f507332020-12-06 22:31:23 -08001530 InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
evitayan43d93a02018-03-22 17:53:08 -07001531 if (checkAddress instanceof Inet4Address) {
1532 family = AF_INET;
1533 } else if (checkAddress instanceof Inet6Address) {
1534 family = AF_INET6;
1535 }
1536 return family;
1537 }
1538
Nathan Harold031acb82017-03-07 13:23:36 -08001539 /**
Chiachang Wang2fea4a72020-08-12 12:23:59 +08001540 * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
Nathan Harold19b99d92017-08-23 13:46:33 -07001541 * IllegalArgumentException if they are not.
1542 */
1543 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001544 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1545
Nathan Harold19b99d92017-08-23 13:46:33 -07001546 switch (config.getEncapType()) {
1547 case IpSecTransform.ENCAP_NONE:
1548 break;
1549 case IpSecTransform.ENCAP_ESPINUDP:
1550 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong6855aee2017-11-16 15:27:22 -08001551 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1552 userRecord.mEncapSocketRecords.getResourceOrThrow(
1553 config.getEncapSocketResourceId());
Nathan Harold19b99d92017-08-23 13:46:33 -07001554
1555 int port = config.getEncapRemotePort();
1556 if (port <= 0 || port > 0xFFFF) {
1557 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1558 }
1559 break;
1560 default:
1561 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1562 }
1563
Nathan Harold5a19b952018-01-05 19:25:13 -08001564 validateAlgorithms(config);
Nathan Harold19b99d92017-08-23 13:46:33 -07001565
Nathan Harold5a19b952018-01-05 19:25:13 -08001566 // Retrieve SPI record; will throw IllegalArgumentException if not found
1567 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1568
Benedict Wong68aac2a2017-12-13 18:26:40 -08001569 // Check to ensure that SPI has not already been used.
1570 if (s.getOwnedByTransform()) {
1571 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1572 }
1573
Nathan Harold5a19b952018-01-05 19:25:13 -08001574 // If no remote address is supplied, then use one from the SPI.
1575 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1576 config.setDestinationAddress(s.getDestinationAddress());
1577 }
1578
1579 // All remote addresses must match
1580 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1581 throw new IllegalArgumentException("Mismatched remote addresseses.");
1582 }
1583
1584 // This check is technically redundant due to the chain of custody between the SPI and
1585 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1586 // the transform, this will prevent us from messing up.
1587 checkInetAddress(config.getDestinationAddress());
1588
1589 // Require a valid source address for all transforms.
1590 checkInetAddress(config.getSourceAddress());
1591
evitayan43d93a02018-03-22 17:53:08 -07001592 // Check to ensure source and destination have the same address family.
1593 String sourceAddress = config.getSourceAddress();
1594 String destinationAddress = config.getDestinationAddress();
1595 int sourceFamily = getFamily(sourceAddress);
1596 int destinationFamily = getFamily(destinationAddress);
1597 if (sourceFamily != destinationFamily) {
1598 throw new IllegalArgumentException(
1599 "Source address ("
1600 + sourceAddress
1601 + ") and destination address ("
1602 + destinationAddress
1603 + ") have different address families.");
1604 }
1605
1606 // Throw an error if UDP Encapsulation is not used in IPv4.
1607 if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
1608 throw new IllegalArgumentException(
1609 "UDP Encapsulation is not supported for this address family");
1610 }
1611
Nathan Harold5a19b952018-01-05 19:25:13 -08001612 switch (config.getMode()) {
1613 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold025aae12018-02-02 18:34:25 -08001614 break;
Nathan Harold5a19b952018-01-05 19:25:13 -08001615 case IpSecTransform.MODE_TUNNEL:
1616 break;
1617 default:
1618 throw new IllegalArgumentException(
1619 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harold19b99d92017-08-23 13:46:33 -07001620 }
Benedict Wong683441d2018-07-25 18:46:19 -07001621
1622 config.setMarkValue(0);
1623 config.setMarkMask(0);
Nathan Harold19b99d92017-08-23 13:46:33 -07001624 }
1625
Benedict Wong2b6a14e2018-09-13 16:45:12 -07001626 private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
Nathan Harold7c250ae2018-05-15 19:18:38 -07001627
Benedict Wonge9763752018-11-08 19:45:34 -08001628 private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1629 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1630 throw new UnsupportedOperationException(
1631 "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1632 }
1633
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001634 Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Benedict Wongc85b7b02019-11-12 22:31:51 -08001635
1636 // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
1637 // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
1638 // permission or is the System Server.
1639 if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
1640 TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1641 return;
Nathan Harold65ef8432018-03-15 18:06:06 -07001642 }
Benedict Wongc85b7b02019-11-12 22:31:51 -08001643 mContext.enforceCallingOrSelfPermission(
1644 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
Nathan Harold025aae12018-02-02 18:34:25 -08001645 }
1646
Benedict Wong8edc5572018-01-19 17:36:02 -08001647 private void createOrUpdateTransform(
1648 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1649 throws RemoteException {
1650
1651 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1652 if (encapType != IpSecTransform.ENCAP_NONE) {
1653 encapLocalPort = socketRecord.getPort();
1654 encapRemotePort = c.getEncapRemotePort();
1655 }
1656
1657 IpSecAlgorithm auth = c.getAuthentication();
1658 IpSecAlgorithm crypt = c.getEncryption();
1659 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1660
Benedict Wong778327e2018-03-15 19:41:41 -07001661 String cryptName;
1662 if (crypt == null) {
1663 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1664 } else {
1665 cryptName = crypt.getName();
1666 }
1667
Aaron Huang2617cf52021-11-29 16:31:32 +08001668 mNetd.ipSecAddSecurityAssociation(
1669 Binder.getCallingUid(),
1670 c.getMode(),
1671 c.getSourceAddress(),
1672 c.getDestinationAddress(),
1673 (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
1674 spiRecord.getSpi(),
1675 c.getMarkValue(),
1676 c.getMarkMask(),
1677 (auth != null) ? auth.getName() : "",
1678 (auth != null) ? auth.getKey() : new byte[] {},
1679 (auth != null) ? auth.getTruncationLengthBits() : 0,
1680 cryptName,
1681 (crypt != null) ? crypt.getKey() : new byte[] {},
1682 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1683 (authCrypt != null) ? authCrypt.getName() : "",
1684 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1685 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1686 encapType,
1687 encapLocalPort,
1688 encapRemotePort,
1689 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001690 }
1691
Nathan Harold19b99d92017-08-23 13:46:33 -07001692 /**
Benedict Wong8edc5572018-01-19 17:36:02 -08001693 * Create a IPsec transform, which represents a single security association in the kernel. The
1694 * transform will be cached by the system server and must be freed when no longer needed. It is
1695 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1696 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold031acb82017-03-07 13:23:36 -08001697 */
1698 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001699 public synchronized IpSecTransformResponse createTransform(
1700 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001701 Objects.requireNonNull(c);
Nathan Harold65ef8432018-03-15 18:06:06 -07001702 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
Benedict Wonge9763752018-11-08 19:45:34 -08001703 enforceTunnelFeatureAndPermissions(callingPackage);
Nathan Harold65ef8432018-03-15 18:06:06 -07001704 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001705 checkIpSecConfig(c);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001706 Objects.requireNonNull(binder, "Null Binder passed to createTransform");
Nathan Haroldfdafce22017-12-13 19:16:33 -08001707 final int resourceId = mNextResourceId++;
Benedict Wong6855aee2017-11-16 15:27:22 -08001708
1709 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wongcbd329b2017-12-13 17:16:53 -08001710 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong6855aee2017-11-16 15:27:22 -08001711
1712 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001713 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1714 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001715
Benedict Wong6855aee2017-11-16 15:27:22 -08001716 EncapSocketRecord socketRecord = null;
Benedict Wong8edc5572018-01-19 17:36:02 -08001717 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001718 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1719 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1720 c.getEncapSocketResourceId());
1721 dependencies.add(refcountedSocketRecord);
Benedict Wong6855aee2017-11-16 15:27:22 -08001722 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold80865392017-04-04 19:37:48 -07001723 }
1724
Nathan Harold5a19b952018-01-05 19:25:13 -08001725 RefcountedResource<SpiRecord> refcountedSpiRecord =
1726 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1727 dependencies.add(refcountedSpiRecord);
1728 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong6855aee2017-11-16 15:27:22 -08001729
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001730 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong8edc5572018-01-19 17:36:02 -08001731
1732 // SA was created successfully, time to construct a record and lock it away
Benedict Wong6855aee2017-11-16 15:27:22 -08001733 userRecord.mTransformRecords.put(
1734 resourceId,
1735 new RefcountedResource<TransformRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001736 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong6855aee2017-11-16 15:27:22 -08001737 binder,
1738 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold80865392017-04-04 19:37:48 -07001739 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001740 }
1741
1742 /**
1743 * Delete a transport mode transform that was previously allocated by + registered with the
1744 * system server. If this is called on an inactive (or non-existent) transform, it will not
1745 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1746 * other reasons.
1747 */
1748 @Override
Benedict Wong0fff56e2018-01-18 14:38:16 -08001749 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001750 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1751 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001752 }
1753
1754 /**
1755 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1756 * association as a correspondent policy to the provided socket
1757 */
1758 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001759 public synchronized void applyTransportModeTransform(
Nathan Harold5a19b952018-01-05 19:25:13 -08001760 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001761 int callingUid = Binder.getCallingUid();
1762 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harold5a19b952018-01-05 19:25:13 -08001763 checkDirection(direction);
Benedict Wong6855aee2017-11-16 15:27:22 -08001764 // Get transform record; if no transform is found, will throw IllegalArgumentException
1765 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001766
Nathan Harold80865392017-04-04 19:37:48 -07001767 // TODO: make this a function.
Aaron Huangfbae3082021-12-06 15:18:42 +08001768 if (info.mPid != getCallingPid() || info.mUid != callingUid) {
Nathan Harold80865392017-04-04 19:37:48 -07001769 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1770 }
1771
Benedict Wong8bc90732018-01-18 18:31:45 -08001772 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold80865392017-04-04 19:37:48 -07001773 IpSecConfig c = info.getConfig();
Benedict Wong8bc90732018-01-18 18:31:45 -08001774 Preconditions.checkArgument(
1775 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1776 "Transform mode was not Transport mode; cannot be applied to a socket");
1777
Aaron Huang2617cf52021-11-29 16:31:32 +08001778 mNetd.ipSecApplyTransportModeTransform(
1779 socket,
1780 callingUid,
1781 direction,
1782 c.getSourceAddress(),
1783 c.getDestinationAddress(),
1784 info.getSpiRecord().getSpi());
Nathan Harold031acb82017-03-07 13:23:36 -08001785 }
Nathan Harold80865392017-04-04 19:37:48 -07001786
Nathan Harold031acb82017-03-07 13:23:36 -08001787 /**
Nathan Harold5a19b952018-01-05 19:25:13 -08001788 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1789 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1790 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1791 * reserved for future improved input validation.
Nathan Harold031acb82017-03-07 13:23:36 -08001792 */
1793 @Override
Nathan Harold0d483b72018-01-17 01:00:20 -08001794 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1795 throws RemoteException {
Aaron Huang2617cf52021-11-29 16:31:32 +08001796 mNetd.ipSecRemoveTransportModeTransform(socket);
Nathan Harold031acb82017-03-07 13:23:36 -08001797 }
1798
Benedict Wong8bc90732018-01-18 18:31:45 -08001799 /**
1800 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1801 * security association as a correspondent policy to the provided interface
1802 */
1803 @Override
1804 public synchronized void applyTunnelModeTransform(
Nathan Harold65ef8432018-03-15 18:06:06 -07001805 int tunnelResourceId, int direction,
1806 int transformResourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001807 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001808 checkDirection(direction);
1809
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001810 int callingUid = Binder.getCallingUid();
1811 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001812
1813 // Get transform record; if no transform is found, will throw IllegalArgumentException
1814 TransformRecord transformInfo =
1815 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1816
1817 // Get tunnelInterface record; if no such interface is found, will throw
1818 // IllegalArgumentException
1819 TunnelInterfaceRecord tunnelInterfaceInfo =
1820 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1821
1822 // Get config and check that to-be-applied transform has the correct mode
1823 IpSecConfig c = transformInfo.getConfig();
1824 Preconditions.checkArgument(
1825 c.getMode() == IpSecTransform.MODE_TUNNEL,
1826 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1827
Benedict Wong8edc5572018-01-19 17:36:02 -08001828 EncapSocketRecord socketRecord = null;
1829 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1830 socketRecord =
1831 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1832 }
Benedict Wongec2e2e22019-10-03 11:09:00 -07001833 SpiRecord spiRecord = transformInfo.getSpiRecord();
Benedict Wong8edc5572018-01-19 17:36:02 -08001834
Benedict Wong8bc90732018-01-18 18:31:45 -08001835 int mark =
Benedict Wong38e52972018-05-07 20:06:44 -07001836 (direction == IpSecManager.DIRECTION_OUT)
1837 ? tunnelInterfaceInfo.getOkey()
Benedict Wong908d34e2021-04-15 11:59:16 -07001838 : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
Benedict Wong8bc90732018-01-18 18:31:45 -08001839
Benedict Wong8edc5572018-01-19 17:36:02 -08001840 try {
Benedict Wong5d749842018-09-06 11:31:25 -07001841 // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
1842 // SPI matching as part of the template resolution.
1843 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1844 c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
1845
Benedict Wong683441d2018-07-25 18:46:19 -07001846 // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
1847 // (and backporting) would allow us to narrow the mark space, and ensure that the SA
1848 // and SPs have matching marks (as VTI are meant to be built).
1849 // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
1850 // config matches the actual allocated resources in the kernel.
Benedict Wongc6fcedd2018-11-21 21:24:55 -08001851 // All SAs will have zero marks (from creation time), and any policy that matches the
1852 // same src/dst could match these SAs. Non-IpSecService governed processes that
1853 // establish floating policies with the same src/dst may result in undefined
1854 // behavior. This is generally limited to vendor code due to the permissions
1855 // (CAP_NET_ADMIN) required.
Benedict Wong683441d2018-07-25 18:46:19 -07001856 //
1857 // c.setMarkValue(mark);
1858 // c.setMarkMask(0xffffffff);
Benedict Wong8edc5572018-01-19 17:36:02 -08001859
1860 if (direction == IpSecManager.DIRECTION_OUT) {
1861 // Set output mark via underlying network (output only)
1862 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1863
Benedict Wong5d749842018-09-06 11:31:25 -07001864 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
1865 // but want to guarantee outbound packets are sent over the new SA.
Benedict Wongec2e2e22019-10-03 11:09:00 -07001866 spi = spiRecord.getSpi();
Benedict Wong5d749842018-09-06 11:31:25 -07001867 }
1868
1869 // Always update the policy with the relevant XFRM_IF_ID
1870 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +08001871 mNetd.ipSecUpdateSecurityPolicy(
1872 callingUid,
1873 selAddrFamily,
1874 direction,
1875 transformInfo.getConfig().getSourceAddress(),
1876 transformInfo.getConfig().getDestinationAddress(),
1877 spi, // If outbound, also add SPI to the policy.
1878 mark, // Must always set policy mark; ikey/okey for VTIs
1879 0xffffffff,
1880 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001881 }
1882
1883 // Update SA with tunnel mark (ikey or okey based on direction)
1884 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1885 } catch (ServiceSpecificException e) {
1886 if (e.errorCode == EINVAL) {
1887 throw new IllegalArgumentException(e.toString());
1888 } else {
1889 throw e;
1890 }
1891 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001892 }
1893
Nathan Harold031acb82017-03-07 13:23:36 -08001894 @Override
ludi89194d62017-05-22 10:52:23 -07001895 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001896 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludi89194d62017-05-22 10:52:23 -07001897
1898 pw.println("IpSecService dump:");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001899 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1900 pw.println();
ludi89194d62017-05-22 10:52:23 -07001901
Benedict Wong6855aee2017-11-16 15:27:22 -08001902 pw.println("mUserResourceTracker:");
1903 pw.println(mUserResourceTracker);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001904 }
1905}