blob: aeb814327e663cbcb0b900ee958d44923a976481 [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
135 static IpSecServiceConfiguration GETSRVINSTANCE =
136 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 {
Nathan Harold031acb82017-03-07 13:23:36 -0800494 final int pid;
495 final int uid;
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;
Nathan Harold031acb82017-03-07 13:23:36 -0800504 pid = Binder.getCallingPid();
505 uid = Binder.getCallingUid();
506
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() {
515 return mUserResourceTracker.getUserRecord(uid);
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=")
530 .append(pid)
531 .append(", uid=")
532 .append(uid)
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
Benedict Wong6855aee2017-11-16 15:27:22 -0800548 public RefcountedResourceArray(String typeName) {
549 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(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -0700631 uid,
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(
Benedict Wong5d749842018-09-06 11:31:25 -0700699 uid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
700 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(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -0700852 uid,
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(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -0700859 uid,
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);
1028 if(appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
1029 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
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001057 public void systemReady() {
1058 if (isNetdAlive()) {
paulhu00e34562021-10-26 09:00:50 +00001059 Log.d(TAG, "IpSecService is ready");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001060 } else {
paulhu00e34562021-10-26 09:00:50 +00001061 Log.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001062 }
1063 }
1064
1065 private void connectNativeNetdService() {
1066 // Avoid blocking the system server to do this
Nathan Harold12accd02017-07-17 14:01:53 -07001067 new Thread() {
1068 @Override
1069 public void run() {
1070 synchronized (IpSecService.this) {
ludi5e623ea2017-05-12 09:15:00 -07001071 NetdService.get(NETD_FETCH_TIMEOUT_MS);
Nathan Harold12accd02017-07-17 14:01:53 -07001072 }
1073 }
1074 }.start();
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001075 }
1076
Nathan Harold80865392017-04-04 19:37:48 -07001077 synchronized boolean isNetdAlive() {
1078 try {
ludi5e623ea2017-05-12 09:15:00 -07001079 final INetd netd = mSrvConfig.getNetdInstance();
Nathan Harold80865392017-04-04 19:37:48 -07001080 if (netd == null) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001081 return false;
1082 }
Nathan Harold80865392017-04-04 19:37:48 -07001083 return netd.isAlive();
1084 } catch (RemoteException re) {
1085 return false;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001086 }
1087 }
1088
Nathan Harold19b99d92017-08-23 13:46:33 -07001089 /**
1090 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1091 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1092 */
1093 private static void checkInetAddress(String inetAddress) {
1094 if (TextUtils.isEmpty(inetAddress)) {
1095 throw new IllegalArgumentException("Unspecified address");
1096 }
1097
Serik Beketayev7f507332020-12-06 22:31:23 -08001098 InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
Nathan Harold19b99d92017-08-23 13:46:33 -07001099
1100 if (checkAddr.isAnyLocalAddress()) {
1101 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1102 }
1103 }
1104
1105 /**
1106 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1107 * DIRECTION_IN or DIRECTION_OUT
1108 */
Benedict Wong908d34e2021-04-15 11:59:16 -07001109 private void checkDirection(int direction) {
Nathan Harold19b99d92017-08-23 13:46:33 -07001110 switch (direction) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001111 case IpSecManager.DIRECTION_OUT:
1112 case IpSecManager.DIRECTION_IN:
Nathan Harold19b99d92017-08-23 13:46:33 -07001113 return;
Benedict Wong908d34e2021-04-15 11:59:16 -07001114 case IpSecManager.DIRECTION_FWD:
Benedict Wong47b528c2021-05-10 18:26:02 -07001115 // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
Benedict Wong908d34e2021-04-15 11:59:16 -07001116 PermissionUtils.enforceNetworkStackPermission(mContext);
1117 return;
Nathan Harold19b99d92017-08-23 13:46:33 -07001118 }
1119 throw new IllegalArgumentException("Invalid Direction: " + direction);
1120 }
1121
Nathan Harold031acb82017-03-07 13:23:36 -08001122 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri20e96c52017-11-16 10:58:01 -08001123 @Override
1124 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harold5a19b952018-01-05 19:25:13 -08001125 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1126 checkInetAddress(destinationAddress);
Nathan Harold1b88f0e2018-03-28 08:52:51 -07001127 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1128 if (requestedSpi > 0 && requestedSpi < 256) {
1129 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1130 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001131 Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harold19b99d92017-08-23 13:46:33 -07001132
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001133 int callingUid = Binder.getCallingUid();
1134 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001135 final int resourceId = mNextResourceId++;
Nathan Harold031acb82017-03-07 13:23:36 -08001136
1137 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -08001138 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001139 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001140 return new IpSecSpiResponse(
Nathan Harold19b99d92017-08-23 13:46:33 -07001141 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold6e4681c2017-04-24 16:16:34 -07001142 }
Nathan Harold5a19b952018-01-05 19:25:13 -08001143
Nathan Harold031acb82017-03-07 13:23:36 -08001144 spi =
ludi5e623ea2017-05-12 09:15:00 -07001145 mSrvConfig
1146 .getNetdInstance()
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001147 .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold031acb82017-03-07 13:23:36 -08001148 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong6855aee2017-11-16 15:27:22 -08001149 userRecord.mSpiRecords.put(
Nathan Harold80865392017-04-04 19:37:48 -07001150 resourceId,
Benedict Wong6855aee2017-11-16 15:27:22 -08001151 new RefcountedResource<SpiRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001152 new SpiRecord(resourceId, "", destinationAddress, spi), binder));
Nathan Harold031acb82017-03-07 13:23:36 -08001153 } catch (ServiceSpecificException e) {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001154 if (e.errorCode == OsConstants.ENOENT) {
1155 return new IpSecSpiResponse(
1156 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1157 }
1158 throw e;
Nathan Harold031acb82017-03-07 13:23:36 -08001159 } catch (RemoteException e) {
1160 throw e.rethrowFromSystemServer();
1161 }
Nathan Harold80865392017-04-04 19:37:48 -07001162 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1163 }
1164
1165 /* This method should only be called from Binder threads. Do not call this from
1166 * within the system server as it will crash the system on failure.
1167 */
Benedict Wong6855aee2017-11-16 15:27:22 -08001168 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold80865392017-04-04 19:37:48 -07001169 throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001170 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold031acb82017-03-07 13:23:36 -08001171 }
1172
1173 /** Release a previously allocated SPI that has been registered with the system server */
1174 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001175 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1176 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1177 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001178 }
1179
1180 /**
1181 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1182 * be unbound.
1183 *
1184 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1185 * a random open port and then bind by number, this function creates a temp socket, binds to a
1186 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1187 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1188 * FileHandle.
1189 *
1190 * <p>The loop in this function handles the inherent race window between un-binding to a port
1191 * and re-binding, during which the system could *technically* hand that port out to someone
1192 * else.
1193 */
Benedict Wongc423cc82017-10-10 20:44:28 -07001194 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold80865392017-04-04 19:37:48 -07001195 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1196 try {
1197 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1198 Os.bind(probeSocket, INADDR_ANY, 0);
1199 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1200 Os.close(probeSocket);
1201 Log.v(TAG, "Binding to port " + port);
1202 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongc423cc82017-10-10 20:44:28 -07001203 return port;
Nathan Harold80865392017-04-04 19:37:48 -07001204 } catch (ErrnoException e) {
1205 // Someone miraculously claimed the port just after we closed probeSocket.
1206 if (e.errno == OsConstants.EADDRINUSE) {
1207 continue;
1208 }
1209 throw e.rethrowAsIOException();
1210 }
1211 }
1212 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1213 }
Nathan Harold031acb82017-03-07 13:23:36 -08001214
1215 /**
Benedict Wong083faee2017-12-03 19:42:36 -08001216 * Functional interface to do traffic tagging of given sockets to UIDs.
1217 *
1218 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1219 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1220 *
1221 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1222 * methods that cannot be easily mocked/tested.
1223 */
1224 @VisibleForTesting
1225 public interface UidFdTagger {
1226 /**
1227 * Sets socket tag to assign all traffic to the provided UID.
1228 *
1229 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1230 * should be accounted to the UID of the unprivileged application.
1231 */
1232 public void tag(FileDescriptor fd, int uid) throws IOException;
1233 }
1234
1235 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001236 * Open a socket via the system server and bind it to the specified port (random if port=0).
1237 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1238 * cache the socket and a record of its owner so that it can and must be freed when no longer
1239 * needed.
1240 */
1241 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001242 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1243 throws RemoteException {
1244 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1245 throw new IllegalArgumentException(
1246 "Specified port number must be a valid non-reserved UDP port");
1247 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001248 Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
Nathan Harold19b99d92017-08-23 13:46:33 -07001249
Benedict Wong083faee2017-12-03 19:42:36 -08001250 int callingUid = Binder.getCallingUid();
1251 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001252 final int resourceId = mNextResourceId++;
Nathan Harold80865392017-04-04 19:37:48 -07001253 FileDescriptor sockFd = null;
1254 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001255 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001256 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1257 }
1258
Nathan Harold80865392017-04-04 19:37:48 -07001259 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Benedict Wong083faee2017-12-03 19:42:36 -08001260 mUidFdTagger.tag(sockFd, callingUid);
Nathan Harold80865392017-04-04 19:37:48 -07001261
Nathan Harold80865392017-04-04 19:37:48 -07001262 // This code is common to both the unspecified and specified port cases
1263 Os.setsockoptInt(
1264 sockFd,
1265 OsConstants.IPPROTO_UDP,
1266 OsConstants.UDP_ENCAP,
1267 OsConstants.UDP_ENCAP_ESPINUDP);
1268
Luke Huangd913fb42018-11-23 12:01:41 +08001269 mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(
1270 new ParcelFileDescriptor(sockFd), callingUid);
Benedict Wong17687442017-12-06 21:56:35 -08001271 if (port != 0) {
1272 Log.v(TAG, "Binding to port " + port);
1273 Os.bind(sockFd, INADDR_ANY, port);
1274 } else {
1275 port = bindToRandomPort(sockFd);
1276 }
1277
Benedict Wong6855aee2017-11-16 15:27:22 -08001278 userRecord.mEncapSocketRecords.put(
1279 resourceId,
1280 new RefcountedResource<EncapSocketRecord>(
1281 new EncapSocketRecord(resourceId, sockFd, port), binder));
Nathan Harold80865392017-04-04 19:37:48 -07001282 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
1283 } catch (IOException | ErrnoException e) {
1284 IoUtils.closeQuietly(sockFd);
1285 }
1286 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1287 // The only reasonable condition that would cause that is resource unavailable.
1288 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold031acb82017-03-07 13:23:36 -08001289 }
1290
1291 /** close a socket that has been been allocated by and registered with the system server */
1292 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001293 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1294 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1295 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001296 }
Nathan Harold031acb82017-03-07 13:23:36 -08001297
Benedict Wong8bc90732018-01-18 18:31:45 -08001298 /**
1299 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1300 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1301 * needed.
1302 */
1303 @Override
1304 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001305 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1306 String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001307 enforceTunnelFeatureAndPermissions(callingPackage);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001308 Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
1309 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
Benedict Wong8bc90732018-01-18 18:31:45 -08001310 checkInetAddress(localAddr);
1311 checkInetAddress(remoteAddr);
1312
1313 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1314 // network (b/72316676).
1315
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001316 int callerUid = Binder.getCallingUid();
1317 UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001318 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1319 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1320 }
1321
1322 final int resourceId = mNextResourceId++;
1323 final int ikey = reserveNetId();
1324 final int okey = reserveNetId();
Nathan Harold7be7f452018-04-26 11:47:14 -07001325 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001326
Benedict Wong8edc5572018-01-19 17:36:02 -08001327 try {
1328 // Calls to netd:
1329 // Create VTI
1330 // Add inbound/outbound global policies
1331 // (use reqid = 0)
Benedict Wong38e52972018-05-07 20:06:44 -07001332 final INetd netd = mSrvConfig.getNetdInstance();
Benedict Wong5d749842018-09-06 11:31:25 -07001333 netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001334
paulhu00e34562021-10-26 09:00:50 +00001335 BinderUtils.withCleanCallingIdentity(() -> {
lucaslinff6fe7b2021-02-03 23:59:45 +08001336 NetdUtils.setInterfaceUp(netd, intfName);
Benedict Wong529e8aa2020-02-11 23:49:36 -08001337 });
1338
Benedict Wong38e52972018-05-07 20:06:44 -07001339 for (int selAddrFamily : ADDRESS_FAMILIES) {
1340 // Always send down correct local/remote addresses for template.
1341 netd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001342 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001343 selAddrFamily,
1344 IpSecManager.DIRECTION_OUT,
1345 localAddr,
1346 remoteAddr,
1347 0,
1348 okey,
Benedict Wong5d749842018-09-06 11:31:25 -07001349 0xffffffff,
1350 resourceId);
Benedict Wong38e52972018-05-07 20:06:44 -07001351 netd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001352 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001353 selAddrFamily,
1354 IpSecManager.DIRECTION_IN,
1355 remoteAddr,
1356 localAddr,
1357 0,
1358 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001359 0xffffffff,
1360 resourceId);
Benedict Wong47b528c2021-05-10 18:26:02 -07001361
1362 // Add a forwarding policy on the tunnel interface. In order to support forwarding
1363 // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
1364 //
1365 // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
1366 // forwarding will be blocked by default (as would be the case if this policy was
1367 // absent).
1368 //
1369 // This is necessary only on the tunnel interface, and not any the interface to
1370 // which traffic will be forwarded to.
Benedict Wong908d34e2021-04-15 11:59:16 -07001371 netd.ipSecAddSecurityPolicy(
1372 callerUid,
1373 selAddrFamily,
1374 IpSecManager.DIRECTION_FWD,
1375 remoteAddr,
1376 localAddr,
1377 0,
1378 ikey,
1379 0xffffffff,
1380 resourceId);
Benedict Wong8edc5572018-01-19 17:36:02 -08001381 }
1382
1383 userRecord.mTunnelInterfaceRecords.put(
1384 resourceId,
1385 new RefcountedResource<TunnelInterfaceRecord>(
1386 new TunnelInterfaceRecord(
1387 resourceId,
1388 intfName,
1389 underlyingNetwork,
1390 localAddr,
1391 remoteAddr,
1392 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001393 okey,
1394 resourceId),
Benedict Wong8edc5572018-01-19 17:36:02 -08001395 binder));
1396 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1397 } catch (RemoteException e) {
1398 // Release keys if we got an error.
1399 releaseNetId(ikey);
1400 releaseNetId(okey);
1401 throw e.rethrowFromSystemServer();
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001402 } catch (Throwable t) {
1403 // Release keys if we got an error.
1404 releaseNetId(ikey);
1405 releaseNetId(okey);
1406 throw t;
Benedict Wong8edc5572018-01-19 17:36:02 -08001407 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001408 }
1409
1410 /**
1411 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1412 * from multiple local IP addresses over the same tunnel.
1413 */
1414 @Override
Benedict Wong97c3c942018-03-01 18:53:07 -08001415 public synchronized void addAddressToTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001416 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001417 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001418 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1419
1420 // Get tunnelInterface record; if no such interface is found, will throw
1421 // IllegalArgumentException
1422 TunnelInterfaceRecord tunnelInterfaceInfo =
1423 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1424
Benedict Wong97c3c942018-03-01 18:53:07 -08001425 try {
1426 // We can assume general validity of the IP address, since we get them as a
1427 // LinkAddress, which does some validation.
1428 mSrvConfig
1429 .getNetdInstance()
1430 .interfaceAddAddress(
1431 tunnelInterfaceInfo.mInterfaceName,
1432 localAddr.getAddress().getHostAddress(),
1433 localAddr.getPrefixLength());
1434 } catch (RemoteException e) {
1435 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001436 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001437 }
1438
1439 /**
1440 * Remove a new local address from the tunnel interface. After removal, the address will no
1441 * longer be available to send from, or receive on.
1442 */
1443 @Override
1444 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001445 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001446 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001447
Nathan Harold65ef8432018-03-15 18:06:06 -07001448 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8bc90732018-01-18 18:31:45 -08001449 // Get tunnelInterface record; if no such interface is found, will throw
1450 // IllegalArgumentException
1451 TunnelInterfaceRecord tunnelInterfaceInfo =
1452 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1453
Benedict Wong97c3c942018-03-01 18:53:07 -08001454 try {
1455 // We can assume general validity of the IP address, since we get them as a
1456 // LinkAddress, which does some validation.
1457 mSrvConfig
1458 .getNetdInstance()
1459 .interfaceDelAddress(
1460 tunnelInterfaceInfo.mInterfaceName,
1461 localAddr.getAddress().getHostAddress(),
1462 localAddr.getPrefixLength());
1463 } catch (RemoteException e) {
1464 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001465 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001466 }
1467
Yan Yana2f3b492020-09-29 23:38:00 -07001468 /** Set TunnelInterface to use a specific underlying network. */
1469 @Override
1470 public synchronized void setNetworkForTunnelInterface(
1471 int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
1472 enforceTunnelFeatureAndPermissions(callingPackage);
1473 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1474
1475 final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1476
1477 // Get tunnelInterface record; if no such interface is found, will throw
1478 // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
1479 final TunnelInterfaceRecord tunnelInterfaceInfo =
1480 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1481
1482 final ConnectivityManager connectivityManager =
1483 mContext.getSystemService(ConnectivityManager.class);
1484 final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
1485 if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
1486 throw new IllegalArgumentException(
1487 "Underlying network cannot be the network being exposed by this tunnel");
1488 }
1489
1490 // It is meaningless to check if the network exists or is valid because the network might
1491 // disconnect at any time after it passes the check.
1492
1493 tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
1494 }
1495
Benedict Wong8bc90732018-01-18 18:31:45 -08001496 /**
1497 * Delete a TunnelInterface that has been been allocated by and registered with the system
1498 * server
1499 */
1500 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001501 public synchronized void deleteTunnelInterface(
1502 int resourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001503 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001504 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1505 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1506 }
1507
Benedict Wong70867e52017-11-06 20:49:10 -08001508 @VisibleForTesting
Nathan Harold5a19b952018-01-05 19:25:13 -08001509 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1510 IpSecAlgorithm auth = config.getAuthentication();
1511 IpSecAlgorithm crypt = config.getEncryption();
1512 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong70867e52017-11-06 20:49:10 -08001513
Nathan Harold5a19b952018-01-05 19:25:13 -08001514 // Validate the algorithm set
1515 Preconditions.checkArgument(
1516 aead != null || crypt != null || auth != null,
1517 "No Encryption or Authentication algorithms specified");
1518 Preconditions.checkArgument(
1519 auth == null || auth.isAuthentication(),
1520 "Unsupported algorithm for Authentication");
1521 Preconditions.checkArgument(
Benedict Wong70867e52017-11-06 20:49:10 -08001522 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harold5a19b952018-01-05 19:25:13 -08001523 Preconditions.checkArgument(
1524 aead == null || aead.isAead(),
1525 "Unsupported algorithm for Authenticated Encryption");
1526 Preconditions.checkArgument(
1527 aead == null || (auth == null && crypt == null),
1528 "Authenticated Encryption is mutually exclusive with other Authentication "
1529 + "or Encryption algorithms");
Benedict Wong70867e52017-11-06 20:49:10 -08001530 }
1531
evitayan43d93a02018-03-22 17:53:08 -07001532 private int getFamily(String inetAddress) {
1533 int family = AF_UNSPEC;
Serik Beketayev7f507332020-12-06 22:31:23 -08001534 InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
evitayan43d93a02018-03-22 17:53:08 -07001535 if (checkAddress instanceof Inet4Address) {
1536 family = AF_INET;
1537 } else if (checkAddress instanceof Inet6Address) {
1538 family = AF_INET6;
1539 }
1540 return family;
1541 }
1542
Nathan Harold031acb82017-03-07 13:23:36 -08001543 /**
Chiachang Wang2fea4a72020-08-12 12:23:59 +08001544 * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
Nathan Harold19b99d92017-08-23 13:46:33 -07001545 * IllegalArgumentException if they are not.
1546 */
1547 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001548 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1549
Nathan Harold19b99d92017-08-23 13:46:33 -07001550 switch (config.getEncapType()) {
1551 case IpSecTransform.ENCAP_NONE:
1552 break;
1553 case IpSecTransform.ENCAP_ESPINUDP:
1554 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong6855aee2017-11-16 15:27:22 -08001555 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1556 userRecord.mEncapSocketRecords.getResourceOrThrow(
1557 config.getEncapSocketResourceId());
Nathan Harold19b99d92017-08-23 13:46:33 -07001558
1559 int port = config.getEncapRemotePort();
1560 if (port <= 0 || port > 0xFFFF) {
1561 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1562 }
1563 break;
1564 default:
1565 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1566 }
1567
Nathan Harold5a19b952018-01-05 19:25:13 -08001568 validateAlgorithms(config);
Nathan Harold19b99d92017-08-23 13:46:33 -07001569
Nathan Harold5a19b952018-01-05 19:25:13 -08001570 // Retrieve SPI record; will throw IllegalArgumentException if not found
1571 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1572
Benedict Wong68aac2a2017-12-13 18:26:40 -08001573 // Check to ensure that SPI has not already been used.
1574 if (s.getOwnedByTransform()) {
1575 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1576 }
1577
Nathan Harold5a19b952018-01-05 19:25:13 -08001578 // If no remote address is supplied, then use one from the SPI.
1579 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1580 config.setDestinationAddress(s.getDestinationAddress());
1581 }
1582
1583 // All remote addresses must match
1584 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1585 throw new IllegalArgumentException("Mismatched remote addresseses.");
1586 }
1587
1588 // This check is technically redundant due to the chain of custody between the SPI and
1589 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1590 // the transform, this will prevent us from messing up.
1591 checkInetAddress(config.getDestinationAddress());
1592
1593 // Require a valid source address for all transforms.
1594 checkInetAddress(config.getSourceAddress());
1595
evitayan43d93a02018-03-22 17:53:08 -07001596 // Check to ensure source and destination have the same address family.
1597 String sourceAddress = config.getSourceAddress();
1598 String destinationAddress = config.getDestinationAddress();
1599 int sourceFamily = getFamily(sourceAddress);
1600 int destinationFamily = getFamily(destinationAddress);
1601 if (sourceFamily != destinationFamily) {
1602 throw new IllegalArgumentException(
1603 "Source address ("
1604 + sourceAddress
1605 + ") and destination address ("
1606 + destinationAddress
1607 + ") have different address families.");
1608 }
1609
1610 // Throw an error if UDP Encapsulation is not used in IPv4.
1611 if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
1612 throw new IllegalArgumentException(
1613 "UDP Encapsulation is not supported for this address family");
1614 }
1615
Nathan Harold5a19b952018-01-05 19:25:13 -08001616 switch (config.getMode()) {
1617 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold025aae12018-02-02 18:34:25 -08001618 break;
Nathan Harold5a19b952018-01-05 19:25:13 -08001619 case IpSecTransform.MODE_TUNNEL:
1620 break;
1621 default:
1622 throw new IllegalArgumentException(
1623 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harold19b99d92017-08-23 13:46:33 -07001624 }
Benedict Wong683441d2018-07-25 18:46:19 -07001625
1626 config.setMarkValue(0);
1627 config.setMarkMask(0);
Nathan Harold19b99d92017-08-23 13:46:33 -07001628 }
1629
Benedict Wong2b6a14e2018-09-13 16:45:12 -07001630 private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
Nathan Harold7c250ae2018-05-15 19:18:38 -07001631
Benedict Wonge9763752018-11-08 19:45:34 -08001632 private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1633 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1634 throw new UnsupportedOperationException(
1635 "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1636 }
1637
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001638 Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Benedict Wongc85b7b02019-11-12 22:31:51 -08001639
1640 // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
1641 // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
1642 // permission or is the System Server.
1643 if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
1644 TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1645 return;
Nathan Harold65ef8432018-03-15 18:06:06 -07001646 }
Benedict Wongc85b7b02019-11-12 22:31:51 -08001647 mContext.enforceCallingOrSelfPermission(
1648 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
Nathan Harold025aae12018-02-02 18:34:25 -08001649 }
1650
Benedict Wong8edc5572018-01-19 17:36:02 -08001651 private void createOrUpdateTransform(
1652 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1653 throws RemoteException {
1654
1655 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1656 if (encapType != IpSecTransform.ENCAP_NONE) {
1657 encapLocalPort = socketRecord.getPort();
1658 encapRemotePort = c.getEncapRemotePort();
1659 }
1660
1661 IpSecAlgorithm auth = c.getAuthentication();
1662 IpSecAlgorithm crypt = c.getEncryption();
1663 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1664
Benedict Wong778327e2018-03-15 19:41:41 -07001665 String cryptName;
1666 if (crypt == null) {
1667 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1668 } else {
1669 cryptName = crypt.getName();
1670 }
1671
Benedict Wong8edc5572018-01-19 17:36:02 -08001672 mSrvConfig
1673 .getNetdInstance()
1674 .ipSecAddSecurityAssociation(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001675 Binder.getCallingUid(),
Benedict Wong8edc5572018-01-19 17:36:02 -08001676 c.getMode(),
1677 c.getSourceAddress(),
1678 c.getDestinationAddress(),
lucaslin20966d92021-03-19 16:06:33 +08001679 (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
Benedict Wong8edc5572018-01-19 17:36:02 -08001680 spiRecord.getSpi(),
1681 c.getMarkValue(),
1682 c.getMarkMask(),
1683 (auth != null) ? auth.getName() : "",
1684 (auth != null) ? auth.getKey() : new byte[] {},
1685 (auth != null) ? auth.getTruncationLengthBits() : 0,
Benedict Wong778327e2018-03-15 19:41:41 -07001686 cryptName,
Benedict Wong8edc5572018-01-19 17:36:02 -08001687 (crypt != null) ? crypt.getKey() : new byte[] {},
1688 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1689 (authCrypt != null) ? authCrypt.getName() : "",
1690 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1691 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1692 encapType,
1693 encapLocalPort,
Benedict Wong5d749842018-09-06 11:31:25 -07001694 encapRemotePort,
1695 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001696 }
1697
Nathan Harold19b99d92017-08-23 13:46:33 -07001698 /**
Benedict Wong8edc5572018-01-19 17:36:02 -08001699 * Create a IPsec transform, which represents a single security association in the kernel. The
1700 * transform will be cached by the system server and must be freed when no longer needed. It is
1701 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1702 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold031acb82017-03-07 13:23:36 -08001703 */
1704 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001705 public synchronized IpSecTransformResponse createTransform(
1706 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001707 Objects.requireNonNull(c);
Nathan Harold65ef8432018-03-15 18:06:06 -07001708 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
Benedict Wonge9763752018-11-08 19:45:34 -08001709 enforceTunnelFeatureAndPermissions(callingPackage);
Nathan Harold65ef8432018-03-15 18:06:06 -07001710 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001711 checkIpSecConfig(c);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001712 Objects.requireNonNull(binder, "Null Binder passed to createTransform");
Nathan Haroldfdafce22017-12-13 19:16:33 -08001713 final int resourceId = mNextResourceId++;
Benedict Wong6855aee2017-11-16 15:27:22 -08001714
1715 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wongcbd329b2017-12-13 17:16:53 -08001716 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong6855aee2017-11-16 15:27:22 -08001717
1718 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001719 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1720 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001721
Benedict Wong6855aee2017-11-16 15:27:22 -08001722 EncapSocketRecord socketRecord = null;
Benedict Wong8edc5572018-01-19 17:36:02 -08001723 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001724 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1725 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1726 c.getEncapSocketResourceId());
1727 dependencies.add(refcountedSocketRecord);
Benedict Wong6855aee2017-11-16 15:27:22 -08001728 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold80865392017-04-04 19:37:48 -07001729 }
1730
Nathan Harold5a19b952018-01-05 19:25:13 -08001731 RefcountedResource<SpiRecord> refcountedSpiRecord =
1732 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1733 dependencies.add(refcountedSpiRecord);
1734 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong6855aee2017-11-16 15:27:22 -08001735
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001736 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong8edc5572018-01-19 17:36:02 -08001737
1738 // SA was created successfully, time to construct a record and lock it away
Benedict Wong6855aee2017-11-16 15:27:22 -08001739 userRecord.mTransformRecords.put(
1740 resourceId,
1741 new RefcountedResource<TransformRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001742 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong6855aee2017-11-16 15:27:22 -08001743 binder,
1744 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold80865392017-04-04 19:37:48 -07001745 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001746 }
1747
1748 /**
1749 * Delete a transport mode transform that was previously allocated by + registered with the
1750 * system server. If this is called on an inactive (or non-existent) transform, it will not
1751 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1752 * other reasons.
1753 */
1754 @Override
Benedict Wong0fff56e2018-01-18 14:38:16 -08001755 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001756 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1757 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001758 }
1759
1760 /**
1761 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1762 * association as a correspondent policy to the provided socket
1763 */
1764 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001765 public synchronized void applyTransportModeTransform(
Nathan Harold5a19b952018-01-05 19:25:13 -08001766 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001767 int callingUid = Binder.getCallingUid();
1768 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harold5a19b952018-01-05 19:25:13 -08001769 checkDirection(direction);
Benedict Wong6855aee2017-11-16 15:27:22 -08001770 // Get transform record; if no transform is found, will throw IllegalArgumentException
1771 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001772
Nathan Harold80865392017-04-04 19:37:48 -07001773 // TODO: make this a function.
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001774 if (info.pid != getCallingPid() || info.uid != callingUid) {
Nathan Harold80865392017-04-04 19:37:48 -07001775 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1776 }
1777
Benedict Wong8bc90732018-01-18 18:31:45 -08001778 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold80865392017-04-04 19:37:48 -07001779 IpSecConfig c = info.getConfig();
Benedict Wong8bc90732018-01-18 18:31:45 -08001780 Preconditions.checkArgument(
1781 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1782 "Transform mode was not Transport mode; cannot be applied to a socket");
1783
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001784 mSrvConfig
1785 .getNetdInstance()
1786 .ipSecApplyTransportModeTransform(
Luke Huangd913fb42018-11-23 12:01:41 +08001787 socket,
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001788 callingUid,
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001789 direction,
1790 c.getSourceAddress(),
1791 c.getDestinationAddress(),
1792 info.getSpiRecord().getSpi());
Nathan Harold031acb82017-03-07 13:23:36 -08001793 }
Nathan Harold80865392017-04-04 19:37:48 -07001794
Nathan Harold031acb82017-03-07 13:23:36 -08001795 /**
Nathan Harold5a19b952018-01-05 19:25:13 -08001796 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1797 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1798 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1799 * reserved for future improved input validation.
Nathan Harold031acb82017-03-07 13:23:36 -08001800 */
1801 @Override
Nathan Harold0d483b72018-01-17 01:00:20 -08001802 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1803 throws RemoteException {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001804 mSrvConfig
1805 .getNetdInstance()
Luke Huangd913fb42018-11-23 12:01:41 +08001806 .ipSecRemoveTransportModeTransform(socket);
Nathan Harold031acb82017-03-07 13:23:36 -08001807 }
1808
Benedict Wong8bc90732018-01-18 18:31:45 -08001809 /**
1810 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1811 * security association as a correspondent policy to the provided interface
1812 */
1813 @Override
1814 public synchronized void applyTunnelModeTransform(
Nathan Harold65ef8432018-03-15 18:06:06 -07001815 int tunnelResourceId, int direction,
1816 int transformResourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001817 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001818 checkDirection(direction);
1819
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001820 int callingUid = Binder.getCallingUid();
1821 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001822
1823 // Get transform record; if no transform is found, will throw IllegalArgumentException
1824 TransformRecord transformInfo =
1825 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1826
1827 // Get tunnelInterface record; if no such interface is found, will throw
1828 // IllegalArgumentException
1829 TunnelInterfaceRecord tunnelInterfaceInfo =
1830 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1831
1832 // Get config and check that to-be-applied transform has the correct mode
1833 IpSecConfig c = transformInfo.getConfig();
1834 Preconditions.checkArgument(
1835 c.getMode() == IpSecTransform.MODE_TUNNEL,
1836 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1837
Benedict Wong8edc5572018-01-19 17:36:02 -08001838 EncapSocketRecord socketRecord = null;
1839 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1840 socketRecord =
1841 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1842 }
Benedict Wongec2e2e22019-10-03 11:09:00 -07001843 SpiRecord spiRecord = transformInfo.getSpiRecord();
Benedict Wong8edc5572018-01-19 17:36:02 -08001844
Benedict Wong8bc90732018-01-18 18:31:45 -08001845 int mark =
Benedict Wong38e52972018-05-07 20:06:44 -07001846 (direction == IpSecManager.DIRECTION_OUT)
1847 ? tunnelInterfaceInfo.getOkey()
Benedict Wong908d34e2021-04-15 11:59:16 -07001848 : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
Benedict Wong8bc90732018-01-18 18:31:45 -08001849
Benedict Wong8edc5572018-01-19 17:36:02 -08001850 try {
Benedict Wong5d749842018-09-06 11:31:25 -07001851 // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
1852 // SPI matching as part of the template resolution.
1853 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1854 c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
1855
Benedict Wong683441d2018-07-25 18:46:19 -07001856 // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
1857 // (and backporting) would allow us to narrow the mark space, and ensure that the SA
1858 // and SPs have matching marks (as VTI are meant to be built).
1859 // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
1860 // config matches the actual allocated resources in the kernel.
Benedict Wongc6fcedd2018-11-21 21:24:55 -08001861 // All SAs will have zero marks (from creation time), and any policy that matches the
1862 // same src/dst could match these SAs. Non-IpSecService governed processes that
1863 // establish floating policies with the same src/dst may result in undefined
1864 // behavior. This is generally limited to vendor code due to the permissions
1865 // (CAP_NET_ADMIN) required.
Benedict Wong683441d2018-07-25 18:46:19 -07001866 //
1867 // c.setMarkValue(mark);
1868 // c.setMarkMask(0xffffffff);
Benedict Wong8edc5572018-01-19 17:36:02 -08001869
1870 if (direction == IpSecManager.DIRECTION_OUT) {
1871 // Set output mark via underlying network (output only)
1872 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1873
Benedict Wong5d749842018-09-06 11:31:25 -07001874 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
1875 // but want to guarantee outbound packets are sent over the new SA.
Benedict Wongec2e2e22019-10-03 11:09:00 -07001876 spi = spiRecord.getSpi();
Benedict Wong5d749842018-09-06 11:31:25 -07001877 }
1878
1879 // Always update the policy with the relevant XFRM_IF_ID
1880 for (int selAddrFamily : ADDRESS_FAMILIES) {
1881 mSrvConfig
1882 .getNetdInstance()
1883 .ipSecUpdateSecurityPolicy(
1884 callingUid,
1885 selAddrFamily,
1886 direction,
1887 transformInfo.getConfig().getSourceAddress(),
1888 transformInfo.getConfig().getDestinationAddress(),
1889 spi, // If outbound, also add SPI to the policy.
1890 mark, // Must always set policy mark; ikey/okey for VTIs
1891 0xffffffff,
1892 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001893 }
1894
1895 // Update SA with tunnel mark (ikey or okey based on direction)
1896 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1897 } catch (ServiceSpecificException e) {
1898 if (e.errorCode == EINVAL) {
1899 throw new IllegalArgumentException(e.toString());
1900 } else {
1901 throw e;
1902 }
1903 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001904 }
1905
Nathan Harold031acb82017-03-07 13:23:36 -08001906 @Override
ludi89194d62017-05-22 10:52:23 -07001907 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001908 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludi89194d62017-05-22 10:52:23 -07001909
1910 pw.println("IpSecService dump:");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001911 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1912 pw.println();
ludi89194d62017-05-22 10:52:23 -07001913
Benedict Wong6855aee2017-11-16 15:27:22 -08001914 pw.println("mUserResourceTracker:");
1915 pw.println(mUserResourceTracker);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001916 }
1917}