blob: dfc57c136f08e0c5dc4f018b4bdff532927f7bb8 [file] [log] [blame]
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static android.Manifest.permission.DUMP;
Nathan Harold031acb82017-03-07 13:23:36 -080020import static android.net.IpSecManager.INVALID_RESOURCE_ID;
Nathan Harold80865392017-04-04 19:37:48 -070021import static android.system.OsConstants.AF_INET;
evitayan43d93a02018-03-22 17:53:08 -070022import static android.system.OsConstants.AF_INET6;
23import static android.system.OsConstants.AF_UNSPEC;
manojboopathiac927fe2017-11-30 17:11:49 -080024import static android.system.OsConstants.EINVAL;
Nathan Harold80865392017-04-04 19:37:48 -070025import static android.system.OsConstants.IPPROTO_UDP;
26import static android.system.OsConstants.SOCK_DGRAM;
Benedict Wong683441d2018-07-25 18:46:19 -070027
Nathan Harold65ef8432018-03-15 18:06:06 -070028import android.annotation.NonNull;
29import android.app.AppOpsManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080030import android.content.Context;
Benedict Wong4aac3e92019-02-25 12:33:22 -080031import android.content.pm.PackageManager;
Yan Yana2f3b492020-09-29 23:38:00 -070032import android.net.ConnectivityManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080033import android.net.IIpSecService;
34import android.net.INetd;
Serik Beketayev7f507332020-12-06 22:31:23 -080035import android.net.InetAddresses;
Nathan Harold031acb82017-03-07 13:23:36 -080036import android.net.IpSecAlgorithm;
37import android.net.IpSecConfig;
38import android.net.IpSecManager;
Nathan Harold80865392017-04-04 19:37:48 -070039import android.net.IpSecSpiResponse;
Nathan Harold031acb82017-03-07 13:23:36 -080040import android.net.IpSecTransform;
Nathan Harold80865392017-04-04 19:37:48 -070041import android.net.IpSecTransformResponse;
Benedict Wong8bc90732018-01-18 18:31:45 -080042import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold80865392017-04-04 19:37:48 -070043import android.net.IpSecUdpEncapResponse;
Benedict Wong97c3c942018-03-01 18:53:07 -080044import android.net.LinkAddress;
Yan Yana2f3b492020-09-29 23:38:00 -070045import android.net.LinkProperties;
Benedict Wong8bc90732018-01-18 18:31:45 -080046import android.net.Network;
Benedict Wong083faee2017-12-03 19:42:36 -080047import android.net.TrafficStats;
Remi NGUYEN VAN5db09192019-01-29 15:38:52 +090048import android.net.util.NetdService;
Nathan Harold031acb82017-03-07 13:23:36 -080049import android.os.Binder;
Nathan Harold031acb82017-03-07 13:23:36 -080050import android.os.IBinder;
51import android.os.ParcelFileDescriptor;
Benedict Wong908d34e2021-04-15 11:59:16 -070052import android.os.Process;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080053import android.os.RemoteException;
Nathan Harold031acb82017-03-07 13:23:36 -080054import android.os.ServiceSpecificException;
Nathan Harold80865392017-04-04 19:37:48 -070055import android.system.ErrnoException;
56import android.system.Os;
57import android.system.OsConstants;
Nathan Harold19b99d92017-08-23 13:46:33 -070058import android.text.TextUtils;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080059import android.util.Log;
lucaslin7eb76592021-03-11 17:39:49 +080060import android.util.Range;
Nathan Harold031acb82017-03-07 13:23:36 -080061import android.util.SparseArray;
Benedict Wong8bc90732018-01-18 18:31:45 -080062import android.util.SparseBooleanArray;
Nathan Harold19b99d92017-08-23 13:46:33 -070063
Nathan Harold031acb82017-03-07 13:23:36 -080064import com.android.internal.annotations.GuardedBy;
ludi5e623ea2017-05-12 09:15:00 -070065import com.android.internal.annotations.VisibleForTesting;
Benedict Wong70867e52017-11-06 20:49:10 -080066import com.android.internal.util.Preconditions;
paulhu00e34562021-10-26 09:00:50 +000067import com.android.net.module.util.BinderUtils;
lucaslinff6fe7b2021-02-03 23:59:45 +080068import com.android.net.module.util.NetdUtils;
Benedict Wong908d34e2021-04-15 11:59:16 -070069import com.android.net.module.util.PermissionUtils;
Nathan Harold19b99d92017-08-23 13:46:33 -070070
Benedict Wong683441d2018-07-25 18:46:19 -070071import libcore.io.IoUtils;
72
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080073import java.io.FileDescriptor;
Nathan Harold80865392017-04-04 19:37:48 -070074import java.io.IOException;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080075import java.io.PrintWriter;
evitayan43d93a02018-03-22 17:53:08 -070076import java.net.Inet4Address;
77import java.net.Inet6Address;
Nathan Harold80865392017-04-04 19:37:48 -070078import java.net.InetAddress;
79import java.net.InetSocketAddress;
80import java.net.UnknownHostException;
Benedict Wong02346822017-10-26 19:41:43 -070081import java.util.ArrayList;
82import java.util.List;
Benedict Wong529e8aa2020-02-11 23:49:36 -080083import java.util.Objects;
Nathan Harold19b99d92017-08-23 13:46:33 -070084
Benedict Wong02346822017-10-26 19:41:43 -070085/**
86 * A service to manage multiple clients that want to access the IpSec API. The service is
87 * responsible for maintaining a list of clients and managing the resources (and related quotas)
88 * that each of them own.
89 *
90 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
91 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
92 * thread is ever running at a time.
93 *
94 * @hide
95 */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080096public class IpSecService extends IIpSecService.Stub {
97 private static final String TAG = "IpSecService";
98 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Nathan Harold80865392017-04-04 19:37:48 -070099
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800100 private static final String NETD_SERVICE_NAME = "netd";
Benedict Wong38e52972018-05-07 20:06:44 -0700101 private static final int[] ADDRESS_FAMILIES =
102 new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800103
ludi5e623ea2017-05-12 09:15:00 -0700104 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold80865392017-04-04 19:37:48 -0700105 private static final InetAddress INADDR_ANY;
106
Benedict Wong29c30772019-03-20 09:44:09 -0700107 @VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
108
Nathan Harold80865392017-04-04 19:37:48 -0700109 static {
110 try {
111 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
112 } catch (UnknownHostException e) {
113 throw new RuntimeException(e);
114 }
115 }
116
117 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
118 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
119
120 /* Binder context for this service */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800121 private final Context mContext;
122
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
ludi5e623ea2017-05-12 09:15:00 -0700132 interface IpSecServiceConfiguration {
133 INetd getNetdInstance() throws RemoteException;
134
Aaron Huangfbae3082021-12-06 15:18:42 +0800135 IpSecServiceConfiguration GETSRVINSTANCE =
ludi5e623ea2017-05-12 09:15:00 -0700136 new IpSecServiceConfiguration() {
137 @Override
138 public INetd getNetdInstance() throws RemoteException {
139 final INetd netd = NetdService.getInstance();
140 if (netd == null) {
141 throw new RemoteException("Failed to Get Netd Instance");
142 }
143 return netd;
144 }
145 };
146 }
147
148 private final IpSecServiceConfiguration mSrvConfig;
Benedict Wong083faee2017-12-03 19:42:36 -0800149 final UidFdTagger mUidFdTagger;
ludi5e623ea2017-05-12 09:15:00 -0700150
Benedict Wong02346822017-10-26 19:41:43 -0700151 /**
152 * Interface for user-reference and kernel-resource cleanup.
153 *
154 * <p>This interface must be implemented for a resource to be reference counted.
155 */
156 @VisibleForTesting
157 public interface IResource {
158 /**
159 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
160 * objects dependent on it.
161 *
162 * <p>Implementations of this method are expected to remove references to the IResource
163 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
164 * the resource is considered invalid for user access or allocation or use in other
165 * resources.
166 *
167 * <p>References to the IResource object may be held by other RefcountedResource objects,
Benedict Wongcbd329b2017-12-13 17:16:53 -0800168 * and as such, the underlying resources and quota may not be cleaned up.
Benedict Wong02346822017-10-26 19:41:43 -0700169 */
170 void invalidate() throws RemoteException;
171
172 /**
173 * Releases underlying resources and related quotas.
174 *
175 * <p>Implementations of this method are expected to remove all system resources that are
176 * tracked by the IResource object. Due to other RefcountedResource objects potentially
Benedict Wong6855aee2017-11-16 15:27:22 -0800177 * having references to the IResource object, freeUnderlyingResources may not always be
Benedict Wong02346822017-10-26 19:41:43 -0700178 * called from releaseIfUnreferencedRecursively().
179 */
180 void freeUnderlyingResources() throws RemoteException;
181 }
182
183 /**
184 * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
185 *
186 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
187 * RefcountedResource object creates an explicit reference that must be freed by calling
188 * userRelease(). Additionally, adding this object as a child of another RefcountedResource
189 * object will add an implicit reference.
190 *
191 * <p>Resources are cleaned up when all references, both implicit and explicit, are released
192 * (ie, when userRelease() is called and when all parents have called releaseReference() on this
193 * object.)
194 */
195 @VisibleForTesting
196 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000197 private final T mResource;
198 private final List<RefcountedResource> mChildren;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800199 int mRefCount = 1; // starts at 1 for user's reference.
Junyu Lai75eabfe2019-04-26 01:38:04 +0000200 IBinder mBinder;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800201
Junyu Lai75eabfe2019-04-26 01:38:04 +0000202 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
Benedict Wong02346822017-10-26 19:41:43 -0700203 synchronized (IpSecService.this) {
204 this.mResource = resource;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000205 this.mChildren = new ArrayList<>(children.length);
Benedict Wong02346822017-10-26 19:41:43 -0700206 this.mBinder = binder;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000207
Benedict Wong02346822017-10-26 19:41:43 -0700208 for (RefcountedResource child : children) {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000209 mChildren.add(child);
Benedict Wong02346822017-10-26 19:41:43 -0700210 child.mRefCount++;
211 }
212
213 try {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000214 mBinder.linkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700215 } catch (RemoteException e) {
216 binderDied();
Benedict Wong04738f52019-02-28 20:28:48 -0800217 e.rethrowFromSystemServer();
Benedict Wong02346822017-10-26 19:41:43 -0700218 }
219 }
220 }
221
222 /**
223 * If the Binder object dies, this function is called to free the system resources that are
Benedict Wong6855aee2017-11-16 15:27:22 -0800224 * being tracked by this record and to subsequently release this record for garbage
Benedict Wong02346822017-10-26 19:41:43 -0700225 * collection
226 */
227 @Override
228 public void binderDied() {
229 synchronized (IpSecService.this) {
230 try {
231 userRelease();
232 } catch (Exception e) {
233 Log.e(TAG, "Failed to release resource: " + e);
234 }
235 }
236 }
237
238 public T getResource() {
239 return mResource;
240 }
241
242 /**
243 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
244 * arrays)
245 *
246 * <p>If this method has been previously called, the RefcountedResource's binder field will
247 * be null, and the method will return without performing the cleanup a second time.
248 *
249 * <p>Note that calling this function does not imply that kernel resources will be freed at
250 * this time, or that the related quota will be returned. Such actions will only be
251 * performed upon the reference count reaching zero.
252 */
253 @GuardedBy("IpSecService.this")
254 public void userRelease() throws RemoteException {
255 // Prevent users from putting reference counts into a bad state by calling
256 // userRelease() multiple times.
257 if (mBinder == null) {
258 return;
259 }
260
Junyu Lai75eabfe2019-04-26 01:38:04 +0000261 mBinder.unlinkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700262 mBinder = null;
263
264 mResource.invalidate();
Junyu Lai75eabfe2019-04-26 01:38:04 +0000265
Benedict Wong02346822017-10-26 19:41:43 -0700266 releaseReference();
267 }
268
269 /**
270 * Removes a reference to this resource. If the resultant reference count is zero, the
271 * underlying resources are freed, and references to all child resources are also dropped
272 * recursively (resulting in them freeing their resources and children, etcetera)
273 *
274 * <p>This method also sets the reference count to an invalid value (-1) to signify that it
275 * has been fully released. Any subsequent calls to this method will result in an
276 * IllegalStateException being thrown due to resource already having been previously
277 * released
278 */
279 @VisibleForTesting
280 @GuardedBy("IpSecService.this")
281 public void releaseReference() throws RemoteException {
282 mRefCount--;
283
284 if (mRefCount > 0) {
285 return;
286 } else if (mRefCount < 0) {
287 throw new IllegalStateException(
288 "Invalid operation - resource has already been released.");
289 }
290
291 // Cleanup own resources
292 mResource.freeUnderlyingResources();
293
294 // Cleanup child resources as needed
295 for (RefcountedResource<? extends IResource> child : mChildren) {
296 child.releaseReference();
297 }
298
299 // Enforce that resource cleanup can only be called once
300 // By decrementing the refcount (from 0 to -1), the next call will throw an
301 // IllegalStateException - it has already been released fully.
302 mRefCount--;
303 }
304
305 @Override
306 public String toString() {
307 return new StringBuilder()
308 .append("{mResource=")
309 .append(mResource)
310 .append(", mRefCount=")
311 .append(mRefCount)
312 .append(", mChildren=")
313 .append(mChildren)
314 .append("}")
315 .toString();
316 }
317 }
318
Benedict Wongcbd329b2017-12-13 17:16:53 -0800319 /**
320 * Very simple counting class that looks much like a counting semaphore
321 *
322 * <p>This class is not thread-safe, and expects that that users of this class will ensure
323 * synchronization and thread safety by holding the IpSecService.this instance lock.
324 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800325 @VisibleForTesting
326 static class ResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700327 private final int mMax;
328 int mCurrent;
329
330 ResourceTracker(int max) {
331 mMax = max;
332 mCurrent = 0;
333 }
334
Benedict Wong6855aee2017-11-16 15:27:22 -0800335 boolean isAvailable() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700336 return (mCurrent < mMax);
337 }
338
Benedict Wong6855aee2017-11-16 15:27:22 -0800339 void take() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700340 if (!isAvailable()) {
341 Log.wtf(TAG, "Too many resources allocated!");
342 }
343 mCurrent++;
344 }
345
Benedict Wong6855aee2017-11-16 15:27:22 -0800346 void give() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700347 if (mCurrent <= 0) {
348 Log.wtf(TAG, "We've released this resource too many times");
349 }
350 mCurrent--;
351 }
ludi529fdec2017-08-10 15:44:40 -0700352
353 @Override
354 public String toString() {
355 return new StringBuilder()
356 .append("{mCurrent=")
357 .append(mCurrent)
358 .append(", mMax=")
359 .append(mMax)
360 .append("}")
361 .toString();
362 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700363 }
364
Benedict Wong6855aee2017-11-16 15:27:22 -0800365 @VisibleForTesting
366 static final class UserRecord {
Benedict Wong6855aee2017-11-16 15:27:22 -0800367 /* Maximum number of each type of resource that a single UID may possess */
Benedict Wong7bcf9c22020-02-11 23:36:42 -0800368
369 // Up to 4 active VPNs/IWLAN with potential soft handover.
370 public static final int MAX_NUM_TUNNEL_INTERFACES = 8;
371 public static final int MAX_NUM_ENCAP_SOCKETS = 16;
372
373 // SPIs and Transforms are both cheap, and are 1:1 correlated.
374 public static final int MAX_NUM_TRANSFORMS = 64;
375 public static final int MAX_NUM_SPIS = 64;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700376
Benedict Wongcbd329b2017-12-13 17:16:53 -0800377 /**
378 * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
379 * and explicit (user) reference management.
380 *
381 * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
382 *
383 * <p>Resources are removed from this array when the user releases their explicit reference
384 * by calling one of the releaseResource() methods.
385 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800386 final RefcountedResourceArray<SpiRecord> mSpiRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800387 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800388 final RefcountedResourceArray<TransformRecord> mTransformRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800389 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800390 final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800391 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
Benedict Wong8bc90732018-01-18 18:31:45 -0800392 final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
393 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
Benedict Wongcbd329b2017-12-13 17:16:53 -0800394
395 /**
396 * Trackers for quotas for each of the OwnedResource types.
397 *
398 * <p>These trackers are separate from the resource arrays, since they are incremented and
399 * decremented at different points in time. Specifically, quota is only returned upon final
400 * resource deallocation (after all explicit and implicit references are released). Note
401 * that it is possible that calls to releaseResource() will not return the used quota if
402 * there are other resources that depend on (are parents of) the resource being released.
403 */
404 final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
405 final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
Benedict Wong6855aee2017-11-16 15:27:22 -0800406 final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
Benedict Wong8bc90732018-01-18 18:31:45 -0800407 final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
Benedict Wong6855aee2017-11-16 15:27:22 -0800408
409 void removeSpiRecord(int resourceId) {
410 mSpiRecords.remove(resourceId);
Nathan Harold6e4681c2017-04-24 16:16:34 -0700411 }
412
Benedict Wong6855aee2017-11-16 15:27:22 -0800413 void removeTransformRecord(int resourceId) {
414 mTransformRecords.remove(resourceId);
415 }
416
Benedict Wong8bc90732018-01-18 18:31:45 -0800417 void removeTunnelInterfaceRecord(int resourceId) {
418 mTunnelInterfaceRecords.remove(resourceId);
419 }
420
Benedict Wong6855aee2017-11-16 15:27:22 -0800421 void removeEncapSocketRecord(int resourceId) {
422 mEncapSocketRecords.remove(resourceId);
423 }
424
425 @Override
426 public String toString() {
427 return new StringBuilder()
428 .append("{mSpiQuotaTracker=")
429 .append(mSpiQuotaTracker)
430 .append(", mTransformQuotaTracker=")
431 .append(mTransformQuotaTracker)
432 .append(", mSocketQuotaTracker=")
433 .append(mSocketQuotaTracker)
Benedict Wong76603702018-01-24 15:31:39 -0800434 .append(", mTunnelQuotaTracker=")
435 .append(mTunnelQuotaTracker)
Benedict Wong6855aee2017-11-16 15:27:22 -0800436 .append(", mSpiRecords=")
437 .append(mSpiRecords)
438 .append(", mTransformRecords=")
439 .append(mTransformRecords)
440 .append(", mEncapSocketRecords=")
441 .append(mEncapSocketRecords)
Benedict Wong76603702018-01-24 15:31:39 -0800442 .append(", mTunnelInterfaceRecords=")
443 .append(mTunnelInterfaceRecords)
Benedict Wong6855aee2017-11-16 15:27:22 -0800444 .append("}")
445 .toString();
446 }
447 }
448
Benedict Wongcbd329b2017-12-13 17:16:53 -0800449 /**
450 * This class is not thread-safe, and expects that that users of this class will ensure
451 * synchronization and thread safety by holding the IpSecService.this instance lock.
452 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800453 @VisibleForTesting
454 static final class UserResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700455 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
456
Benedict Wongcbd329b2017-12-13 17:16:53 -0800457 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
Benedict Wong6855aee2017-11-16 15:27:22 -0800458 public UserRecord getUserRecord(int uid) {
459 checkCallerUid(uid);
460
Nathan Harold6e4681c2017-04-24 16:16:34 -0700461 UserRecord r = mUserRecords.get(uid);
462 if (r == null) {
463 r = new UserRecord();
464 mUserRecords.put(uid, r);
465 }
466 return r;
467 }
ludi529fdec2017-08-10 15:44:40 -0700468
Benedict Wong6855aee2017-11-16 15:27:22 -0800469 /** Safety method; guards against access of other user's UserRecords */
470 private void checkCallerUid(int uid) {
Benedict Wong908d34e2021-04-15 11:59:16 -0700471 if (uid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800472 throw new SecurityException("Attempted access of unowned resources");
473 }
474 }
475
ludi529fdec2017-08-10 15:44:40 -0700476 @Override
477 public String toString() {
478 return mUserRecords.toString();
479 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700480 }
481
Benedict Wong6855aee2017-11-16 15:27:22 -0800482 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700483
Nathan Harold80865392017-04-04 19:37:48 -0700484 /**
Benedict Wongcbd329b2017-12-13 17:16:53 -0800485 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
Benedict Wong6855aee2017-11-16 15:27:22 -0800486 * resources. It relies on a provided resourceId that should uniquely identify the kernel
487 * resource. To use this class, the user should implement the invalidate() and
488 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
Benedict Wongcbd329b2017-12-13 17:16:53 -0800489 * tracking arrays and kernel resources, respectively.
490 *
491 * <p>This class associates kernel resources with the UID that owns and controls them.
Nathan Harold80865392017-04-04 19:37:48 -0700492 */
Benedict Wongcbd329b2017-12-13 17:16:53 -0800493 private abstract class OwnedResourceRecord implements IResource {
Aaron Huangfbae3082021-12-06 15:18:42 +0800494 final int mPid;
495 final int mUid;
Benedict Wong6855aee2017-11-16 15:27:22 -0800496 protected final int mResourceId;
Nathan Harold031acb82017-03-07 13:23:36 -0800497
Benedict Wongcbd329b2017-12-13 17:16:53 -0800498 OwnedResourceRecord(int resourceId) {
Nathan Harold031acb82017-03-07 13:23:36 -0800499 super();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700500 if (resourceId == INVALID_RESOURCE_ID) {
501 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
502 }
Nathan Harold80865392017-04-04 19:37:48 -0700503 mResourceId = resourceId;
Aaron Huangfbae3082021-12-06 15:18:42 +0800504 mPid = Binder.getCallingPid();
505 mUid = Binder.getCallingUid();
Nathan Harold031acb82017-03-07 13:23:36 -0800506
Nathan Harold6e4681c2017-04-24 16:16:34 -0700507 getResourceTracker().take();
Nathan Harold031acb82017-03-07 13:23:36 -0800508 }
509
Benedict Wong6855aee2017-11-16 15:27:22 -0800510 @Override
511 public abstract void invalidate() throws RemoteException;
512
513 /** Convenience method; retrieves the user resource record for the stored UID. */
514 protected UserRecord getUserRecord() {
Aaron Huangfbae3082021-12-06 15:18:42 +0800515 return mUserResourceTracker.getUserRecord(mUid);
Nathan Harold80865392017-04-04 19:37:48 -0700516 }
517
Benedict Wong6855aee2017-11-16 15:27:22 -0800518 @Override
519 public abstract void freeUnderlyingResources() throws RemoteException;
ludi89194d62017-05-22 10:52:23 -0700520
Nathan Harold6e4681c2017-04-24 16:16:34 -0700521 /** Get the resource tracker for this resource */
522 protected abstract ResourceTracker getResourceTracker();
523
ludi89194d62017-05-22 10:52:23 -0700524 @Override
525 public String toString() {
526 return new StringBuilder()
527 .append("{mResourceId=")
528 .append(mResourceId)
529 .append(", pid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800530 .append(mPid)
ludi89194d62017-05-22 10:52:23 -0700531 .append(", uid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800532 .append(mUid)
ludi89194d62017-05-22 10:52:23 -0700533 .append("}")
534 .toString();
535 }
Nathan Harold031acb82017-03-07 13:23:36 -0800536 };
537
Nathan Harold80865392017-04-04 19:37:48 -0700538 /**
Benedict Wong6855aee2017-11-16 15:27:22 -0800539 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
540 *
541 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
542 * if a key is not found during a retrieval process.
Nathan Harold80865392017-04-04 19:37:48 -0700543 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800544 static class RefcountedResourceArray<T extends IResource> {
545 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
546 private final String mTypeName;
Nathan Harold031acb82017-03-07 13:23:36 -0800547
Aaron Huangfbae3082021-12-06 15:18:42 +0800548 RefcountedResourceArray(String typeName) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800549 this.mTypeName = typeName;
Nathan Harold80865392017-04-04 19:37:48 -0700550 }
551
Benedict Wong6855aee2017-11-16 15:27:22 -0800552 /**
553 * Accessor method to get inner resource object.
554 *
555 * @throws IllegalArgumentException if no resource with provided key is found.
556 */
557 T getResourceOrThrow(int key) {
558 return getRefcountedResourceOrThrow(key).getResource();
559 }
560
561 /**
562 * Accessor method to get reference counting wrapper.
563 *
564 * @throws IllegalArgumentException if no resource with provided key is found.
565 */
566 RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
567 RefcountedResource<T> resource = mArray.get(key);
568 if (resource == null) {
569 throw new IllegalArgumentException(
570 String.format("No such %s found for given id: %d", mTypeName, key));
571 }
572
573 return resource;
574 }
575
576 void put(int key, RefcountedResource<T> obj) {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +0000577 Objects.requireNonNull(obj, "Null resources cannot be added");
Nathan Harold80865392017-04-04 19:37:48 -0700578 mArray.put(key, obj);
579 }
580
581 void remove(int key) {
582 mArray.remove(key);
583 }
ludi89194d62017-05-22 10:52:23 -0700584
585 @Override
586 public String toString() {
587 return mArray.toString();
588 }
Nathan Harold80865392017-04-04 19:37:48 -0700589 }
590
Benedict Wongcbd329b2017-12-13 17:16:53 -0800591 /**
592 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
593 * created, the SpiRecord that originally tracked the SAs will reliquish the
594 * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
595 */
596 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700597 private final IpSecConfig mConfig;
Nathan Harold5a19b952018-01-05 19:25:13 -0800598 private final SpiRecord mSpi;
Benedict Wong6855aee2017-11-16 15:27:22 -0800599 private final EncapSocketRecord mSocket;
Nathan Harold80865392017-04-04 19:37:48 -0700600
601 TransformRecord(
Nathan Harold5a19b952018-01-05 19:25:13 -0800602 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800603 super(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -0800604 mConfig = config;
Nathan Harold5a19b952018-01-05 19:25:13 -0800605 mSpi = spi;
Nathan Harold80865392017-04-04 19:37:48 -0700606 mSocket = socket;
Benedict Wong68aac2a2017-12-13 18:26:40 -0800607
608 spi.setOwnedByTransform();
Nathan Harold031acb82017-03-07 13:23:36 -0800609 }
610
611 public IpSecConfig getConfig() {
612 return mConfig;
613 }
614
Nathan Harold5a19b952018-01-05 19:25:13 -0800615 public SpiRecord getSpiRecord() {
616 return mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700617 }
618
Benedict Wong8edc5572018-01-19 17:36:02 -0800619 public EncapSocketRecord getSocketRecord() {
620 return mSocket;
621 }
622
Nathan Harold80865392017-04-04 19:37:48 -0700623 /** always guarded by IpSecService#this */
Nathan Harold031acb82017-03-07 13:23:36 -0800624 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800625 public void freeUnderlyingResources() {
Nathan Harold5a19b952018-01-05 19:25:13 -0800626 int spi = mSpi.getSpi();
627 try {
628 mSrvConfig
629 .getNetdInstance()
630 .ipSecDeleteSecurityAssociation(
Aaron Huangfbae3082021-12-06 15:18:42 +0800631 mUid,
Nathan Harold5a19b952018-01-05 19:25:13 -0800632 mConfig.getSourceAddress(),
633 mConfig.getDestinationAddress(),
Di Lu3fef7042018-01-11 11:35:25 -0800634 spi,
635 mConfig.getMarkValue(),
Benedict Wong5d749842018-09-06 11:31:25 -0700636 mConfig.getMarkMask(),
637 mConfig.getXfrmInterfaceId());
Benedict Wong97c3c942018-03-01 18:53:07 -0800638 } catch (RemoteException | ServiceSpecificException e) {
639 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800640 }
Nathan Harold031acb82017-03-07 13:23:36 -0800641
Benedict Wong6855aee2017-11-16 15:27:22 -0800642 getResourceTracker().give();
Nathan Harold031acb82017-03-07 13:23:36 -0800643 }
ludi89194d62017-05-22 10:52:23 -0700644
Benedict Wong6855aee2017-11-16 15:27:22 -0800645 @Override
646 public void invalidate() throws RemoteException {
647 getUserRecord().removeTransformRecord(mResourceId);
648 }
649
650 @Override
Nathan Harold6e4681c2017-04-24 16:16:34 -0700651 protected ResourceTracker getResourceTracker() {
Benedict Wong6855aee2017-11-16 15:27:22 -0800652 return getUserRecord().mTransformQuotaTracker;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700653 }
654
ludi89194d62017-05-22 10:52:23 -0700655 @Override
656 public String toString() {
657 StringBuilder strBuilder = new StringBuilder();
658 strBuilder
659 .append("{super=")
660 .append(super.toString())
661 .append(", mSocket=")
662 .append(mSocket)
Nathan Harold5a19b952018-01-05 19:25:13 -0800663 .append(", mSpi.mResourceId=")
664 .append(mSpi.mResourceId)
ludi89194d62017-05-22 10:52:23 -0700665 .append(", mConfig=")
666 .append(mConfig)
667 .append("}");
668 return strBuilder.toString();
669 }
Nathan Harold031acb82017-03-07 13:23:36 -0800670 }
671
Benedict Wongcbd329b2017-12-13 17:16:53 -0800672 /**
673 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
674 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
675 * object
676 */
677 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harold5a19b952018-01-05 19:25:13 -0800678 private final String mSourceAddress;
679 private final String mDestinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800680 private int mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700681
682 private boolean mOwnedByTransform = false;
Nathan Harold031acb82017-03-07 13:23:36 -0800683
Nathan Harold5a19b952018-01-05 19:25:13 -0800684 SpiRecord(int resourceId, String sourceAddress, 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) {
696 mSrvConfig
697 .getNetdInstance()
698 .ipSecDeleteSecurityAssociation(
Aaron Huangfbae3082021-12-06 15:18:42 +0800699 mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
Benedict Wong5d749842018-09-06 11:31:25 -0700700 0 /* mask */, 0 /* if_id */);
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800701 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800702 } catch (ServiceSpecificException | RemoteException e) {
703 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800704 }
Nathan Harold80865392017-04-04 19:37:48 -0700705
706 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -0800707
Benedict Wong6855aee2017-11-16 15:27:22 -0800708 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700709 }
710
Nathan Harold80865392017-04-04 19:37:48 -0700711 public int getSpi() {
712 return mSpi;
713 }
714
Nathan Harold5a19b952018-01-05 19:25:13 -0800715 public String getDestinationAddress() {
716 return mDestinationAddress;
717 }
718
Nathan Harold80865392017-04-04 19:37:48 -0700719 public void setOwnedByTransform() {
720 if (mOwnedByTransform) {
721 // Programming error
Andreas Gampeafb01e22017-07-11 10:25:09 -0700722 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold80865392017-04-04 19:37:48 -0700723 }
724
725 mOwnedByTransform = true;
Nathan Harold031acb82017-03-07 13:23:36 -0800726 }
ludi89194d62017-05-22 10:52:23 -0700727
Benedict Wong68aac2a2017-12-13 18:26:40 -0800728 public boolean getOwnedByTransform() {
729 return mOwnedByTransform;
730 }
731
ludi89194d62017-05-22 10:52:23 -0700732 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800733 public void invalidate() throws RemoteException {
734 getUserRecord().removeSpiRecord(mResourceId);
735 }
736
737 @Override
738 protected ResourceTracker getResourceTracker() {
739 return getUserRecord().mSpiQuotaTracker;
740 }
741
742 @Override
ludi89194d62017-05-22 10:52:23 -0700743 public String toString() {
744 StringBuilder strBuilder = new StringBuilder();
745 strBuilder
746 .append("{super=")
747 .append(super.toString())
748 .append(", mSpi=")
749 .append(mSpi)
Nathan Harold5a19b952018-01-05 19:25:13 -0800750 .append(", mSourceAddress=")
751 .append(mSourceAddress)
752 .append(", mDestinationAddress=")
753 .append(mDestinationAddress)
ludi89194d62017-05-22 10:52:23 -0700754 .append(", mOwnedByTransform=")
755 .append(mOwnedByTransform)
756 .append("}");
757 return strBuilder.toString();
758 }
Nathan Harold031acb82017-03-07 13:23:36 -0800759 }
760
Benedict Wong8bc90732018-01-18 18:31:45 -0800761 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
lucaslin7eb76592021-03-11 17:39:49 +0800762 final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
763 private int mNextTunnelNetId = mNetIdRange.getLower();
Benedict Wong8bc90732018-01-18 18:31:45 -0800764
765 /**
766 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
767 *
768 * <p>This method should only be called from Binder threads. Do not call this from within the
769 * system server as it will crash the system on failure.
770 *
771 * @return an integer key within the netId range, if successful
772 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
773 */
774 @VisibleForTesting
775 int reserveNetId() {
lucaslin7eb76592021-03-11 17:39:49 +0800776 final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
Benedict Wong8bc90732018-01-18 18:31:45 -0800777 synchronized (mTunnelNetIds) {
lucaslin7eb76592021-03-11 17:39:49 +0800778 for (int i = 0; i < range; i++) {
779 final int netId = mNextTunnelNetId;
780 if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
781 mNextTunnelNetId = mNetIdRange.getLower();
782 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800783 if (!mTunnelNetIds.get(netId)) {
784 mTunnelNetIds.put(netId, true);
785 return netId;
786 }
787 }
788 }
789 throw new IllegalStateException("No free netIds to allocate");
790 }
791
792 @VisibleForTesting
793 void releaseNetId(int netId) {
794 synchronized (mTunnelNetIds) {
795 mTunnelNetIds.delete(netId);
796 }
797 }
798
Yan Yana2f3b492020-09-29 23:38:00 -0700799 /**
800 * Tracks an tunnel interface, and manages cleanup paths.
801 *
802 * <p>This class is not thread-safe, and expects that that users of this class will ensure
803 * synchronization and thread safety by holding the IpSecService.this instance lock
804 */
805 @VisibleForTesting
806 final class TunnelInterfaceRecord extends OwnedResourceRecord {
Benedict Wong8bc90732018-01-18 18:31:45 -0800807 private final String mInterfaceName;
Benedict Wong8bc90732018-01-18 18:31:45 -0800808
809 // outer addresses
810 private final String mLocalAddress;
811 private final String mRemoteAddress;
812
813 private final int mIkey;
814 private final int mOkey;
815
Benedict Wong5d749842018-09-06 11:31:25 -0700816 private final int mIfId;
817
Yan Yana2f3b492020-09-29 23:38:00 -0700818 private Network mUnderlyingNetwork;
819
Benedict Wong8bc90732018-01-18 18:31:45 -0800820 TunnelInterfaceRecord(
821 int resourceId,
822 String interfaceName,
823 Network underlyingNetwork,
824 String localAddr,
825 String remoteAddr,
826 int ikey,
Benedict Wong5d749842018-09-06 11:31:25 -0700827 int okey,
828 int intfId) {
Benedict Wong8bc90732018-01-18 18:31:45 -0800829 super(resourceId);
830
831 mInterfaceName = interfaceName;
832 mUnderlyingNetwork = underlyingNetwork;
833 mLocalAddress = localAddr;
834 mRemoteAddress = remoteAddr;
835 mIkey = ikey;
836 mOkey = okey;
Benedict Wong5d749842018-09-06 11:31:25 -0700837 mIfId = intfId;
Benedict Wong8bc90732018-01-18 18:31:45 -0800838 }
839
840 /** always guarded by IpSecService#this */
841 @Override
842 public void freeUnderlyingResources() {
Benedict Wong8edc5572018-01-19 17:36:02 -0800843 // Calls to netd
Benedict Wong8bc90732018-01-18 18:31:45 -0800844 // Teardown VTI
845 // Delete global policies
Benedict Wong8edc5572018-01-19 17:36:02 -0800846 try {
Benedict Wong38e52972018-05-07 20:06:44 -0700847 final INetd netd = mSrvConfig.getNetdInstance();
Benedict Wong5d749842018-09-06 11:31:25 -0700848 netd.ipSecRemoveTunnelInterface(mInterfaceName);
Benedict Wong8edc5572018-01-19 17:36:02 -0800849
Benedict Wong38e52972018-05-07 20:06:44 -0700850 for (int selAddrFamily : ADDRESS_FAMILIES) {
851 netd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800852 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700853 selAddrFamily,
854 IpSecManager.DIRECTION_OUT,
855 mOkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700856 0xffffffff,
857 mIfId);
Benedict Wong38e52972018-05-07 20:06:44 -0700858 netd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800859 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700860 selAddrFamily,
861 IpSecManager.DIRECTION_IN,
862 mIkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700863 0xffffffff,
864 mIfId);
Benedict Wong8edc5572018-01-19 17:36:02 -0800865 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800866 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong8edc5572018-01-19 17:36:02 -0800867 Log.e(
868 TAG,
869 "Failed to delete VTI with interface name: "
870 + mInterfaceName
871 + " and id: "
Benedict Wong97c3c942018-03-01 18:53:07 -0800872 + mResourceId, e);
Benedict Wong8edc5572018-01-19 17:36:02 -0800873 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800874
875 getResourceTracker().give();
876 releaseNetId(mIkey);
877 releaseNetId(mOkey);
878 }
879
Yan Yana2f3b492020-09-29 23:38:00 -0700880 @GuardedBy("IpSecService.this")
881 public void setUnderlyingNetwork(Network underlyingNetwork) {
882 // When #applyTunnelModeTransform is called, this new underlying network will be used to
883 // update the output mark of the input transform.
884 mUnderlyingNetwork = underlyingNetwork;
Benedict Wong8bc90732018-01-18 18:31:45 -0800885 }
886
Yan Yana2f3b492020-09-29 23:38:00 -0700887 @GuardedBy("IpSecService.this")
Benedict Wong8bc90732018-01-18 18:31:45 -0800888 public Network getUnderlyingNetwork() {
889 return mUnderlyingNetwork;
890 }
891
Yan Yana2f3b492020-09-29 23:38:00 -0700892 public String getInterfaceName() {
893 return mInterfaceName;
894 }
895
Benedict Wong8bc90732018-01-18 18:31:45 -0800896 /** Returns the local, outer address for the tunnelInterface */
897 public String getLocalAddress() {
898 return mLocalAddress;
899 }
900
901 /** Returns the remote, outer address for the tunnelInterface */
902 public String getRemoteAddress() {
903 return mRemoteAddress;
904 }
905
906 public int getIkey() {
907 return mIkey;
908 }
909
910 public int getOkey() {
911 return mOkey;
912 }
913
Benedict Wong5d749842018-09-06 11:31:25 -0700914 public int getIfId() {
915 return mIfId;
916 }
917
Benedict Wong8bc90732018-01-18 18:31:45 -0800918 @Override
919 protected ResourceTracker getResourceTracker() {
920 return getUserRecord().mTunnelQuotaTracker;
921 }
922
923 @Override
924 public void invalidate() {
925 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
926 }
927
928 @Override
929 public String toString() {
930 return new StringBuilder()
931 .append("{super=")
932 .append(super.toString())
933 .append(", mInterfaceName=")
934 .append(mInterfaceName)
935 .append(", mUnderlyingNetwork=")
936 .append(mUnderlyingNetwork)
937 .append(", mLocalAddress=")
938 .append(mLocalAddress)
939 .append(", mRemoteAddress=")
940 .append(mRemoteAddress)
941 .append(", mIkey=")
942 .append(mIkey)
943 .append(", mOkey=")
944 .append(mOkey)
945 .append("}")
946 .toString();
947 }
948 }
949
Benedict Wongcbd329b2017-12-13 17:16:53 -0800950 /**
951 * Tracks a UDP encap socket, and manages cleanup paths
952 *
953 * <p>While this class does not manage non-kernel resources, race conditions around socket
954 * binding require that the service creates the encap socket, binds it and applies the socket
955 * policy before handing it to a user.
956 */
957 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700958 private FileDescriptor mSocket;
959 private final int mPort;
Nathan Harold031acb82017-03-07 13:23:36 -0800960
Benedict Wong6855aee2017-11-16 15:27:22 -0800961 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
962 super(resourceId);
Nathan Harold80865392017-04-04 19:37:48 -0700963 mSocket = socket;
964 mPort = port;
965 }
966
967 /** always guarded by IpSecService#this */
968 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800969 public void freeUnderlyingResources() {
Nathan Harold80865392017-04-04 19:37:48 -0700970 Log.d(TAG, "Closing port " + mPort);
971 IoUtils.closeQuietly(mSocket);
972 mSocket = null;
Nathan Harold80865392017-04-04 19:37:48 -0700973
Benedict Wong6855aee2017-11-16 15:27:22 -0800974 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700975 }
976
Nathan Harold80865392017-04-04 19:37:48 -0700977 public int getPort() {
978 return mPort;
979 }
980
Benedict Wonga386e372018-03-27 16:55:48 -0700981 public FileDescriptor getFileDescriptor() {
Nathan Harold80865392017-04-04 19:37:48 -0700982 return mSocket;
983 }
ludi89194d62017-05-22 10:52:23 -0700984
985 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800986 protected ResourceTracker getResourceTracker() {
987 return getUserRecord().mSocketQuotaTracker;
988 }
989
990 @Override
991 public void invalidate() {
992 getUserRecord().removeEncapSocketRecord(mResourceId);
993 }
994
995 @Override
ludi89194d62017-05-22 10:52:23 -0700996 public String toString() {
997 return new StringBuilder()
998 .append("{super=")
999 .append(super.toString())
1000 .append(", mSocket=")
1001 .append(mSocket)
1002 .append(", mPort=")
1003 .append(mPort)
1004 .append("}")
1005 .toString();
1006 }
Nathan Harold80865392017-04-04 19:37:48 -07001007 }
Nathan Harold031acb82017-03-07 13:23:36 -08001008
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001009 /**
1010 * Constructs a new IpSecService instance
1011 *
1012 * @param context Binder context for this service
1013 */
lucaslind66e6082021-01-18 13:06:39 +08001014 private IpSecService(Context context) {
1015 this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001016 }
1017
lucaslind66e6082021-01-18 13:06:39 +08001018 static IpSecService create(Context context)
Benedict Wong529e8aa2020-02-11 23:49:36 -08001019 throws InterruptedException {
lucaslind66e6082021-01-18 13:06:39 +08001020 final IpSecService service = new IpSecService(context);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001021 service.connectNativeNetdService();
1022 return service;
1023 }
1024
Nathan Harold65ef8432018-03-15 18:06:06 -07001025 @NonNull
1026 private AppOpsManager getAppOpsManager() {
1027 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Aaron Huangfbae3082021-12-06 15:18:42 +08001028 if (appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
Nathan Harold65ef8432018-03-15 18:06:06 -07001029 return appOps;
1030 }
1031
ludi5e623ea2017-05-12 09:15:00 -07001032 /** @hide */
1033 @VisibleForTesting
lucaslind66e6082021-01-18 13:06:39 +08001034 public IpSecService(Context context, IpSecServiceConfiguration config) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001035 this(
1036 context,
1037 config,
1038 (fd, uid) -> {
1039 try {
1040 TrafficStats.setThreadStatsUid(uid);
1041 TrafficStats.tagFileDescriptor(fd);
1042 } finally {
1043 TrafficStats.clearThreadStatsUid();
1044 }
1045 });
Benedict Wong083faee2017-12-03 19:42:36 -08001046 }
1047
1048 /** @hide */
1049 @VisibleForTesting
lucaslind66e6082021-01-18 13:06:39 +08001050 public IpSecService(Context context, IpSecServiceConfiguration config,
1051 UidFdTagger uidFdTagger) {
ludi5e623ea2017-05-12 09:15:00 -07001052 mContext = context;
1053 mSrvConfig = config;
Benedict Wong083faee2017-12-03 19:42:36 -08001054 mUidFdTagger = uidFdTagger;
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
1066 private void connectNativeNetdService() {
1067 // Avoid blocking the system server to do this
Nathan Harold12accd02017-07-17 14:01:53 -07001068 new Thread() {
1069 @Override
1070 public void run() {
1071 synchronized (IpSecService.this) {
ludi5e623ea2017-05-12 09:15:00 -07001072 NetdService.get(NETD_FETCH_TIMEOUT_MS);
Nathan Harold12accd02017-07-17 14:01:53 -07001073 }
1074 }
1075 }.start();
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001076 }
1077
Nathan Harold80865392017-04-04 19:37:48 -07001078 synchronized boolean isNetdAlive() {
1079 try {
ludi5e623ea2017-05-12 09:15:00 -07001080 final INetd netd = mSrvConfig.getNetdInstance();
Nathan Harold80865392017-04-04 19:37:48 -07001081 if (netd == null) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001082 return false;
1083 }
Nathan Harold80865392017-04-04 19:37:48 -07001084 return netd.isAlive();
1085 } catch (RemoteException re) {
1086 return false;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001087 }
1088 }
1089
Nathan Harold19b99d92017-08-23 13:46:33 -07001090 /**
1091 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1092 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1093 */
1094 private static void checkInetAddress(String inetAddress) {
1095 if (TextUtils.isEmpty(inetAddress)) {
1096 throw new IllegalArgumentException("Unspecified address");
1097 }
1098
Serik Beketayev7f507332020-12-06 22:31:23 -08001099 InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
Nathan Harold19b99d92017-08-23 13:46:33 -07001100
1101 if (checkAddr.isAnyLocalAddress()) {
1102 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1103 }
1104 }
1105
1106 /**
1107 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1108 * DIRECTION_IN or DIRECTION_OUT
1109 */
Benedict Wong908d34e2021-04-15 11:59:16 -07001110 private void checkDirection(int direction) {
Nathan Harold19b99d92017-08-23 13:46:33 -07001111 switch (direction) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001112 case IpSecManager.DIRECTION_OUT:
1113 case IpSecManager.DIRECTION_IN:
Nathan Harold19b99d92017-08-23 13:46:33 -07001114 return;
Benedict Wong908d34e2021-04-15 11:59:16 -07001115 case IpSecManager.DIRECTION_FWD:
Benedict Wong47b528c2021-05-10 18:26:02 -07001116 // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
Benedict Wong908d34e2021-04-15 11:59:16 -07001117 PermissionUtils.enforceNetworkStackPermission(mContext);
1118 return;
Nathan Harold19b99d92017-08-23 13:46:33 -07001119 }
1120 throw new IllegalArgumentException("Invalid Direction: " + direction);
1121 }
1122
Nathan Harold031acb82017-03-07 13:23:36 -08001123 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri20e96c52017-11-16 10:58:01 -08001124 @Override
1125 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harold5a19b952018-01-05 19:25:13 -08001126 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1127 checkInetAddress(destinationAddress);
Nathan Harold1b88f0e2018-03-28 08:52:51 -07001128 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1129 if (requestedSpi > 0 && requestedSpi < 256) {
1130 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1131 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001132 Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harold19b99d92017-08-23 13:46:33 -07001133
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001134 int callingUid = Binder.getCallingUid();
1135 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001136 final int resourceId = mNextResourceId++;
Nathan Harold031acb82017-03-07 13:23:36 -08001137
1138 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -08001139 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001140 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001141 return new IpSecSpiResponse(
Nathan Harold19b99d92017-08-23 13:46:33 -07001142 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold6e4681c2017-04-24 16:16:34 -07001143 }
Nathan Harold5a19b952018-01-05 19:25:13 -08001144
Nathan Harold031acb82017-03-07 13:23:36 -08001145 spi =
ludi5e623ea2017-05-12 09:15:00 -07001146 mSrvConfig
1147 .getNetdInstance()
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001148 .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold031acb82017-03-07 13:23:36 -08001149 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong6855aee2017-11-16 15:27:22 -08001150 userRecord.mSpiRecords.put(
Nathan Harold80865392017-04-04 19:37:48 -07001151 resourceId,
Benedict Wong6855aee2017-11-16 15:27:22 -08001152 new RefcountedResource<SpiRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001153 new SpiRecord(resourceId, "", destinationAddress, spi), binder));
Nathan Harold031acb82017-03-07 13:23:36 -08001154 } catch (ServiceSpecificException e) {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001155 if (e.errorCode == OsConstants.ENOENT) {
1156 return new IpSecSpiResponse(
1157 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1158 }
1159 throw e;
Nathan Harold031acb82017-03-07 13:23:36 -08001160 } catch (RemoteException e) {
1161 throw e.rethrowFromSystemServer();
1162 }
Nathan Harold80865392017-04-04 19:37:48 -07001163 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1164 }
1165
1166 /* This method should only be called from Binder threads. Do not call this from
1167 * within the system server as it will crash the system on failure.
1168 */
Benedict Wong6855aee2017-11-16 15:27:22 -08001169 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold80865392017-04-04 19:37:48 -07001170 throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001171 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold031acb82017-03-07 13:23:36 -08001172 }
1173
1174 /** Release a previously allocated SPI that has been registered with the system server */
1175 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001176 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1177 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1178 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001179 }
1180
1181 /**
1182 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1183 * be unbound.
1184 *
1185 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1186 * a random open port and then bind by number, this function creates a temp socket, binds to a
1187 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1188 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1189 * FileHandle.
1190 *
1191 * <p>The loop in this function handles the inherent race window between un-binding to a port
1192 * and re-binding, during which the system could *technically* hand that port out to someone
1193 * else.
1194 */
Benedict Wongc423cc82017-10-10 20:44:28 -07001195 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold80865392017-04-04 19:37:48 -07001196 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1197 try {
1198 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1199 Os.bind(probeSocket, INADDR_ANY, 0);
1200 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1201 Os.close(probeSocket);
1202 Log.v(TAG, "Binding to port " + port);
1203 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongc423cc82017-10-10 20:44:28 -07001204 return port;
Nathan Harold80865392017-04-04 19:37:48 -07001205 } catch (ErrnoException e) {
1206 // Someone miraculously claimed the port just after we closed probeSocket.
1207 if (e.errno == OsConstants.EADDRINUSE) {
1208 continue;
1209 }
1210 throw e.rethrowAsIOException();
1211 }
1212 }
1213 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1214 }
Nathan Harold031acb82017-03-07 13:23:36 -08001215
1216 /**
Benedict Wong083faee2017-12-03 19:42:36 -08001217 * Functional interface to do traffic tagging of given sockets to UIDs.
1218 *
1219 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1220 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1221 *
1222 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1223 * methods that cannot be easily mocked/tested.
1224 */
1225 @VisibleForTesting
1226 public interface UidFdTagger {
1227 /**
1228 * Sets socket tag to assign all traffic to the provided UID.
1229 *
1230 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1231 * should be accounted to the UID of the unprivileged application.
1232 */
Aaron Huangfbae3082021-12-06 15:18:42 +08001233 void tag(FileDescriptor fd, int uid) throws IOException;
Benedict Wong083faee2017-12-03 19:42:36 -08001234 }
1235
1236 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001237 * Open a socket via the system server and bind it to the specified port (random if port=0).
1238 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1239 * cache the socket and a record of its owner so that it can and must be freed when no longer
1240 * needed.
1241 */
1242 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001243 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1244 throws RemoteException {
1245 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1246 throw new IllegalArgumentException(
1247 "Specified port number must be a valid non-reserved UDP port");
1248 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001249 Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
Nathan Harold19b99d92017-08-23 13:46:33 -07001250
Benedict Wong083faee2017-12-03 19:42:36 -08001251 int callingUid = Binder.getCallingUid();
1252 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001253 final int resourceId = mNextResourceId++;
Nathan Harold80865392017-04-04 19:37:48 -07001254 FileDescriptor sockFd = null;
1255 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001256 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001257 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1258 }
1259
Nathan Harold80865392017-04-04 19:37:48 -07001260 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Benedict Wong083faee2017-12-03 19:42:36 -08001261 mUidFdTagger.tag(sockFd, callingUid);
Nathan Harold80865392017-04-04 19:37:48 -07001262
Nathan Harold80865392017-04-04 19:37:48 -07001263 // This code is common to both the unspecified and specified port cases
1264 Os.setsockoptInt(
1265 sockFd,
1266 OsConstants.IPPROTO_UDP,
1267 OsConstants.UDP_ENCAP,
1268 OsConstants.UDP_ENCAP_ESPINUDP);
1269
Luke Huangd913fb42018-11-23 12:01:41 +08001270 mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(
1271 new ParcelFileDescriptor(sockFd), callingUid);
Benedict Wong17687442017-12-06 21:56:35 -08001272 if (port != 0) {
1273 Log.v(TAG, "Binding to port " + port);
1274 Os.bind(sockFd, INADDR_ANY, port);
1275 } else {
1276 port = bindToRandomPort(sockFd);
1277 }
1278
Benedict Wong6855aee2017-11-16 15:27:22 -08001279 userRecord.mEncapSocketRecords.put(
1280 resourceId,
1281 new RefcountedResource<EncapSocketRecord>(
1282 new EncapSocketRecord(resourceId, sockFd, port), binder));
Nathan Harold80865392017-04-04 19:37:48 -07001283 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
1284 } catch (IOException | ErrnoException e) {
1285 IoUtils.closeQuietly(sockFd);
1286 }
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)
Benedict Wong38e52972018-05-07 20:06:44 -07001333 final INetd netd = mSrvConfig.getNetdInstance();
Benedict Wong5d749842018-09-06 11:31:25 -07001334 netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001335
paulhu00e34562021-10-26 09:00:50 +00001336 BinderUtils.withCleanCallingIdentity(() -> {
lucaslinff6fe7b2021-02-03 23:59:45 +08001337 NetdUtils.setInterfaceUp(netd, intfName);
Benedict Wong529e8aa2020-02-11 23:49:36 -08001338 });
1339
Benedict Wong38e52972018-05-07 20:06:44 -07001340 for (int selAddrFamily : ADDRESS_FAMILIES) {
1341 // Always send down correct local/remote addresses for template.
1342 netd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001343 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001344 selAddrFamily,
1345 IpSecManager.DIRECTION_OUT,
1346 localAddr,
1347 remoteAddr,
1348 0,
1349 okey,
Benedict Wong5d749842018-09-06 11:31:25 -07001350 0xffffffff,
1351 resourceId);
Benedict Wong38e52972018-05-07 20:06:44 -07001352 netd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001353 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001354 selAddrFamily,
1355 IpSecManager.DIRECTION_IN,
1356 remoteAddr,
1357 localAddr,
1358 0,
1359 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001360 0xffffffff,
1361 resourceId);
Benedict Wong47b528c2021-05-10 18:26:02 -07001362
1363 // Add a forwarding policy on the tunnel interface. In order to support forwarding
1364 // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
1365 //
1366 // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
1367 // forwarding will be blocked by default (as would be the case if this policy was
1368 // absent).
1369 //
1370 // This is necessary only on the tunnel interface, and not any the interface to
1371 // which traffic will be forwarded to.
Benedict Wong908d34e2021-04-15 11:59:16 -07001372 netd.ipSecAddSecurityPolicy(
1373 callerUid,
1374 selAddrFamily,
1375 IpSecManager.DIRECTION_FWD,
1376 remoteAddr,
1377 localAddr,
1378 0,
1379 ikey,
1380 0xffffffff,
1381 resourceId);
Benedict Wong8edc5572018-01-19 17:36:02 -08001382 }
1383
1384 userRecord.mTunnelInterfaceRecords.put(
1385 resourceId,
1386 new RefcountedResource<TunnelInterfaceRecord>(
1387 new TunnelInterfaceRecord(
1388 resourceId,
1389 intfName,
1390 underlyingNetwork,
1391 localAddr,
1392 remoteAddr,
1393 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001394 okey,
1395 resourceId),
Benedict Wong8edc5572018-01-19 17:36:02 -08001396 binder));
1397 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1398 } catch (RemoteException e) {
1399 // Release keys if we got an error.
1400 releaseNetId(ikey);
1401 releaseNetId(okey);
1402 throw e.rethrowFromSystemServer();
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001403 } catch (Throwable t) {
1404 // Release keys if we got an error.
1405 releaseNetId(ikey);
1406 releaseNetId(okey);
1407 throw t;
Benedict Wong8edc5572018-01-19 17:36:02 -08001408 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001409 }
1410
1411 /**
1412 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1413 * from multiple local IP addresses over the same tunnel.
1414 */
1415 @Override
Benedict Wong97c3c942018-03-01 18:53:07 -08001416 public synchronized void addAddressToTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001417 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001418 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001419 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1420
1421 // Get tunnelInterface record; if no such interface is found, will throw
1422 // IllegalArgumentException
1423 TunnelInterfaceRecord tunnelInterfaceInfo =
1424 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1425
Benedict Wong97c3c942018-03-01 18:53:07 -08001426 try {
1427 // We can assume general validity of the IP address, since we get them as a
1428 // LinkAddress, which does some validation.
1429 mSrvConfig
1430 .getNetdInstance()
1431 .interfaceAddAddress(
1432 tunnelInterfaceInfo.mInterfaceName,
1433 localAddr.getAddress().getHostAddress(),
1434 localAddr.getPrefixLength());
1435 } catch (RemoteException e) {
1436 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001437 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001438 }
1439
1440 /**
1441 * Remove a new local address from the tunnel interface. After removal, the address will no
1442 * longer be available to send from, or receive on.
1443 */
1444 @Override
1445 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001446 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001447 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001448
Nathan Harold65ef8432018-03-15 18:06:06 -07001449 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8bc90732018-01-18 18:31:45 -08001450 // Get tunnelInterface record; if no such interface is found, will throw
1451 // IllegalArgumentException
1452 TunnelInterfaceRecord tunnelInterfaceInfo =
1453 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1454
Benedict Wong97c3c942018-03-01 18:53:07 -08001455 try {
1456 // We can assume general validity of the IP address, since we get them as a
1457 // LinkAddress, which does some validation.
1458 mSrvConfig
1459 .getNetdInstance()
1460 .interfaceDelAddress(
1461 tunnelInterfaceInfo.mInterfaceName,
1462 localAddr.getAddress().getHostAddress(),
1463 localAddr.getPrefixLength());
1464 } catch (RemoteException e) {
1465 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001466 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001467 }
1468
Yan Yana2f3b492020-09-29 23:38:00 -07001469 /** Set TunnelInterface to use a specific underlying network. */
1470 @Override
1471 public synchronized void setNetworkForTunnelInterface(
1472 int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
1473 enforceTunnelFeatureAndPermissions(callingPackage);
1474 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1475
1476 final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1477
1478 // Get tunnelInterface record; if no such interface is found, will throw
1479 // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
1480 final TunnelInterfaceRecord tunnelInterfaceInfo =
1481 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1482
1483 final ConnectivityManager connectivityManager =
1484 mContext.getSystemService(ConnectivityManager.class);
1485 final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
1486 if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
1487 throw new IllegalArgumentException(
1488 "Underlying network cannot be the network being exposed by this tunnel");
1489 }
1490
1491 // It is meaningless to check if the network exists or is valid because the network might
1492 // disconnect at any time after it passes the check.
1493
1494 tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
1495 }
1496
Benedict Wong8bc90732018-01-18 18:31:45 -08001497 /**
1498 * Delete a TunnelInterface that has been been allocated by and registered with the system
1499 * server
1500 */
1501 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001502 public synchronized void deleteTunnelInterface(
1503 int resourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001504 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001505 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1506 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1507 }
1508
Benedict Wong70867e52017-11-06 20:49:10 -08001509 @VisibleForTesting
Nathan Harold5a19b952018-01-05 19:25:13 -08001510 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1511 IpSecAlgorithm auth = config.getAuthentication();
1512 IpSecAlgorithm crypt = config.getEncryption();
1513 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong70867e52017-11-06 20:49:10 -08001514
Nathan Harold5a19b952018-01-05 19:25:13 -08001515 // Validate the algorithm set
1516 Preconditions.checkArgument(
1517 aead != null || crypt != null || auth != null,
1518 "No Encryption or Authentication algorithms specified");
1519 Preconditions.checkArgument(
1520 auth == null || auth.isAuthentication(),
1521 "Unsupported algorithm for Authentication");
1522 Preconditions.checkArgument(
Benedict Wong70867e52017-11-06 20:49:10 -08001523 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harold5a19b952018-01-05 19:25:13 -08001524 Preconditions.checkArgument(
1525 aead == null || aead.isAead(),
1526 "Unsupported algorithm for Authenticated Encryption");
1527 Preconditions.checkArgument(
1528 aead == null || (auth == null && crypt == null),
1529 "Authenticated Encryption is mutually exclusive with other Authentication "
1530 + "or Encryption algorithms");
Benedict Wong70867e52017-11-06 20:49:10 -08001531 }
1532
evitayan43d93a02018-03-22 17:53:08 -07001533 private int getFamily(String inetAddress) {
1534 int family = AF_UNSPEC;
Serik Beketayev7f507332020-12-06 22:31:23 -08001535 InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
evitayan43d93a02018-03-22 17:53:08 -07001536 if (checkAddress instanceof Inet4Address) {
1537 family = AF_INET;
1538 } else if (checkAddress instanceof Inet6Address) {
1539 family = AF_INET6;
1540 }
1541 return family;
1542 }
1543
Nathan Harold031acb82017-03-07 13:23:36 -08001544 /**
Chiachang Wang2fea4a72020-08-12 12:23:59 +08001545 * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
Nathan Harold19b99d92017-08-23 13:46:33 -07001546 * IllegalArgumentException if they are not.
1547 */
1548 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001549 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1550
Nathan Harold19b99d92017-08-23 13:46:33 -07001551 switch (config.getEncapType()) {
1552 case IpSecTransform.ENCAP_NONE:
1553 break;
1554 case IpSecTransform.ENCAP_ESPINUDP:
1555 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong6855aee2017-11-16 15:27:22 -08001556 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1557 userRecord.mEncapSocketRecords.getResourceOrThrow(
1558 config.getEncapSocketResourceId());
Nathan Harold19b99d92017-08-23 13:46:33 -07001559
1560 int port = config.getEncapRemotePort();
1561 if (port <= 0 || port > 0xFFFF) {
1562 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1563 }
1564 break;
1565 default:
1566 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1567 }
1568
Nathan Harold5a19b952018-01-05 19:25:13 -08001569 validateAlgorithms(config);
Nathan Harold19b99d92017-08-23 13:46:33 -07001570
Nathan Harold5a19b952018-01-05 19:25:13 -08001571 // Retrieve SPI record; will throw IllegalArgumentException if not found
1572 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1573
Benedict Wong68aac2a2017-12-13 18:26:40 -08001574 // Check to ensure that SPI has not already been used.
1575 if (s.getOwnedByTransform()) {
1576 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1577 }
1578
Nathan Harold5a19b952018-01-05 19:25:13 -08001579 // If no remote address is supplied, then use one from the SPI.
1580 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1581 config.setDestinationAddress(s.getDestinationAddress());
1582 }
1583
1584 // All remote addresses must match
1585 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1586 throw new IllegalArgumentException("Mismatched remote addresseses.");
1587 }
1588
1589 // This check is technically redundant due to the chain of custody between the SPI and
1590 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1591 // the transform, this will prevent us from messing up.
1592 checkInetAddress(config.getDestinationAddress());
1593
1594 // Require a valid source address for all transforms.
1595 checkInetAddress(config.getSourceAddress());
1596
evitayan43d93a02018-03-22 17:53:08 -07001597 // Check to ensure source and destination have the same address family.
1598 String sourceAddress = config.getSourceAddress();
1599 String destinationAddress = config.getDestinationAddress();
1600 int sourceFamily = getFamily(sourceAddress);
1601 int destinationFamily = getFamily(destinationAddress);
1602 if (sourceFamily != destinationFamily) {
1603 throw new IllegalArgumentException(
1604 "Source address ("
1605 + sourceAddress
1606 + ") and destination address ("
1607 + destinationAddress
1608 + ") have different address families.");
1609 }
1610
1611 // Throw an error if UDP Encapsulation is not used in IPv4.
1612 if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
1613 throw new IllegalArgumentException(
1614 "UDP Encapsulation is not supported for this address family");
1615 }
1616
Nathan Harold5a19b952018-01-05 19:25:13 -08001617 switch (config.getMode()) {
1618 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold025aae12018-02-02 18:34:25 -08001619 break;
Nathan Harold5a19b952018-01-05 19:25:13 -08001620 case IpSecTransform.MODE_TUNNEL:
1621 break;
1622 default:
1623 throw new IllegalArgumentException(
1624 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harold19b99d92017-08-23 13:46:33 -07001625 }
Benedict Wong683441d2018-07-25 18:46:19 -07001626
1627 config.setMarkValue(0);
1628 config.setMarkMask(0);
Nathan Harold19b99d92017-08-23 13:46:33 -07001629 }
1630
Benedict Wong2b6a14e2018-09-13 16:45:12 -07001631 private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
Nathan Harold7c250ae2018-05-15 19:18:38 -07001632
Benedict Wonge9763752018-11-08 19:45:34 -08001633 private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1634 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1635 throw new UnsupportedOperationException(
1636 "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1637 }
1638
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001639 Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Benedict Wongc85b7b02019-11-12 22:31:51 -08001640
1641 // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
1642 // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
1643 // permission or is the System Server.
1644 if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
1645 TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1646 return;
Nathan Harold65ef8432018-03-15 18:06:06 -07001647 }
Benedict Wongc85b7b02019-11-12 22:31:51 -08001648 mContext.enforceCallingOrSelfPermission(
1649 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
Nathan Harold025aae12018-02-02 18:34:25 -08001650 }
1651
Benedict Wong8edc5572018-01-19 17:36:02 -08001652 private void createOrUpdateTransform(
1653 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1654 throws RemoteException {
1655
1656 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1657 if (encapType != IpSecTransform.ENCAP_NONE) {
1658 encapLocalPort = socketRecord.getPort();
1659 encapRemotePort = c.getEncapRemotePort();
1660 }
1661
1662 IpSecAlgorithm auth = c.getAuthentication();
1663 IpSecAlgorithm crypt = c.getEncryption();
1664 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1665
Benedict Wong778327e2018-03-15 19:41:41 -07001666 String cryptName;
1667 if (crypt == null) {
1668 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1669 } else {
1670 cryptName = crypt.getName();
1671 }
1672
Benedict Wong8edc5572018-01-19 17:36:02 -08001673 mSrvConfig
1674 .getNetdInstance()
1675 .ipSecAddSecurityAssociation(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001676 Binder.getCallingUid(),
Benedict Wong8edc5572018-01-19 17:36:02 -08001677 c.getMode(),
1678 c.getSourceAddress(),
1679 c.getDestinationAddress(),
lucaslin20966d92021-03-19 16:06:33 +08001680 (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
Benedict Wong8edc5572018-01-19 17:36:02 -08001681 spiRecord.getSpi(),
1682 c.getMarkValue(),
1683 c.getMarkMask(),
1684 (auth != null) ? auth.getName() : "",
1685 (auth != null) ? auth.getKey() : new byte[] {},
1686 (auth != null) ? auth.getTruncationLengthBits() : 0,
Benedict Wong778327e2018-03-15 19:41:41 -07001687 cryptName,
Benedict Wong8edc5572018-01-19 17:36:02 -08001688 (crypt != null) ? crypt.getKey() : new byte[] {},
1689 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1690 (authCrypt != null) ? authCrypt.getName() : "",
1691 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1692 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1693 encapType,
1694 encapLocalPort,
Benedict Wong5d749842018-09-06 11:31:25 -07001695 encapRemotePort,
1696 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001697 }
1698
Nathan Harold19b99d92017-08-23 13:46:33 -07001699 /**
Benedict Wong8edc5572018-01-19 17:36:02 -08001700 * Create a IPsec transform, which represents a single security association in the kernel. The
1701 * transform will be cached by the system server and must be freed when no longer needed. It is
1702 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1703 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold031acb82017-03-07 13:23:36 -08001704 */
1705 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001706 public synchronized IpSecTransformResponse createTransform(
1707 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001708 Objects.requireNonNull(c);
Nathan Harold65ef8432018-03-15 18:06:06 -07001709 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
Benedict Wonge9763752018-11-08 19:45:34 -08001710 enforceTunnelFeatureAndPermissions(callingPackage);
Nathan Harold65ef8432018-03-15 18:06:06 -07001711 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001712 checkIpSecConfig(c);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001713 Objects.requireNonNull(binder, "Null Binder passed to createTransform");
Nathan Haroldfdafce22017-12-13 19:16:33 -08001714 final int resourceId = mNextResourceId++;
Benedict Wong6855aee2017-11-16 15:27:22 -08001715
1716 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wongcbd329b2017-12-13 17:16:53 -08001717 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong6855aee2017-11-16 15:27:22 -08001718
1719 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001720 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1721 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001722
Benedict Wong6855aee2017-11-16 15:27:22 -08001723 EncapSocketRecord socketRecord = null;
Benedict Wong8edc5572018-01-19 17:36:02 -08001724 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001725 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1726 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1727 c.getEncapSocketResourceId());
1728 dependencies.add(refcountedSocketRecord);
Benedict Wong6855aee2017-11-16 15:27:22 -08001729 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold80865392017-04-04 19:37:48 -07001730 }
1731
Nathan Harold5a19b952018-01-05 19:25:13 -08001732 RefcountedResource<SpiRecord> refcountedSpiRecord =
1733 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1734 dependencies.add(refcountedSpiRecord);
1735 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong6855aee2017-11-16 15:27:22 -08001736
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001737 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong8edc5572018-01-19 17:36:02 -08001738
1739 // SA was created successfully, time to construct a record and lock it away
Benedict Wong6855aee2017-11-16 15:27:22 -08001740 userRecord.mTransformRecords.put(
1741 resourceId,
1742 new RefcountedResource<TransformRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001743 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong6855aee2017-11-16 15:27:22 -08001744 binder,
1745 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold80865392017-04-04 19:37:48 -07001746 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001747 }
1748
1749 /**
1750 * Delete a transport mode transform that was previously allocated by + registered with the
1751 * system server. If this is called on an inactive (or non-existent) transform, it will not
1752 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1753 * other reasons.
1754 */
1755 @Override
Benedict Wong0fff56e2018-01-18 14:38:16 -08001756 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001757 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1758 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001759 }
1760
1761 /**
1762 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1763 * association as a correspondent policy to the provided socket
1764 */
1765 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001766 public synchronized void applyTransportModeTransform(
Nathan Harold5a19b952018-01-05 19:25:13 -08001767 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001768 int callingUid = Binder.getCallingUid();
1769 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harold5a19b952018-01-05 19:25:13 -08001770 checkDirection(direction);
Benedict Wong6855aee2017-11-16 15:27:22 -08001771 // Get transform record; if no transform is found, will throw IllegalArgumentException
1772 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001773
Nathan Harold80865392017-04-04 19:37:48 -07001774 // TODO: make this a function.
Aaron Huangfbae3082021-12-06 15:18:42 +08001775 if (info.mPid != getCallingPid() || info.mUid != callingUid) {
Nathan Harold80865392017-04-04 19:37:48 -07001776 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1777 }
1778
Benedict Wong8bc90732018-01-18 18:31:45 -08001779 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold80865392017-04-04 19:37:48 -07001780 IpSecConfig c = info.getConfig();
Benedict Wong8bc90732018-01-18 18:31:45 -08001781 Preconditions.checkArgument(
1782 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1783 "Transform mode was not Transport mode; cannot be applied to a socket");
1784
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001785 mSrvConfig
1786 .getNetdInstance()
1787 .ipSecApplyTransportModeTransform(
Luke Huangd913fb42018-11-23 12:01:41 +08001788 socket,
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001789 callingUid,
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001790 direction,
1791 c.getSourceAddress(),
1792 c.getDestinationAddress(),
1793 info.getSpiRecord().getSpi());
Nathan Harold031acb82017-03-07 13:23:36 -08001794 }
Nathan Harold80865392017-04-04 19:37:48 -07001795
Nathan Harold031acb82017-03-07 13:23:36 -08001796 /**
Nathan Harold5a19b952018-01-05 19:25:13 -08001797 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1798 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1799 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1800 * reserved for future improved input validation.
Nathan Harold031acb82017-03-07 13:23:36 -08001801 */
1802 @Override
Nathan Harold0d483b72018-01-17 01:00:20 -08001803 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1804 throws RemoteException {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001805 mSrvConfig
1806 .getNetdInstance()
Luke Huangd913fb42018-11-23 12:01:41 +08001807 .ipSecRemoveTransportModeTransform(socket);
Nathan Harold031acb82017-03-07 13:23:36 -08001808 }
1809
Benedict Wong8bc90732018-01-18 18:31:45 -08001810 /**
1811 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1812 * security association as a correspondent policy to the provided interface
1813 */
1814 @Override
1815 public synchronized void applyTunnelModeTransform(
Nathan Harold65ef8432018-03-15 18:06:06 -07001816 int tunnelResourceId, int direction,
1817 int transformResourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001818 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001819 checkDirection(direction);
1820
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001821 int callingUid = Binder.getCallingUid();
1822 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001823
1824 // Get transform record; if no transform is found, will throw IllegalArgumentException
1825 TransformRecord transformInfo =
1826 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1827
1828 // Get tunnelInterface record; if no such interface is found, will throw
1829 // IllegalArgumentException
1830 TunnelInterfaceRecord tunnelInterfaceInfo =
1831 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1832
1833 // Get config and check that to-be-applied transform has the correct mode
1834 IpSecConfig c = transformInfo.getConfig();
1835 Preconditions.checkArgument(
1836 c.getMode() == IpSecTransform.MODE_TUNNEL,
1837 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1838
Benedict Wong8edc5572018-01-19 17:36:02 -08001839 EncapSocketRecord socketRecord = null;
1840 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1841 socketRecord =
1842 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1843 }
Benedict Wongec2e2e22019-10-03 11:09:00 -07001844 SpiRecord spiRecord = transformInfo.getSpiRecord();
Benedict Wong8edc5572018-01-19 17:36:02 -08001845
Benedict Wong8bc90732018-01-18 18:31:45 -08001846 int mark =
Benedict Wong38e52972018-05-07 20:06:44 -07001847 (direction == IpSecManager.DIRECTION_OUT)
1848 ? tunnelInterfaceInfo.getOkey()
Benedict Wong908d34e2021-04-15 11:59:16 -07001849 : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
Benedict Wong8bc90732018-01-18 18:31:45 -08001850
Benedict Wong8edc5572018-01-19 17:36:02 -08001851 try {
Benedict Wong5d749842018-09-06 11:31:25 -07001852 // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
1853 // SPI matching as part of the template resolution.
1854 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1855 c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
1856
Benedict Wong683441d2018-07-25 18:46:19 -07001857 // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
1858 // (and backporting) would allow us to narrow the mark space, and ensure that the SA
1859 // and SPs have matching marks (as VTI are meant to be built).
1860 // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
1861 // config matches the actual allocated resources in the kernel.
Benedict Wongc6fcedd2018-11-21 21:24:55 -08001862 // All SAs will have zero marks (from creation time), and any policy that matches the
1863 // same src/dst could match these SAs. Non-IpSecService governed processes that
1864 // establish floating policies with the same src/dst may result in undefined
1865 // behavior. This is generally limited to vendor code due to the permissions
1866 // (CAP_NET_ADMIN) required.
Benedict Wong683441d2018-07-25 18:46:19 -07001867 //
1868 // c.setMarkValue(mark);
1869 // c.setMarkMask(0xffffffff);
Benedict Wong8edc5572018-01-19 17:36:02 -08001870
1871 if (direction == IpSecManager.DIRECTION_OUT) {
1872 // Set output mark via underlying network (output only)
1873 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1874
Benedict Wong5d749842018-09-06 11:31:25 -07001875 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
1876 // but want to guarantee outbound packets are sent over the new SA.
Benedict Wongec2e2e22019-10-03 11:09:00 -07001877 spi = spiRecord.getSpi();
Benedict Wong5d749842018-09-06 11:31:25 -07001878 }
1879
1880 // Always update the policy with the relevant XFRM_IF_ID
1881 for (int selAddrFamily : ADDRESS_FAMILIES) {
1882 mSrvConfig
1883 .getNetdInstance()
1884 .ipSecUpdateSecurityPolicy(
1885 callingUid,
1886 selAddrFamily,
1887 direction,
1888 transformInfo.getConfig().getSourceAddress(),
1889 transformInfo.getConfig().getDestinationAddress(),
1890 spi, // If outbound, also add SPI to the policy.
1891 mark, // Must always set policy mark; ikey/okey for VTIs
1892 0xffffffff,
1893 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001894 }
1895
1896 // Update SA with tunnel mark (ikey or okey based on direction)
1897 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1898 } catch (ServiceSpecificException e) {
1899 if (e.errorCode == EINVAL) {
1900 throw new IllegalArgumentException(e.toString());
1901 } else {
1902 throw e;
1903 }
1904 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001905 }
1906
Nathan Harold031acb82017-03-07 13:23:36 -08001907 @Override
ludi89194d62017-05-22 10:52:23 -07001908 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001909 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludi89194d62017-05-22 10:52:23 -07001910
1911 pw.println("IpSecService dump:");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001912 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1913 pw.println();
ludi89194d62017-05-22 10:52:23 -07001914
Benedict Wong6855aee2017-11-16 15:27:22 -08001915 pw.println("mUserResourceTracker:");
1916 pw.println(mUserResourceTracker);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001917 }
1918}