blob: 6cee08ade89dc6b1819b5508f6b8d4154111695a [file] [log] [blame]
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static android.Manifest.permission.DUMP;
Nathan Harold031acb82017-03-07 13:23:36 -080020import static android.net.IpSecManager.INVALID_RESOURCE_ID;
Nathan Harold80865392017-04-04 19:37:48 -070021import static android.system.OsConstants.AF_INET;
evitayan43d93a02018-03-22 17:53:08 -070022import static android.system.OsConstants.AF_INET6;
23import static android.system.OsConstants.AF_UNSPEC;
manojboopathiac927fe2017-11-30 17:11:49 -080024import static android.system.OsConstants.EINVAL;
Nathan Harold80865392017-04-04 19:37:48 -070025import static android.system.OsConstants.IPPROTO_UDP;
26import static android.system.OsConstants.SOCK_DGRAM;
Benedict Wong683441d2018-07-25 18:46:19 -070027
Nathan Harold65ef8432018-03-15 18:06:06 -070028import android.annotation.NonNull;
29import android.app.AppOpsManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080030import android.content.Context;
Benedict Wong4aac3e92019-02-25 12:33:22 -080031import android.content.pm.PackageManager;
Yan Yana2f3b492020-09-29 23:38:00 -070032import android.net.ConnectivityManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080033import android.net.IIpSecService;
34import android.net.INetd;
Serik Beketayev7f507332020-12-06 22:31:23 -080035import android.net.InetAddresses;
Nathan Harold031acb82017-03-07 13:23:36 -080036import android.net.IpSecAlgorithm;
37import android.net.IpSecConfig;
38import android.net.IpSecManager;
Nathan Harold80865392017-04-04 19:37:48 -070039import android.net.IpSecSpiResponse;
Nathan Harold031acb82017-03-07 13:23:36 -080040import android.net.IpSecTransform;
Nathan Harold80865392017-04-04 19:37:48 -070041import android.net.IpSecTransformResponse;
Benedict Wong8bc90732018-01-18 18:31:45 -080042import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold80865392017-04-04 19:37:48 -070043import android.net.IpSecUdpEncapResponse;
Benedict Wong97c3c942018-03-01 18:53:07 -080044import android.net.LinkAddress;
Yan Yana2f3b492020-09-29 23:38:00 -070045import android.net.LinkProperties;
Benedict Wong8bc90732018-01-18 18:31:45 -080046import android.net.Network;
Benedict Wong083faee2017-12-03 19:42:36 -080047import android.net.TrafficStats;
Nathan Harold031acb82017-03-07 13:23:36 -080048import android.os.Binder;
Nathan Harold031acb82017-03-07 13:23:36 -080049import android.os.IBinder;
50import android.os.ParcelFileDescriptor;
Benedict Wong908d34e2021-04-15 11:59:16 -070051import android.os.Process;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080052import android.os.RemoteException;
Nathan Harold031acb82017-03-07 13:23:36 -080053import android.os.ServiceSpecificException;
Nathan Harold80865392017-04-04 19:37:48 -070054import android.system.ErrnoException;
55import android.system.Os;
56import android.system.OsConstants;
Nathan Harold19b99d92017-08-23 13:46:33 -070057import android.text.TextUtils;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080058import android.util.Log;
lucaslin7eb76592021-03-11 17:39:49 +080059import android.util.Range;
Nathan Harold031acb82017-03-07 13:23:36 -080060import android.util.SparseArray;
Benedict Wong8bc90732018-01-18 18:31:45 -080061import android.util.SparseBooleanArray;
Nathan Harold19b99d92017-08-23 13:46:33 -070062
Nathan Harold031acb82017-03-07 13:23:36 -080063import com.android.internal.annotations.GuardedBy;
ludi5e623ea2017-05-12 09:15:00 -070064import com.android.internal.annotations.VisibleForTesting;
Benedict Wong70867e52017-11-06 20:49:10 -080065import com.android.internal.util.Preconditions;
paulhu00e34562021-10-26 09:00:50 +000066import com.android.net.module.util.BinderUtils;
lucaslinff6fe7b2021-02-03 23:59:45 +080067import com.android.net.module.util.NetdUtils;
Benedict Wong908d34e2021-04-15 11:59:16 -070068import com.android.net.module.util.PermissionUtils;
Nathan Harold19b99d92017-08-23 13:46:33 -070069
Benedict Wong683441d2018-07-25 18:46:19 -070070import libcore.io.IoUtils;
71
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080072import java.io.FileDescriptor;
Nathan Harold80865392017-04-04 19:37:48 -070073import java.io.IOException;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080074import java.io.PrintWriter;
evitayan43d93a02018-03-22 17:53:08 -070075import java.net.Inet4Address;
76import java.net.Inet6Address;
Nathan Harold80865392017-04-04 19:37:48 -070077import java.net.InetAddress;
78import java.net.InetSocketAddress;
79import java.net.UnknownHostException;
Benedict Wong02346822017-10-26 19:41:43 -070080import java.util.ArrayList;
81import java.util.List;
Benedict Wong529e8aa2020-02-11 23:49:36 -080082import java.util.Objects;
Nathan Harold19b99d92017-08-23 13:46:33 -070083
Benedict Wong02346822017-10-26 19:41:43 -070084/**
85 * A service to manage multiple clients that want to access the IpSec API. The service is
86 * responsible for maintaining a list of clients and managing the resources (and related quotas)
87 * that each of them own.
88 *
89 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
90 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
91 * thread is ever running at a time.
92 *
93 * @hide
94 */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080095public class IpSecService extends IIpSecService.Stub {
96 private static final String TAG = "IpSecService";
97 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Benedict Wong38e52972018-05-07 20:06:44 -070098 private static final int[] ADDRESS_FAMILIES =
99 new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800100
ludi5e623ea2017-05-12 09:15:00 -0700101 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold80865392017-04-04 19:37:48 -0700102 private static final InetAddress INADDR_ANY;
103
Benedict Wong29c30772019-03-20 09:44:09 -0700104 @VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
105
Aaron Huang2617cf52021-11-29 16:31:32 +0800106 private final INetd mNetd;
107
Nathan Harold80865392017-04-04 19:37:48 -0700108 static {
109 try {
110 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
111 } catch (UnknownHostException e) {
112 throw new RuntimeException(e);
113 }
114 }
115
116 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
117 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
118
119 /* Binder context for this service */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800120 private final Context mContext;
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800121 private final Dependencies mDeps;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800122
Nathan Haroldfdafce22017-12-13 19:16:33 -0800123 /**
Nathan Harold5a19b952018-01-05 19:25:13 -0800124 * The next non-repeating global ID for tracking resources between users, this service, and
125 * kernel data structures. Accessing this variable is not thread safe, so it is only read or
126 * modified within blocks synchronized on IpSecService.this. We want to avoid -1
127 * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
Nathan Haroldfdafce22017-12-13 19:16:33 -0800128 */
129 @GuardedBy("IpSecService.this")
130 private int mNextResourceId = 1;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800131
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800132 /**
133 * Dependencies of IpSecService, for injection in tests.
134 */
135 @VisibleForTesting
136 public static class Dependencies {
137 /**
138 * Get a reference to INetd.
139 */
140 public INetd getNetdInstance(Context context) throws RemoteException {
141 final INetd netd = INetd.Stub.asInterface((IBinder)
142 context.getSystemService(Context.NETD_SERVICE));
143 if (netd == null) {
144 throw new RemoteException("Failed to Get Netd Instance");
145 }
146 return netd;
147 }
ludi5e623ea2017-05-12 09:15:00 -0700148 }
149
Benedict Wong083faee2017-12-03 19:42:36 -0800150 final UidFdTagger mUidFdTagger;
ludi5e623ea2017-05-12 09:15:00 -0700151
Benedict Wong02346822017-10-26 19:41:43 -0700152 /**
153 * Interface for user-reference and kernel-resource cleanup.
154 *
155 * <p>This interface must be implemented for a resource to be reference counted.
156 */
157 @VisibleForTesting
158 public interface IResource {
159 /**
160 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
161 * objects dependent on it.
162 *
163 * <p>Implementations of this method are expected to remove references to the IResource
164 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
165 * the resource is considered invalid for user access or allocation or use in other
166 * resources.
167 *
168 * <p>References to the IResource object may be held by other RefcountedResource objects,
Benedict Wongcbd329b2017-12-13 17:16:53 -0800169 * and as such, the underlying resources and quota may not be cleaned up.
Benedict Wong02346822017-10-26 19:41:43 -0700170 */
171 void invalidate() throws RemoteException;
172
173 /**
174 * Releases underlying resources and related quotas.
175 *
176 * <p>Implementations of this method are expected to remove all system resources that are
177 * tracked by the IResource object. Due to other RefcountedResource objects potentially
Benedict Wong6855aee2017-11-16 15:27:22 -0800178 * having references to the IResource object, freeUnderlyingResources may not always be
Benedict Wong02346822017-10-26 19:41:43 -0700179 * called from releaseIfUnreferencedRecursively().
180 */
181 void freeUnderlyingResources() throws RemoteException;
182 }
183
184 /**
185 * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
186 *
187 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
188 * RefcountedResource object creates an explicit reference that must be freed by calling
189 * userRelease(). Additionally, adding this object as a child of another RefcountedResource
190 * object will add an implicit reference.
191 *
192 * <p>Resources are cleaned up when all references, both implicit and explicit, are released
193 * (ie, when userRelease() is called and when all parents have called releaseReference() on this
194 * object.)
195 */
196 @VisibleForTesting
197 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000198 private final T mResource;
199 private final List<RefcountedResource> mChildren;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800200 int mRefCount = 1; // starts at 1 for user's reference.
Junyu Lai75eabfe2019-04-26 01:38:04 +0000201 IBinder mBinder;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800202
Junyu Lai75eabfe2019-04-26 01:38:04 +0000203 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
Benedict Wong02346822017-10-26 19:41:43 -0700204 synchronized (IpSecService.this) {
205 this.mResource = resource;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000206 this.mChildren = new ArrayList<>(children.length);
Benedict Wong02346822017-10-26 19:41:43 -0700207 this.mBinder = binder;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000208
Benedict Wong02346822017-10-26 19:41:43 -0700209 for (RefcountedResource child : children) {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000210 mChildren.add(child);
Benedict Wong02346822017-10-26 19:41:43 -0700211 child.mRefCount++;
212 }
213
214 try {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000215 mBinder.linkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700216 } catch (RemoteException e) {
217 binderDied();
Benedict Wong04738f52019-02-28 20:28:48 -0800218 e.rethrowFromSystemServer();
Benedict Wong02346822017-10-26 19:41:43 -0700219 }
220 }
221 }
222
223 /**
224 * If the Binder object dies, this function is called to free the system resources that are
Benedict Wong6855aee2017-11-16 15:27:22 -0800225 * being tracked by this record and to subsequently release this record for garbage
Benedict Wong02346822017-10-26 19:41:43 -0700226 * collection
227 */
228 @Override
229 public void binderDied() {
230 synchronized (IpSecService.this) {
231 try {
232 userRelease();
233 } catch (Exception e) {
234 Log.e(TAG, "Failed to release resource: " + e);
235 }
236 }
237 }
238
239 public T getResource() {
240 return mResource;
241 }
242
243 /**
244 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
245 * arrays)
246 *
247 * <p>If this method has been previously called, the RefcountedResource's binder field will
248 * be null, and the method will return without performing the cleanup a second time.
249 *
250 * <p>Note that calling this function does not imply that kernel resources will be freed at
251 * this time, or that the related quota will be returned. Such actions will only be
252 * performed upon the reference count reaching zero.
253 */
254 @GuardedBy("IpSecService.this")
255 public void userRelease() throws RemoteException {
256 // Prevent users from putting reference counts into a bad state by calling
257 // userRelease() multiple times.
258 if (mBinder == null) {
259 return;
260 }
261
Junyu Lai75eabfe2019-04-26 01:38:04 +0000262 mBinder.unlinkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700263 mBinder = null;
264
265 mResource.invalidate();
Junyu Lai75eabfe2019-04-26 01:38:04 +0000266
Benedict Wong02346822017-10-26 19:41:43 -0700267 releaseReference();
268 }
269
270 /**
271 * Removes a reference to this resource. If the resultant reference count is zero, the
272 * underlying resources are freed, and references to all child resources are also dropped
273 * recursively (resulting in them freeing their resources and children, etcetera)
274 *
275 * <p>This method also sets the reference count to an invalid value (-1) to signify that it
276 * has been fully released. Any subsequent calls to this method will result in an
277 * IllegalStateException being thrown due to resource already having been previously
278 * released
279 */
280 @VisibleForTesting
281 @GuardedBy("IpSecService.this")
282 public void releaseReference() throws RemoteException {
283 mRefCount--;
284
285 if (mRefCount > 0) {
286 return;
287 } else if (mRefCount < 0) {
288 throw new IllegalStateException(
289 "Invalid operation - resource has already been released.");
290 }
291
292 // Cleanup own resources
293 mResource.freeUnderlyingResources();
294
295 // Cleanup child resources as needed
296 for (RefcountedResource<? extends IResource> child : mChildren) {
297 child.releaseReference();
298 }
299
300 // Enforce that resource cleanup can only be called once
301 // By decrementing the refcount (from 0 to -1), the next call will throw an
302 // IllegalStateException - it has already been released fully.
303 mRefCount--;
304 }
305
306 @Override
307 public String toString() {
308 return new StringBuilder()
309 .append("{mResource=")
310 .append(mResource)
311 .append(", mRefCount=")
312 .append(mRefCount)
313 .append(", mChildren=")
314 .append(mChildren)
315 .append("}")
316 .toString();
317 }
318 }
319
Benedict Wongcbd329b2017-12-13 17:16:53 -0800320 /**
321 * Very simple counting class that looks much like a counting semaphore
322 *
323 * <p>This class is not thread-safe, and expects that that users of this class will ensure
324 * synchronization and thread safety by holding the IpSecService.this instance lock.
325 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800326 @VisibleForTesting
327 static class ResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700328 private final int mMax;
329 int mCurrent;
330
331 ResourceTracker(int max) {
332 mMax = max;
333 mCurrent = 0;
334 }
335
Benedict Wong6855aee2017-11-16 15:27:22 -0800336 boolean isAvailable() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700337 return (mCurrent < mMax);
338 }
339
Benedict Wong6855aee2017-11-16 15:27:22 -0800340 void take() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700341 if (!isAvailable()) {
342 Log.wtf(TAG, "Too many resources allocated!");
343 }
344 mCurrent++;
345 }
346
Benedict Wong6855aee2017-11-16 15:27:22 -0800347 void give() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700348 if (mCurrent <= 0) {
349 Log.wtf(TAG, "We've released this resource too many times");
350 }
351 mCurrent--;
352 }
ludi529fdec2017-08-10 15:44:40 -0700353
354 @Override
355 public String toString() {
356 return new StringBuilder()
357 .append("{mCurrent=")
358 .append(mCurrent)
359 .append(", mMax=")
360 .append(mMax)
361 .append("}")
362 .toString();
363 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700364 }
365
Benedict Wong6855aee2017-11-16 15:27:22 -0800366 @VisibleForTesting
367 static final class UserRecord {
Benedict Wong6855aee2017-11-16 15:27:22 -0800368 /* Maximum number of each type of resource that a single UID may possess */
Benedict Wong7bcf9c22020-02-11 23:36:42 -0800369
370 // Up to 4 active VPNs/IWLAN with potential soft handover.
371 public static final int MAX_NUM_TUNNEL_INTERFACES = 8;
372 public static final int MAX_NUM_ENCAP_SOCKETS = 16;
373
374 // SPIs and Transforms are both cheap, and are 1:1 correlated.
375 public static final int MAX_NUM_TRANSFORMS = 64;
376 public static final int MAX_NUM_SPIS = 64;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700377
Benedict Wongcbd329b2017-12-13 17:16:53 -0800378 /**
379 * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
380 * and explicit (user) reference management.
381 *
382 * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
383 *
384 * <p>Resources are removed from this array when the user releases their explicit reference
385 * by calling one of the releaseResource() methods.
386 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800387 final RefcountedResourceArray<SpiRecord> mSpiRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800388 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800389 final RefcountedResourceArray<TransformRecord> mTransformRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800390 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800391 final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800392 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
Benedict Wong8bc90732018-01-18 18:31:45 -0800393 final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
394 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
Benedict Wongcbd329b2017-12-13 17:16:53 -0800395
396 /**
397 * Trackers for quotas for each of the OwnedResource types.
398 *
399 * <p>These trackers are separate from the resource arrays, since they are incremented and
400 * decremented at different points in time. Specifically, quota is only returned upon final
401 * resource deallocation (after all explicit and implicit references are released). Note
402 * that it is possible that calls to releaseResource() will not return the used quota if
403 * there are other resources that depend on (are parents of) the resource being released.
404 */
405 final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
406 final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
Benedict Wong6855aee2017-11-16 15:27:22 -0800407 final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
Benedict Wong8bc90732018-01-18 18:31:45 -0800408 final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
Benedict Wong6855aee2017-11-16 15:27:22 -0800409
410 void removeSpiRecord(int resourceId) {
411 mSpiRecords.remove(resourceId);
Nathan Harold6e4681c2017-04-24 16:16:34 -0700412 }
413
Benedict Wong6855aee2017-11-16 15:27:22 -0800414 void removeTransformRecord(int resourceId) {
415 mTransformRecords.remove(resourceId);
416 }
417
Benedict Wong8bc90732018-01-18 18:31:45 -0800418 void removeTunnelInterfaceRecord(int resourceId) {
419 mTunnelInterfaceRecords.remove(resourceId);
420 }
421
Benedict Wong6855aee2017-11-16 15:27:22 -0800422 void removeEncapSocketRecord(int resourceId) {
423 mEncapSocketRecords.remove(resourceId);
424 }
425
426 @Override
427 public String toString() {
428 return new StringBuilder()
429 .append("{mSpiQuotaTracker=")
430 .append(mSpiQuotaTracker)
431 .append(", mTransformQuotaTracker=")
432 .append(mTransformQuotaTracker)
433 .append(", mSocketQuotaTracker=")
434 .append(mSocketQuotaTracker)
Benedict Wong76603702018-01-24 15:31:39 -0800435 .append(", mTunnelQuotaTracker=")
436 .append(mTunnelQuotaTracker)
Benedict Wong6855aee2017-11-16 15:27:22 -0800437 .append(", mSpiRecords=")
438 .append(mSpiRecords)
439 .append(", mTransformRecords=")
440 .append(mTransformRecords)
441 .append(", mEncapSocketRecords=")
442 .append(mEncapSocketRecords)
Benedict Wong76603702018-01-24 15:31:39 -0800443 .append(", mTunnelInterfaceRecords=")
444 .append(mTunnelInterfaceRecords)
Benedict Wong6855aee2017-11-16 15:27:22 -0800445 .append("}")
446 .toString();
447 }
448 }
449
Benedict Wongcbd329b2017-12-13 17:16:53 -0800450 /**
451 * This class is not thread-safe, and expects that that users of this class will ensure
452 * synchronization and thread safety by holding the IpSecService.this instance lock.
453 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800454 @VisibleForTesting
455 static final class UserResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700456 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
457
Benedict Wongcbd329b2017-12-13 17:16:53 -0800458 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
Benedict Wong6855aee2017-11-16 15:27:22 -0800459 public UserRecord getUserRecord(int uid) {
460 checkCallerUid(uid);
461
Nathan Harold6e4681c2017-04-24 16:16:34 -0700462 UserRecord r = mUserRecords.get(uid);
463 if (r == null) {
464 r = new UserRecord();
465 mUserRecords.put(uid, r);
466 }
467 return r;
468 }
ludi529fdec2017-08-10 15:44:40 -0700469
Benedict Wong6855aee2017-11-16 15:27:22 -0800470 /** Safety method; guards against access of other user's UserRecords */
471 private void checkCallerUid(int uid) {
Benedict Wong908d34e2021-04-15 11:59:16 -0700472 if (uid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800473 throw new SecurityException("Attempted access of unowned resources");
474 }
475 }
476
ludi529fdec2017-08-10 15:44:40 -0700477 @Override
478 public String toString() {
479 return mUserRecords.toString();
480 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700481 }
482
Benedict Wong6855aee2017-11-16 15:27:22 -0800483 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700484
Nathan Harold80865392017-04-04 19:37:48 -0700485 /**
Benedict Wongcbd329b2017-12-13 17:16:53 -0800486 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
Benedict Wong6855aee2017-11-16 15:27:22 -0800487 * resources. It relies on a provided resourceId that should uniquely identify the kernel
488 * resource. To use this class, the user should implement the invalidate() and
489 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
Benedict Wongcbd329b2017-12-13 17:16:53 -0800490 * tracking arrays and kernel resources, respectively.
491 *
492 * <p>This class associates kernel resources with the UID that owns and controls them.
Nathan Harold80865392017-04-04 19:37:48 -0700493 */
Benedict Wongcbd329b2017-12-13 17:16:53 -0800494 private abstract class OwnedResourceRecord implements IResource {
Aaron Huangfbae3082021-12-06 15:18:42 +0800495 final int mPid;
496 final int mUid;
Benedict Wong6855aee2017-11-16 15:27:22 -0800497 protected final int mResourceId;
Nathan Harold031acb82017-03-07 13:23:36 -0800498
Benedict Wongcbd329b2017-12-13 17:16:53 -0800499 OwnedResourceRecord(int resourceId) {
Nathan Harold031acb82017-03-07 13:23:36 -0800500 super();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700501 if (resourceId == INVALID_RESOURCE_ID) {
502 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
503 }
Nathan Harold80865392017-04-04 19:37:48 -0700504 mResourceId = resourceId;
Aaron Huangfbae3082021-12-06 15:18:42 +0800505 mPid = Binder.getCallingPid();
506 mUid = Binder.getCallingUid();
Nathan Harold031acb82017-03-07 13:23:36 -0800507
Nathan Harold6e4681c2017-04-24 16:16:34 -0700508 getResourceTracker().take();
Nathan Harold031acb82017-03-07 13:23:36 -0800509 }
510
Benedict Wong6855aee2017-11-16 15:27:22 -0800511 @Override
512 public abstract void invalidate() throws RemoteException;
513
514 /** Convenience method; retrieves the user resource record for the stored UID. */
515 protected UserRecord getUserRecord() {
Aaron Huangfbae3082021-12-06 15:18:42 +0800516 return mUserResourceTracker.getUserRecord(mUid);
Nathan Harold80865392017-04-04 19:37:48 -0700517 }
518
Benedict Wong6855aee2017-11-16 15:27:22 -0800519 @Override
520 public abstract void freeUnderlyingResources() throws RemoteException;
ludi89194d62017-05-22 10:52:23 -0700521
Nathan Harold6e4681c2017-04-24 16:16:34 -0700522 /** Get the resource tracker for this resource */
523 protected abstract ResourceTracker getResourceTracker();
524
ludi89194d62017-05-22 10:52:23 -0700525 @Override
526 public String toString() {
527 return new StringBuilder()
528 .append("{mResourceId=")
529 .append(mResourceId)
530 .append(", pid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800531 .append(mPid)
ludi89194d62017-05-22 10:52:23 -0700532 .append(", uid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800533 .append(mUid)
ludi89194d62017-05-22 10:52:23 -0700534 .append("}")
535 .toString();
536 }
Nathan Harold031acb82017-03-07 13:23:36 -0800537 };
538
Nathan Harold80865392017-04-04 19:37:48 -0700539 /**
Benedict Wong6855aee2017-11-16 15:27:22 -0800540 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
541 *
542 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
543 * if a key is not found during a retrieval process.
Nathan Harold80865392017-04-04 19:37:48 -0700544 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800545 static class RefcountedResourceArray<T extends IResource> {
546 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
547 private final String mTypeName;
Nathan Harold031acb82017-03-07 13:23:36 -0800548
Aaron Huangfbae3082021-12-06 15:18:42 +0800549 RefcountedResourceArray(String typeName) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800550 this.mTypeName = typeName;
Nathan Harold80865392017-04-04 19:37:48 -0700551 }
552
Benedict Wong6855aee2017-11-16 15:27:22 -0800553 /**
554 * Accessor method to get inner resource object.
555 *
556 * @throws IllegalArgumentException if no resource with provided key is found.
557 */
558 T getResourceOrThrow(int key) {
559 return getRefcountedResourceOrThrow(key).getResource();
560 }
561
562 /**
563 * Accessor method to get reference counting wrapper.
564 *
565 * @throws IllegalArgumentException if no resource with provided key is found.
566 */
567 RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
568 RefcountedResource<T> resource = mArray.get(key);
569 if (resource == null) {
570 throw new IllegalArgumentException(
571 String.format("No such %s found for given id: %d", mTypeName, key));
572 }
573
574 return resource;
575 }
576
577 void put(int key, RefcountedResource<T> obj) {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +0000578 Objects.requireNonNull(obj, "Null resources cannot be added");
Nathan Harold80865392017-04-04 19:37:48 -0700579 mArray.put(key, obj);
580 }
581
582 void remove(int key) {
583 mArray.remove(key);
584 }
ludi89194d62017-05-22 10:52:23 -0700585
586 @Override
587 public String toString() {
588 return mArray.toString();
589 }
Nathan Harold80865392017-04-04 19:37:48 -0700590 }
591
Benedict Wongcbd329b2017-12-13 17:16:53 -0800592 /**
593 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
594 * created, the SpiRecord that originally tracked the SAs will reliquish the
595 * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
596 */
597 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700598 private final IpSecConfig mConfig;
Nathan Harold5a19b952018-01-05 19:25:13 -0800599 private final SpiRecord mSpi;
Benedict Wong6855aee2017-11-16 15:27:22 -0800600 private final EncapSocketRecord mSocket;
Nathan Harold80865392017-04-04 19:37:48 -0700601
602 TransformRecord(
Nathan Harold5a19b952018-01-05 19:25:13 -0800603 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800604 super(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -0800605 mConfig = config;
Nathan Harold5a19b952018-01-05 19:25:13 -0800606 mSpi = spi;
Nathan Harold80865392017-04-04 19:37:48 -0700607 mSocket = socket;
Benedict Wong68aac2a2017-12-13 18:26:40 -0800608
609 spi.setOwnedByTransform();
Nathan Harold031acb82017-03-07 13:23:36 -0800610 }
611
612 public IpSecConfig getConfig() {
613 return mConfig;
614 }
615
Nathan Harold5a19b952018-01-05 19:25:13 -0800616 public SpiRecord getSpiRecord() {
617 return mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700618 }
619
Benedict Wong8edc5572018-01-19 17:36:02 -0800620 public EncapSocketRecord getSocketRecord() {
621 return mSocket;
622 }
623
Nathan Harold80865392017-04-04 19:37:48 -0700624 /** always guarded by IpSecService#this */
Nathan Harold031acb82017-03-07 13:23:36 -0800625 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800626 public void freeUnderlyingResources() {
Nathan Harold5a19b952018-01-05 19:25:13 -0800627 int spi = mSpi.getSpi();
628 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800629 mNetd.ipSecDeleteSecurityAssociation(
630 mUid,
631 mConfig.getSourceAddress(),
632 mConfig.getDestinationAddress(),
633 spi,
634 mConfig.getMarkValue(),
635 mConfig.getMarkMask(),
636 mConfig.getXfrmInterfaceId());
Benedict Wong97c3c942018-03-01 18:53:07 -0800637 } catch (RemoteException | ServiceSpecificException e) {
638 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800639 }
Nathan Harold031acb82017-03-07 13:23:36 -0800640
Benedict Wong6855aee2017-11-16 15:27:22 -0800641 getResourceTracker().give();
Nathan Harold031acb82017-03-07 13:23:36 -0800642 }
ludi89194d62017-05-22 10:52:23 -0700643
Benedict Wong6855aee2017-11-16 15:27:22 -0800644 @Override
645 public void invalidate() throws RemoteException {
646 getUserRecord().removeTransformRecord(mResourceId);
647 }
648
649 @Override
Nathan Harold6e4681c2017-04-24 16:16:34 -0700650 protected ResourceTracker getResourceTracker() {
Benedict Wong6855aee2017-11-16 15:27:22 -0800651 return getUserRecord().mTransformQuotaTracker;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700652 }
653
ludi89194d62017-05-22 10:52:23 -0700654 @Override
655 public String toString() {
656 StringBuilder strBuilder = new StringBuilder();
657 strBuilder
658 .append("{super=")
659 .append(super.toString())
660 .append(", mSocket=")
661 .append(mSocket)
Nathan Harold5a19b952018-01-05 19:25:13 -0800662 .append(", mSpi.mResourceId=")
663 .append(mSpi.mResourceId)
ludi89194d62017-05-22 10:52:23 -0700664 .append(", mConfig=")
665 .append(mConfig)
666 .append("}");
667 return strBuilder.toString();
668 }
Nathan Harold031acb82017-03-07 13:23:36 -0800669 }
670
Benedict Wongcbd329b2017-12-13 17:16:53 -0800671 /**
672 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
673 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
674 * object
675 */
676 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harold5a19b952018-01-05 19:25:13 -0800677 private final String mSourceAddress;
678 private final String mDestinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800679 private int mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700680
681 private boolean mOwnedByTransform = false;
Nathan Harold031acb82017-03-07 13:23:36 -0800682
Aaron Huang2617cf52021-11-29 16:31:32 +0800683 SpiRecord(int resourceId, String sourceAddress,
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800684 String destinationAddress, int spi) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800685 super(resourceId);
Nathan Harold5a19b952018-01-05 19:25:13 -0800686 mSourceAddress = sourceAddress;
687 mDestinationAddress = destinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800688 mSpi = spi;
Nathan Harold031acb82017-03-07 13:23:36 -0800689 }
690
Nathan Harold80865392017-04-04 19:37:48 -0700691 /** always guarded by IpSecService#this */
692 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800693 public void freeUnderlyingResources() {
Nathan Harold031acb82017-03-07 13:23:36 -0800694 try {
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800695 if (!mOwnedByTransform) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800696 mNetd.ipSecDeleteSecurityAssociation(
697 mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
698 0 /* mask */, 0 /* if_id */);
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800699 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800700 } catch (ServiceSpecificException | RemoteException e) {
701 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800702 }
Nathan Harold80865392017-04-04 19:37:48 -0700703
704 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -0800705
Benedict Wong6855aee2017-11-16 15:27:22 -0800706 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700707 }
708
Nathan Harold80865392017-04-04 19:37:48 -0700709 public int getSpi() {
710 return mSpi;
711 }
712
Nathan Harold5a19b952018-01-05 19:25:13 -0800713 public String getDestinationAddress() {
714 return mDestinationAddress;
715 }
716
Nathan Harold80865392017-04-04 19:37:48 -0700717 public void setOwnedByTransform() {
718 if (mOwnedByTransform) {
719 // Programming error
Andreas Gampeafb01e22017-07-11 10:25:09 -0700720 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold80865392017-04-04 19:37:48 -0700721 }
722
723 mOwnedByTransform = true;
Nathan Harold031acb82017-03-07 13:23:36 -0800724 }
ludi89194d62017-05-22 10:52:23 -0700725
Benedict Wong68aac2a2017-12-13 18:26:40 -0800726 public boolean getOwnedByTransform() {
727 return mOwnedByTransform;
728 }
729
ludi89194d62017-05-22 10:52:23 -0700730 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800731 public void invalidate() throws RemoteException {
732 getUserRecord().removeSpiRecord(mResourceId);
733 }
734
735 @Override
736 protected ResourceTracker getResourceTracker() {
737 return getUserRecord().mSpiQuotaTracker;
738 }
739
740 @Override
ludi89194d62017-05-22 10:52:23 -0700741 public String toString() {
742 StringBuilder strBuilder = new StringBuilder();
743 strBuilder
744 .append("{super=")
745 .append(super.toString())
746 .append(", mSpi=")
747 .append(mSpi)
Nathan Harold5a19b952018-01-05 19:25:13 -0800748 .append(", mSourceAddress=")
749 .append(mSourceAddress)
750 .append(", mDestinationAddress=")
751 .append(mDestinationAddress)
ludi89194d62017-05-22 10:52:23 -0700752 .append(", mOwnedByTransform=")
753 .append(mOwnedByTransform)
754 .append("}");
755 return strBuilder.toString();
756 }
Nathan Harold031acb82017-03-07 13:23:36 -0800757 }
758
Benedict Wong8bc90732018-01-18 18:31:45 -0800759 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
lucaslin7eb76592021-03-11 17:39:49 +0800760 final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
761 private int mNextTunnelNetId = mNetIdRange.getLower();
Benedict Wong8bc90732018-01-18 18:31:45 -0800762
763 /**
764 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
765 *
766 * <p>This method should only be called from Binder threads. Do not call this from within the
767 * system server as it will crash the system on failure.
768 *
769 * @return an integer key within the netId range, if successful
770 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
771 */
772 @VisibleForTesting
773 int reserveNetId() {
lucaslin7eb76592021-03-11 17:39:49 +0800774 final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
Benedict Wong8bc90732018-01-18 18:31:45 -0800775 synchronized (mTunnelNetIds) {
lucaslin7eb76592021-03-11 17:39:49 +0800776 for (int i = 0; i < range; i++) {
777 final int netId = mNextTunnelNetId;
778 if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
779 mNextTunnelNetId = mNetIdRange.getLower();
780 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800781 if (!mTunnelNetIds.get(netId)) {
782 mTunnelNetIds.put(netId, true);
783 return netId;
784 }
785 }
786 }
787 throw new IllegalStateException("No free netIds to allocate");
788 }
789
790 @VisibleForTesting
791 void releaseNetId(int netId) {
792 synchronized (mTunnelNetIds) {
793 mTunnelNetIds.delete(netId);
794 }
795 }
796
Yan Yana2f3b492020-09-29 23:38:00 -0700797 /**
798 * Tracks an tunnel interface, and manages cleanup paths.
799 *
800 * <p>This class is not thread-safe, and expects that that users of this class will ensure
801 * synchronization and thread safety by holding the IpSecService.this instance lock
802 */
803 @VisibleForTesting
804 final class TunnelInterfaceRecord extends OwnedResourceRecord {
Benedict Wong8bc90732018-01-18 18:31:45 -0800805 private final String mInterfaceName;
Benedict Wong8bc90732018-01-18 18:31:45 -0800806
807 // outer addresses
808 private final String mLocalAddress;
809 private final String mRemoteAddress;
810
811 private final int mIkey;
812 private final int mOkey;
813
Benedict Wong5d749842018-09-06 11:31:25 -0700814 private final int mIfId;
815
Yan Yana2f3b492020-09-29 23:38:00 -0700816 private Network mUnderlyingNetwork;
817
Benedict Wong8bc90732018-01-18 18:31:45 -0800818 TunnelInterfaceRecord(
819 int resourceId,
820 String interfaceName,
821 Network underlyingNetwork,
822 String localAddr,
823 String remoteAddr,
824 int ikey,
Benedict Wong5d749842018-09-06 11:31:25 -0700825 int okey,
826 int intfId) {
Benedict Wong8bc90732018-01-18 18:31:45 -0800827 super(resourceId);
828
829 mInterfaceName = interfaceName;
830 mUnderlyingNetwork = underlyingNetwork;
831 mLocalAddress = localAddr;
832 mRemoteAddress = remoteAddr;
833 mIkey = ikey;
834 mOkey = okey;
Benedict Wong5d749842018-09-06 11:31:25 -0700835 mIfId = intfId;
Benedict Wong8bc90732018-01-18 18:31:45 -0800836 }
837
838 /** always guarded by IpSecService#this */
839 @Override
840 public void freeUnderlyingResources() {
Benedict Wong8edc5572018-01-19 17:36:02 -0800841 // Calls to netd
Benedict Wong8bc90732018-01-18 18:31:45 -0800842 // Teardown VTI
843 // Delete global policies
Benedict Wong8edc5572018-01-19 17:36:02 -0800844 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800845 mNetd.ipSecRemoveTunnelInterface(mInterfaceName);
Benedict Wong8edc5572018-01-19 17:36:02 -0800846
Benedict Wong38e52972018-05-07 20:06:44 -0700847 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800848 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800849 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700850 selAddrFamily,
851 IpSecManager.DIRECTION_OUT,
852 mOkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700853 0xffffffff,
854 mIfId);
Aaron Huang2617cf52021-11-29 16:31:32 +0800855 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800856 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700857 selAddrFamily,
858 IpSecManager.DIRECTION_IN,
859 mIkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700860 0xffffffff,
861 mIfId);
Yan Yanf4eceba2022-10-31 20:41:13 +0000862 mNetd.ipSecDeleteSecurityPolicy(
863 mUid,
864 selAddrFamily,
865 IpSecManager.DIRECTION_FWD,
866 mIkey,
867 0xffffffff,
868 mIfId);
Benedict Wong8edc5572018-01-19 17:36:02 -0800869 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800870 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong8edc5572018-01-19 17:36:02 -0800871 Log.e(
872 TAG,
873 "Failed to delete VTI with interface name: "
874 + mInterfaceName
875 + " and id: "
Benedict Wong97c3c942018-03-01 18:53:07 -0800876 + mResourceId, e);
Benedict Wong8edc5572018-01-19 17:36:02 -0800877 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800878
879 getResourceTracker().give();
880 releaseNetId(mIkey);
881 releaseNetId(mOkey);
882 }
883
Yan Yana2f3b492020-09-29 23:38:00 -0700884 @GuardedBy("IpSecService.this")
885 public void setUnderlyingNetwork(Network underlyingNetwork) {
886 // When #applyTunnelModeTransform is called, this new underlying network will be used to
887 // update the output mark of the input transform.
888 mUnderlyingNetwork = underlyingNetwork;
Benedict Wong8bc90732018-01-18 18:31:45 -0800889 }
890
Yan Yana2f3b492020-09-29 23:38:00 -0700891 @GuardedBy("IpSecService.this")
Benedict Wong8bc90732018-01-18 18:31:45 -0800892 public Network getUnderlyingNetwork() {
893 return mUnderlyingNetwork;
894 }
895
Yan Yana2f3b492020-09-29 23:38:00 -0700896 public String getInterfaceName() {
897 return mInterfaceName;
898 }
899
Benedict Wong8bc90732018-01-18 18:31:45 -0800900 /** Returns the local, outer address for the tunnelInterface */
901 public String getLocalAddress() {
902 return mLocalAddress;
903 }
904
905 /** Returns the remote, outer address for the tunnelInterface */
906 public String getRemoteAddress() {
907 return mRemoteAddress;
908 }
909
910 public int getIkey() {
911 return mIkey;
912 }
913
914 public int getOkey() {
915 return mOkey;
916 }
917
Benedict Wong5d749842018-09-06 11:31:25 -0700918 public int getIfId() {
919 return mIfId;
920 }
921
Benedict Wong8bc90732018-01-18 18:31:45 -0800922 @Override
923 protected ResourceTracker getResourceTracker() {
924 return getUserRecord().mTunnelQuotaTracker;
925 }
926
927 @Override
928 public void invalidate() {
929 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
930 }
931
932 @Override
933 public String toString() {
934 return new StringBuilder()
935 .append("{super=")
936 .append(super.toString())
937 .append(", mInterfaceName=")
938 .append(mInterfaceName)
939 .append(", mUnderlyingNetwork=")
940 .append(mUnderlyingNetwork)
941 .append(", mLocalAddress=")
942 .append(mLocalAddress)
943 .append(", mRemoteAddress=")
944 .append(mRemoteAddress)
945 .append(", mIkey=")
946 .append(mIkey)
947 .append(", mOkey=")
948 .append(mOkey)
949 .append("}")
950 .toString();
951 }
952 }
953
Benedict Wongcbd329b2017-12-13 17:16:53 -0800954 /**
955 * Tracks a UDP encap socket, and manages cleanup paths
956 *
957 * <p>While this class does not manage non-kernel resources, race conditions around socket
958 * binding require that the service creates the encap socket, binds it and applies the socket
959 * policy before handing it to a user.
960 */
961 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700962 private FileDescriptor mSocket;
963 private final int mPort;
Nathan Harold031acb82017-03-07 13:23:36 -0800964
Benedict Wong6855aee2017-11-16 15:27:22 -0800965 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
966 super(resourceId);
Nathan Harold80865392017-04-04 19:37:48 -0700967 mSocket = socket;
968 mPort = port;
969 }
970
971 /** always guarded by IpSecService#this */
972 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800973 public void freeUnderlyingResources() {
Nathan Harold80865392017-04-04 19:37:48 -0700974 Log.d(TAG, "Closing port " + mPort);
975 IoUtils.closeQuietly(mSocket);
976 mSocket = null;
Nathan Harold80865392017-04-04 19:37:48 -0700977
Benedict Wong6855aee2017-11-16 15:27:22 -0800978 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700979 }
980
Nathan Harold80865392017-04-04 19:37:48 -0700981 public int getPort() {
982 return mPort;
983 }
984
Benedict Wonga386e372018-03-27 16:55:48 -0700985 public FileDescriptor getFileDescriptor() {
Nathan Harold80865392017-04-04 19:37:48 -0700986 return mSocket;
987 }
ludi89194d62017-05-22 10:52:23 -0700988
989 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800990 protected ResourceTracker getResourceTracker() {
991 return getUserRecord().mSocketQuotaTracker;
992 }
993
994 @Override
995 public void invalidate() {
996 getUserRecord().removeEncapSocketRecord(mResourceId);
997 }
998
999 @Override
ludi89194d62017-05-22 10:52:23 -07001000 public String toString() {
1001 return new StringBuilder()
1002 .append("{super=")
1003 .append(super.toString())
1004 .append(", mSocket=")
1005 .append(mSocket)
1006 .append(", mPort=")
1007 .append(mPort)
1008 .append("}")
1009 .toString();
1010 }
Nathan Harold80865392017-04-04 19:37:48 -07001011 }
Nathan Harold031acb82017-03-07 13:23:36 -08001012
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001013 /**
1014 * Constructs a new IpSecService instance
1015 *
1016 * @param context Binder context for this service
1017 */
Aaron Huangb944ff12022-01-12 15:11:01 +08001018 public IpSecService(Context context) {
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001019 this(context, new Dependencies());
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001020 }
1021
Nathan Harold65ef8432018-03-15 18:06:06 -07001022 @NonNull
1023 private AppOpsManager getAppOpsManager() {
1024 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Aaron Huangfbae3082021-12-06 15:18:42 +08001025 if (appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
Nathan Harold65ef8432018-03-15 18:06:06 -07001026 return appOps;
1027 }
1028
ludi5e623ea2017-05-12 09:15:00 -07001029 /** @hide */
1030 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001031 public IpSecService(Context context, Dependencies deps) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001032 this(
1033 context,
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001034 deps,
Nathan Harold5a19b952018-01-05 19:25:13 -08001035 (fd, uid) -> {
1036 try {
1037 TrafficStats.setThreadStatsUid(uid);
1038 TrafficStats.tagFileDescriptor(fd);
1039 } finally {
1040 TrafficStats.clearThreadStatsUid();
1041 }
1042 });
Benedict Wong083faee2017-12-03 19:42:36 -08001043 }
1044
1045 /** @hide */
1046 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001047 public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
ludi5e623ea2017-05-12 09:15:00 -07001048 mContext = context;
Aaron Huang2617cf52021-11-29 16:31:32 +08001049 mDeps = Objects.requireNonNull(deps, "Missing dependencies.");
Benedict Wong083faee2017-12-03 19:42:36 -08001050 mUidFdTagger = uidFdTagger;
Aaron Huang2617cf52021-11-29 16:31:32 +08001051 try {
1052 mNetd = mDeps.getNetdInstance(mContext);
1053 } catch (RemoteException e) {
1054 throw e.rethrowFromSystemServer();
1055 }
ludi5e623ea2017-05-12 09:15:00 -07001056 }
1057
Nathan Harold19b99d92017-08-23 13:46:33 -07001058 /**
1059 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1060 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1061 */
1062 private static void checkInetAddress(String inetAddress) {
1063 if (TextUtils.isEmpty(inetAddress)) {
1064 throw new IllegalArgumentException("Unspecified address");
1065 }
1066
Serik Beketayev7f507332020-12-06 22:31:23 -08001067 InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
Nathan Harold19b99d92017-08-23 13:46:33 -07001068
1069 if (checkAddr.isAnyLocalAddress()) {
1070 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1071 }
1072 }
1073
1074 /**
1075 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1076 * DIRECTION_IN or DIRECTION_OUT
1077 */
Benedict Wong908d34e2021-04-15 11:59:16 -07001078 private void checkDirection(int direction) {
Nathan Harold19b99d92017-08-23 13:46:33 -07001079 switch (direction) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001080 case IpSecManager.DIRECTION_OUT:
1081 case IpSecManager.DIRECTION_IN:
Nathan Harold19b99d92017-08-23 13:46:33 -07001082 return;
Benedict Wong908d34e2021-04-15 11:59:16 -07001083 case IpSecManager.DIRECTION_FWD:
Benedict Wong47b528c2021-05-10 18:26:02 -07001084 // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
Benedict Wong908d34e2021-04-15 11:59:16 -07001085 PermissionUtils.enforceNetworkStackPermission(mContext);
1086 return;
Nathan Harold19b99d92017-08-23 13:46:33 -07001087 }
1088 throw new IllegalArgumentException("Invalid Direction: " + direction);
1089 }
1090
Nathan Harold031acb82017-03-07 13:23:36 -08001091 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri20e96c52017-11-16 10:58:01 -08001092 @Override
1093 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harold5a19b952018-01-05 19:25:13 -08001094 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1095 checkInetAddress(destinationAddress);
Nathan Harold1b88f0e2018-03-28 08:52:51 -07001096 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1097 if (requestedSpi > 0 && requestedSpi < 256) {
1098 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1099 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001100 Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harold19b99d92017-08-23 13:46:33 -07001101
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001102 int callingUid = Binder.getCallingUid();
1103 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001104 final int resourceId = mNextResourceId++;
Nathan Harold031acb82017-03-07 13:23:36 -08001105
1106 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -08001107 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001108 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001109 return new IpSecSpiResponse(
Nathan Harold19b99d92017-08-23 13:46:33 -07001110 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold6e4681c2017-04-24 16:16:34 -07001111 }
Nathan Harold5a19b952018-01-05 19:25:13 -08001112
Aaron Huang2617cf52021-11-29 16:31:32 +08001113 spi = mNetd.ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold031acb82017-03-07 13:23:36 -08001114 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong6855aee2017-11-16 15:27:22 -08001115 userRecord.mSpiRecords.put(
Nathan Harold80865392017-04-04 19:37:48 -07001116 resourceId,
Benedict Wong6855aee2017-11-16 15:27:22 -08001117 new RefcountedResource<SpiRecord>(
Aaron Huang2617cf52021-11-29 16:31:32 +08001118 new SpiRecord(resourceId, "",
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001119 destinationAddress, spi), binder));
Nathan Harold031acb82017-03-07 13:23:36 -08001120 } catch (ServiceSpecificException e) {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001121 if (e.errorCode == OsConstants.ENOENT) {
1122 return new IpSecSpiResponse(
1123 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1124 }
1125 throw e;
Nathan Harold031acb82017-03-07 13:23:36 -08001126 } catch (RemoteException e) {
1127 throw e.rethrowFromSystemServer();
1128 }
Nathan Harold80865392017-04-04 19:37:48 -07001129 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1130 }
1131
1132 /* This method should only be called from Binder threads. Do not call this from
1133 * within the system server as it will crash the system on failure.
1134 */
Benedict Wong6855aee2017-11-16 15:27:22 -08001135 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold80865392017-04-04 19:37:48 -07001136 throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001137 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold031acb82017-03-07 13:23:36 -08001138 }
1139
1140 /** Release a previously allocated SPI that has been registered with the system server */
1141 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001142 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1143 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1144 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001145 }
1146
1147 /**
1148 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1149 * be unbound.
1150 *
1151 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1152 * a random open port and then bind by number, this function creates a temp socket, binds to a
1153 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1154 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1155 * FileHandle.
1156 *
1157 * <p>The loop in this function handles the inherent race window between un-binding to a port
1158 * and re-binding, during which the system could *technically* hand that port out to someone
1159 * else.
1160 */
Benedict Wongc423cc82017-10-10 20:44:28 -07001161 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold80865392017-04-04 19:37:48 -07001162 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1163 try {
1164 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1165 Os.bind(probeSocket, INADDR_ANY, 0);
1166 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1167 Os.close(probeSocket);
1168 Log.v(TAG, "Binding to port " + port);
1169 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongc423cc82017-10-10 20:44:28 -07001170 return port;
Nathan Harold80865392017-04-04 19:37:48 -07001171 } catch (ErrnoException e) {
1172 // Someone miraculously claimed the port just after we closed probeSocket.
1173 if (e.errno == OsConstants.EADDRINUSE) {
1174 continue;
1175 }
1176 throw e.rethrowAsIOException();
1177 }
1178 }
1179 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1180 }
Nathan Harold031acb82017-03-07 13:23:36 -08001181
1182 /**
Benedict Wong083faee2017-12-03 19:42:36 -08001183 * Functional interface to do traffic tagging of given sockets to UIDs.
1184 *
1185 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1186 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1187 *
1188 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1189 * methods that cannot be easily mocked/tested.
1190 */
1191 @VisibleForTesting
1192 public interface UidFdTagger {
1193 /**
1194 * Sets socket tag to assign all traffic to the provided UID.
1195 *
1196 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1197 * should be accounted to the UID of the unprivileged application.
1198 */
Aaron Huangfbae3082021-12-06 15:18:42 +08001199 void tag(FileDescriptor fd, int uid) throws IOException;
Benedict Wong083faee2017-12-03 19:42:36 -08001200 }
1201
1202 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001203 * Open a socket via the system server and bind it to the specified port (random if port=0).
1204 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1205 * cache the socket and a record of its owner so that it can and must be freed when no longer
1206 * needed.
1207 */
1208 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001209 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1210 throws RemoteException {
1211 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1212 throw new IllegalArgumentException(
1213 "Specified port number must be a valid non-reserved UDP port");
1214 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001215 Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
Nathan Harold19b99d92017-08-23 13:46:33 -07001216
Benedict Wong083faee2017-12-03 19:42:36 -08001217 int callingUid = Binder.getCallingUid();
1218 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001219 final int resourceId = mNextResourceId++;
Aaron Huangb01254f2021-12-23 10:47:05 +08001220
1221 ParcelFileDescriptor pFd = null;
Nathan Harold80865392017-04-04 19:37:48 -07001222 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001223 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001224 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1225 }
1226
Aaron Huangb01254f2021-12-23 10:47:05 +08001227 FileDescriptor sockFd = null;
1228 try {
1229 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1230 pFd = ParcelFileDescriptor.dup(sockFd);
1231 } finally {
1232 IoUtils.closeQuietly(sockFd);
1233 }
Nathan Harold80865392017-04-04 19:37:48 -07001234
Aaron Huangb01254f2021-12-23 10:47:05 +08001235 mUidFdTagger.tag(pFd.getFileDescriptor(), callingUid);
Nathan Harold80865392017-04-04 19:37:48 -07001236 // This code is common to both the unspecified and specified port cases
1237 Os.setsockoptInt(
Aaron Huangb01254f2021-12-23 10:47:05 +08001238 pFd.getFileDescriptor(),
Nathan Harold80865392017-04-04 19:37:48 -07001239 OsConstants.IPPROTO_UDP,
1240 OsConstants.UDP_ENCAP,
1241 OsConstants.UDP_ENCAP_ESPINUDP);
1242
Aaron Huangb01254f2021-12-23 10:47:05 +08001243 mNetd.ipSecSetEncapSocketOwner(pFd, callingUid);
Benedict Wong17687442017-12-06 21:56:35 -08001244 if (port != 0) {
1245 Log.v(TAG, "Binding to port " + port);
Aaron Huangb01254f2021-12-23 10:47:05 +08001246 Os.bind(pFd.getFileDescriptor(), INADDR_ANY, port);
Benedict Wong17687442017-12-06 21:56:35 -08001247 } else {
Aaron Huangb01254f2021-12-23 10:47:05 +08001248 port = bindToRandomPort(pFd.getFileDescriptor());
Benedict Wong17687442017-12-06 21:56:35 -08001249 }
1250
Benedict Wong6855aee2017-11-16 15:27:22 -08001251 userRecord.mEncapSocketRecords.put(
1252 resourceId,
1253 new RefcountedResource<EncapSocketRecord>(
Aaron Huangb01254f2021-12-23 10:47:05 +08001254 new EncapSocketRecord(resourceId, pFd.getFileDescriptor(), port),
1255 binder));
1256 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port,
1257 pFd.getFileDescriptor());
Nathan Harold80865392017-04-04 19:37:48 -07001258 } catch (IOException | ErrnoException e) {
Aaron Huangb01254f2021-12-23 10:47:05 +08001259 try {
1260 if (pFd != null) {
1261 pFd.close();
1262 }
1263 } catch (IOException ex) {
1264 // Nothing can be done at this point
1265 Log.e(TAG, "Failed to close pFd.");
1266 }
Nathan Harold80865392017-04-04 19:37:48 -07001267 }
1268 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1269 // The only reasonable condition that would cause that is resource unavailable.
1270 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold031acb82017-03-07 13:23:36 -08001271 }
1272
1273 /** close a socket that has been been allocated by and registered with the system server */
1274 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001275 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1276 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1277 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001278 }
Nathan Harold031acb82017-03-07 13:23:36 -08001279
Benedict Wong8bc90732018-01-18 18:31:45 -08001280 /**
1281 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1282 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1283 * needed.
1284 */
1285 @Override
1286 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001287 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1288 String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001289 enforceTunnelFeatureAndPermissions(callingPackage);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001290 Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
1291 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
Benedict Wong8bc90732018-01-18 18:31:45 -08001292 checkInetAddress(localAddr);
1293 checkInetAddress(remoteAddr);
1294
1295 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1296 // network (b/72316676).
1297
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001298 int callerUid = Binder.getCallingUid();
1299 UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001300 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1301 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1302 }
1303
1304 final int resourceId = mNextResourceId++;
1305 final int ikey = reserveNetId();
1306 final int okey = reserveNetId();
Nathan Harold7be7f452018-04-26 11:47:14 -07001307 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001308
Benedict Wong8edc5572018-01-19 17:36:02 -08001309 try {
1310 // Calls to netd:
1311 // Create VTI
1312 // Add inbound/outbound global policies
1313 // (use reqid = 0)
Aaron Huang2617cf52021-11-29 16:31:32 +08001314 mNetd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001315
paulhu00e34562021-10-26 09:00:50 +00001316 BinderUtils.withCleanCallingIdentity(() -> {
Aaron Huang2617cf52021-11-29 16:31:32 +08001317 NetdUtils.setInterfaceUp(mNetd, intfName);
Benedict Wong529e8aa2020-02-11 23:49:36 -08001318 });
1319
Benedict Wong38e52972018-05-07 20:06:44 -07001320 for (int selAddrFamily : ADDRESS_FAMILIES) {
1321 // Always send down correct local/remote addresses for template.
Aaron Huang2617cf52021-11-29 16:31:32 +08001322 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001323 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001324 selAddrFamily,
1325 IpSecManager.DIRECTION_OUT,
1326 localAddr,
1327 remoteAddr,
1328 0,
1329 okey,
Benedict Wong5d749842018-09-06 11:31:25 -07001330 0xffffffff,
1331 resourceId);
Aaron Huang2617cf52021-11-29 16:31:32 +08001332 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001333 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001334 selAddrFamily,
1335 IpSecManager.DIRECTION_IN,
1336 remoteAddr,
1337 localAddr,
1338 0,
1339 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001340 0xffffffff,
1341 resourceId);
Benedict Wong47b528c2021-05-10 18:26:02 -07001342
1343 // Add a forwarding policy on the tunnel interface. In order to support forwarding
1344 // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
1345 //
1346 // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
1347 // forwarding will be blocked by default (as would be the case if this policy was
1348 // absent).
1349 //
1350 // This is necessary only on the tunnel interface, and not any the interface to
1351 // which traffic will be forwarded to.
Aaron Huang2617cf52021-11-29 16:31:32 +08001352 mNetd.ipSecAddSecurityPolicy(
Benedict Wong908d34e2021-04-15 11:59:16 -07001353 callerUid,
1354 selAddrFamily,
1355 IpSecManager.DIRECTION_FWD,
1356 remoteAddr,
1357 localAddr,
1358 0,
1359 ikey,
1360 0xffffffff,
1361 resourceId);
Benedict Wong8edc5572018-01-19 17:36:02 -08001362 }
1363
1364 userRecord.mTunnelInterfaceRecords.put(
1365 resourceId,
1366 new RefcountedResource<TunnelInterfaceRecord>(
1367 new TunnelInterfaceRecord(
1368 resourceId,
1369 intfName,
1370 underlyingNetwork,
1371 localAddr,
1372 remoteAddr,
1373 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001374 okey,
1375 resourceId),
Benedict Wong8edc5572018-01-19 17:36:02 -08001376 binder));
1377 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1378 } catch (RemoteException e) {
1379 // Release keys if we got an error.
1380 releaseNetId(ikey);
1381 releaseNetId(okey);
1382 throw e.rethrowFromSystemServer();
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001383 } catch (Throwable t) {
1384 // Release keys if we got an error.
1385 releaseNetId(ikey);
1386 releaseNetId(okey);
1387 throw t;
Benedict Wong8edc5572018-01-19 17:36:02 -08001388 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001389 }
1390
1391 /**
1392 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1393 * from multiple local IP addresses over the same tunnel.
1394 */
1395 @Override
Benedict Wong97c3c942018-03-01 18:53:07 -08001396 public synchronized void addAddressToTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001397 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001398 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001399 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1400
1401 // Get tunnelInterface record; if no such interface is found, will throw
1402 // IllegalArgumentException
1403 TunnelInterfaceRecord tunnelInterfaceInfo =
1404 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1405
Benedict Wong97c3c942018-03-01 18:53:07 -08001406 try {
1407 // We can assume general validity of the IP address, since we get them as a
1408 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001409 mNetd.interfaceAddAddress(
1410 tunnelInterfaceInfo.mInterfaceName,
1411 localAddr.getAddress().getHostAddress(),
1412 localAddr.getPrefixLength());
Benedict Wong97c3c942018-03-01 18:53:07 -08001413 } catch (RemoteException e) {
1414 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001415 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001416 }
1417
1418 /**
1419 * Remove a new local address from the tunnel interface. After removal, the address will no
1420 * longer be available to send from, or receive on.
1421 */
1422 @Override
1423 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001424 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001425 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001426
Nathan Harold65ef8432018-03-15 18:06:06 -07001427 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8bc90732018-01-18 18:31:45 -08001428 // Get tunnelInterface record; if no such interface is found, will throw
1429 // IllegalArgumentException
1430 TunnelInterfaceRecord tunnelInterfaceInfo =
1431 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1432
Benedict Wong97c3c942018-03-01 18:53:07 -08001433 try {
1434 // We can assume general validity of the IP address, since we get them as a
1435 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001436 mNetd.interfaceDelAddress(
Benedict Wong97c3c942018-03-01 18:53:07 -08001437 tunnelInterfaceInfo.mInterfaceName,
1438 localAddr.getAddress().getHostAddress(),
1439 localAddr.getPrefixLength());
1440 } catch (RemoteException e) {
1441 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001442 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001443 }
1444
Yan Yana2f3b492020-09-29 23:38:00 -07001445 /** Set TunnelInterface to use a specific underlying network. */
1446 @Override
1447 public synchronized void setNetworkForTunnelInterface(
1448 int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
1449 enforceTunnelFeatureAndPermissions(callingPackage);
1450 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1451
1452 final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1453
1454 // Get tunnelInterface record; if no such interface is found, will throw
1455 // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
1456 final TunnelInterfaceRecord tunnelInterfaceInfo =
1457 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1458
1459 final ConnectivityManager connectivityManager =
1460 mContext.getSystemService(ConnectivityManager.class);
1461 final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
Yan Yanbe3eb3d2022-05-16 17:13:45 -07001462 if (lp == null) {
1463 throw new IllegalArgumentException(
1464 "LinkProperties is null. The underlyingNetwork may not be functional");
1465 }
1466
Yan Yana2f3b492020-09-29 23:38:00 -07001467 if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
1468 throw new IllegalArgumentException(
1469 "Underlying network cannot be the network being exposed by this tunnel");
1470 }
1471
1472 // It is meaningless to check if the network exists or is valid because the network might
1473 // disconnect at any time after it passes the check.
1474
1475 tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
1476 }
1477
Benedict Wong8bc90732018-01-18 18:31:45 -08001478 /**
1479 * Delete a TunnelInterface that has been been allocated by and registered with the system
1480 * server
1481 */
1482 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001483 public synchronized void deleteTunnelInterface(
1484 int resourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001485 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001486 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1487 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1488 }
1489
Benedict Wong70867e52017-11-06 20:49:10 -08001490 @VisibleForTesting
Nathan Harold5a19b952018-01-05 19:25:13 -08001491 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1492 IpSecAlgorithm auth = config.getAuthentication();
1493 IpSecAlgorithm crypt = config.getEncryption();
1494 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong70867e52017-11-06 20:49:10 -08001495
Nathan Harold5a19b952018-01-05 19:25:13 -08001496 // Validate the algorithm set
1497 Preconditions.checkArgument(
1498 aead != null || crypt != null || auth != null,
1499 "No Encryption or Authentication algorithms specified");
1500 Preconditions.checkArgument(
1501 auth == null || auth.isAuthentication(),
1502 "Unsupported algorithm for Authentication");
1503 Preconditions.checkArgument(
Benedict Wong70867e52017-11-06 20:49:10 -08001504 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harold5a19b952018-01-05 19:25:13 -08001505 Preconditions.checkArgument(
1506 aead == null || aead.isAead(),
1507 "Unsupported algorithm for Authenticated Encryption");
1508 Preconditions.checkArgument(
1509 aead == null || (auth == null && crypt == null),
1510 "Authenticated Encryption is mutually exclusive with other Authentication "
1511 + "or Encryption algorithms");
Benedict Wong70867e52017-11-06 20:49:10 -08001512 }
1513
evitayan43d93a02018-03-22 17:53:08 -07001514 private int getFamily(String inetAddress) {
1515 int family = AF_UNSPEC;
Serik Beketayev7f507332020-12-06 22:31:23 -08001516 InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
evitayan43d93a02018-03-22 17:53:08 -07001517 if (checkAddress instanceof Inet4Address) {
1518 family = AF_INET;
1519 } else if (checkAddress instanceof Inet6Address) {
1520 family = AF_INET6;
1521 }
1522 return family;
1523 }
1524
Nathan Harold031acb82017-03-07 13:23:36 -08001525 /**
Chiachang Wang2fea4a72020-08-12 12:23:59 +08001526 * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
Nathan Harold19b99d92017-08-23 13:46:33 -07001527 * IllegalArgumentException if they are not.
1528 */
1529 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001530 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1531
Nathan Harold19b99d92017-08-23 13:46:33 -07001532 switch (config.getEncapType()) {
1533 case IpSecTransform.ENCAP_NONE:
1534 break;
1535 case IpSecTransform.ENCAP_ESPINUDP:
1536 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong6855aee2017-11-16 15:27:22 -08001537 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1538 userRecord.mEncapSocketRecords.getResourceOrThrow(
1539 config.getEncapSocketResourceId());
Nathan Harold19b99d92017-08-23 13:46:33 -07001540
1541 int port = config.getEncapRemotePort();
1542 if (port <= 0 || port > 0xFFFF) {
1543 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1544 }
1545 break;
1546 default:
1547 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1548 }
1549
Nathan Harold5a19b952018-01-05 19:25:13 -08001550 validateAlgorithms(config);
Nathan Harold19b99d92017-08-23 13:46:33 -07001551
Nathan Harold5a19b952018-01-05 19:25:13 -08001552 // Retrieve SPI record; will throw IllegalArgumentException if not found
1553 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1554
Benedict Wong68aac2a2017-12-13 18:26:40 -08001555 // Check to ensure that SPI has not already been used.
1556 if (s.getOwnedByTransform()) {
1557 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1558 }
1559
Nathan Harold5a19b952018-01-05 19:25:13 -08001560 // If no remote address is supplied, then use one from the SPI.
1561 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1562 config.setDestinationAddress(s.getDestinationAddress());
1563 }
1564
1565 // All remote addresses must match
1566 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1567 throw new IllegalArgumentException("Mismatched remote addresseses.");
1568 }
1569
1570 // This check is technically redundant due to the chain of custody between the SPI and
1571 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1572 // the transform, this will prevent us from messing up.
1573 checkInetAddress(config.getDestinationAddress());
1574
1575 // Require a valid source address for all transforms.
1576 checkInetAddress(config.getSourceAddress());
1577
evitayan43d93a02018-03-22 17:53:08 -07001578 // Check to ensure source and destination have the same address family.
1579 String sourceAddress = config.getSourceAddress();
1580 String destinationAddress = config.getDestinationAddress();
1581 int sourceFamily = getFamily(sourceAddress);
1582 int destinationFamily = getFamily(destinationAddress);
1583 if (sourceFamily != destinationFamily) {
1584 throw new IllegalArgumentException(
1585 "Source address ("
1586 + sourceAddress
1587 + ") and destination address ("
1588 + destinationAddress
1589 + ") have different address families.");
1590 }
1591
1592 // Throw an error if UDP Encapsulation is not used in IPv4.
1593 if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
1594 throw new IllegalArgumentException(
1595 "UDP Encapsulation is not supported for this address family");
1596 }
1597
Nathan Harold5a19b952018-01-05 19:25:13 -08001598 switch (config.getMode()) {
1599 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold025aae12018-02-02 18:34:25 -08001600 break;
Nathan Harold5a19b952018-01-05 19:25:13 -08001601 case IpSecTransform.MODE_TUNNEL:
1602 break;
1603 default:
1604 throw new IllegalArgumentException(
1605 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harold19b99d92017-08-23 13:46:33 -07001606 }
Benedict Wong683441d2018-07-25 18:46:19 -07001607
1608 config.setMarkValue(0);
1609 config.setMarkMask(0);
Nathan Harold19b99d92017-08-23 13:46:33 -07001610 }
1611
Benedict Wong2b6a14e2018-09-13 16:45:12 -07001612 private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
Nathan Harold7c250ae2018-05-15 19:18:38 -07001613
Benedict Wonge9763752018-11-08 19:45:34 -08001614 private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1615 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1616 throw new UnsupportedOperationException(
1617 "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1618 }
1619
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001620 Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Benedict Wongc85b7b02019-11-12 22:31:51 -08001621
1622 // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
1623 // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
1624 // permission or is the System Server.
1625 if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
1626 TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1627 return;
Nathan Harold65ef8432018-03-15 18:06:06 -07001628 }
Benedict Wongc85b7b02019-11-12 22:31:51 -08001629 mContext.enforceCallingOrSelfPermission(
1630 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
Nathan Harold025aae12018-02-02 18:34:25 -08001631 }
1632
Benedict Wong8edc5572018-01-19 17:36:02 -08001633 private void createOrUpdateTransform(
1634 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1635 throws RemoteException {
1636
1637 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1638 if (encapType != IpSecTransform.ENCAP_NONE) {
1639 encapLocalPort = socketRecord.getPort();
1640 encapRemotePort = c.getEncapRemotePort();
1641 }
1642
1643 IpSecAlgorithm auth = c.getAuthentication();
1644 IpSecAlgorithm crypt = c.getEncryption();
1645 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1646
Benedict Wong778327e2018-03-15 19:41:41 -07001647 String cryptName;
1648 if (crypt == null) {
1649 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1650 } else {
1651 cryptName = crypt.getName();
1652 }
1653
Aaron Huang2617cf52021-11-29 16:31:32 +08001654 mNetd.ipSecAddSecurityAssociation(
1655 Binder.getCallingUid(),
1656 c.getMode(),
1657 c.getSourceAddress(),
1658 c.getDestinationAddress(),
1659 (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
1660 spiRecord.getSpi(),
1661 c.getMarkValue(),
1662 c.getMarkMask(),
1663 (auth != null) ? auth.getName() : "",
1664 (auth != null) ? auth.getKey() : new byte[] {},
1665 (auth != null) ? auth.getTruncationLengthBits() : 0,
1666 cryptName,
1667 (crypt != null) ? crypt.getKey() : new byte[] {},
1668 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1669 (authCrypt != null) ? authCrypt.getName() : "",
1670 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1671 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1672 encapType,
1673 encapLocalPort,
1674 encapRemotePort,
1675 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001676 }
1677
Nathan Harold19b99d92017-08-23 13:46:33 -07001678 /**
Benedict Wong8edc5572018-01-19 17:36:02 -08001679 * Create a IPsec transform, which represents a single security association in the kernel. The
1680 * transform will be cached by the system server and must be freed when no longer needed. It is
1681 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1682 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold031acb82017-03-07 13:23:36 -08001683 */
1684 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001685 public synchronized IpSecTransformResponse createTransform(
1686 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001687 Objects.requireNonNull(c);
Nathan Harold65ef8432018-03-15 18:06:06 -07001688 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
Benedict Wonge9763752018-11-08 19:45:34 -08001689 enforceTunnelFeatureAndPermissions(callingPackage);
Nathan Harold65ef8432018-03-15 18:06:06 -07001690 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001691 checkIpSecConfig(c);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001692 Objects.requireNonNull(binder, "Null Binder passed to createTransform");
Nathan Haroldfdafce22017-12-13 19:16:33 -08001693 final int resourceId = mNextResourceId++;
Benedict Wong6855aee2017-11-16 15:27:22 -08001694
1695 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wongcbd329b2017-12-13 17:16:53 -08001696 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong6855aee2017-11-16 15:27:22 -08001697
1698 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001699 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1700 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001701
Benedict Wong6855aee2017-11-16 15:27:22 -08001702 EncapSocketRecord socketRecord = null;
Benedict Wong8edc5572018-01-19 17:36:02 -08001703 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001704 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1705 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1706 c.getEncapSocketResourceId());
1707 dependencies.add(refcountedSocketRecord);
Benedict Wong6855aee2017-11-16 15:27:22 -08001708 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold80865392017-04-04 19:37:48 -07001709 }
1710
Nathan Harold5a19b952018-01-05 19:25:13 -08001711 RefcountedResource<SpiRecord> refcountedSpiRecord =
1712 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1713 dependencies.add(refcountedSpiRecord);
1714 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong6855aee2017-11-16 15:27:22 -08001715
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001716 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong8edc5572018-01-19 17:36:02 -08001717
1718 // SA was created successfully, time to construct a record and lock it away
Benedict Wong6855aee2017-11-16 15:27:22 -08001719 userRecord.mTransformRecords.put(
1720 resourceId,
1721 new RefcountedResource<TransformRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001722 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong6855aee2017-11-16 15:27:22 -08001723 binder,
1724 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold80865392017-04-04 19:37:48 -07001725 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001726 }
1727
1728 /**
1729 * Delete a transport mode transform that was previously allocated by + registered with the
1730 * system server. If this is called on an inactive (or non-existent) transform, it will not
1731 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1732 * other reasons.
1733 */
1734 @Override
Benedict Wong0fff56e2018-01-18 14:38:16 -08001735 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001736 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1737 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001738 }
1739
1740 /**
1741 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1742 * association as a correspondent policy to the provided socket
1743 */
1744 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001745 public synchronized void applyTransportModeTransform(
Nathan Harold5a19b952018-01-05 19:25:13 -08001746 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001747 int callingUid = Binder.getCallingUid();
1748 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harold5a19b952018-01-05 19:25:13 -08001749 checkDirection(direction);
Benedict Wong6855aee2017-11-16 15:27:22 -08001750 // Get transform record; if no transform is found, will throw IllegalArgumentException
1751 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001752
Nathan Harold80865392017-04-04 19:37:48 -07001753 // TODO: make this a function.
Aaron Huangfbae3082021-12-06 15:18:42 +08001754 if (info.mPid != getCallingPid() || info.mUid != callingUid) {
Nathan Harold80865392017-04-04 19:37:48 -07001755 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1756 }
1757
Benedict Wong8bc90732018-01-18 18:31:45 -08001758 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold80865392017-04-04 19:37:48 -07001759 IpSecConfig c = info.getConfig();
Benedict Wong8bc90732018-01-18 18:31:45 -08001760 Preconditions.checkArgument(
1761 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1762 "Transform mode was not Transport mode; cannot be applied to a socket");
1763
Aaron Huang2617cf52021-11-29 16:31:32 +08001764 mNetd.ipSecApplyTransportModeTransform(
1765 socket,
1766 callingUid,
1767 direction,
1768 c.getSourceAddress(),
1769 c.getDestinationAddress(),
1770 info.getSpiRecord().getSpi());
Nathan Harold031acb82017-03-07 13:23:36 -08001771 }
Nathan Harold80865392017-04-04 19:37:48 -07001772
Nathan Harold031acb82017-03-07 13:23:36 -08001773 /**
Nathan Harold5a19b952018-01-05 19:25:13 -08001774 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1775 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1776 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1777 * reserved for future improved input validation.
Nathan Harold031acb82017-03-07 13:23:36 -08001778 */
1779 @Override
Nathan Harold0d483b72018-01-17 01:00:20 -08001780 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1781 throws RemoteException {
Aaron Huang2617cf52021-11-29 16:31:32 +08001782 mNetd.ipSecRemoveTransportModeTransform(socket);
Nathan Harold031acb82017-03-07 13:23:36 -08001783 }
1784
Benedict Wong8bc90732018-01-18 18:31:45 -08001785 /**
1786 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1787 * security association as a correspondent policy to the provided interface
1788 */
1789 @Override
1790 public synchronized void applyTunnelModeTransform(
Nathan Harold65ef8432018-03-15 18:06:06 -07001791 int tunnelResourceId, int direction,
1792 int transformResourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001793 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001794 checkDirection(direction);
1795
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001796 int callingUid = Binder.getCallingUid();
1797 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001798
1799 // Get transform record; if no transform is found, will throw IllegalArgumentException
1800 TransformRecord transformInfo =
1801 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1802
1803 // Get tunnelInterface record; if no such interface is found, will throw
1804 // IllegalArgumentException
1805 TunnelInterfaceRecord tunnelInterfaceInfo =
1806 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1807
1808 // Get config and check that to-be-applied transform has the correct mode
1809 IpSecConfig c = transformInfo.getConfig();
1810 Preconditions.checkArgument(
1811 c.getMode() == IpSecTransform.MODE_TUNNEL,
1812 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1813
Benedict Wong8edc5572018-01-19 17:36:02 -08001814 EncapSocketRecord socketRecord = null;
1815 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1816 socketRecord =
1817 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1818 }
Benedict Wongec2e2e22019-10-03 11:09:00 -07001819 SpiRecord spiRecord = transformInfo.getSpiRecord();
Benedict Wong8edc5572018-01-19 17:36:02 -08001820
Benedict Wong8bc90732018-01-18 18:31:45 -08001821 int mark =
Benedict Wong38e52972018-05-07 20:06:44 -07001822 (direction == IpSecManager.DIRECTION_OUT)
1823 ? tunnelInterfaceInfo.getOkey()
Benedict Wong908d34e2021-04-15 11:59:16 -07001824 : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
Benedict Wong8bc90732018-01-18 18:31:45 -08001825
Benedict Wong8edc5572018-01-19 17:36:02 -08001826 try {
Benedict Wong5d749842018-09-06 11:31:25 -07001827 // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
1828 // SPI matching as part of the template resolution.
1829 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1830 c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
1831
Benedict Wong683441d2018-07-25 18:46:19 -07001832 // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
1833 // (and backporting) would allow us to narrow the mark space, and ensure that the SA
1834 // and SPs have matching marks (as VTI are meant to be built).
1835 // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
1836 // config matches the actual allocated resources in the kernel.
Benedict Wongc6fcedd2018-11-21 21:24:55 -08001837 // All SAs will have zero marks (from creation time), and any policy that matches the
1838 // same src/dst could match these SAs. Non-IpSecService governed processes that
1839 // establish floating policies with the same src/dst may result in undefined
1840 // behavior. This is generally limited to vendor code due to the permissions
1841 // (CAP_NET_ADMIN) required.
Benedict Wong683441d2018-07-25 18:46:19 -07001842 //
1843 // c.setMarkValue(mark);
1844 // c.setMarkMask(0xffffffff);
Benedict Wong8edc5572018-01-19 17:36:02 -08001845
1846 if (direction == IpSecManager.DIRECTION_OUT) {
1847 // Set output mark via underlying network (output only)
1848 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1849
Benedict Wong5d749842018-09-06 11:31:25 -07001850 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
1851 // but want to guarantee outbound packets are sent over the new SA.
Benedict Wongec2e2e22019-10-03 11:09:00 -07001852 spi = spiRecord.getSpi();
Benedict Wong5d749842018-09-06 11:31:25 -07001853 }
1854
1855 // Always update the policy with the relevant XFRM_IF_ID
1856 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +08001857 mNetd.ipSecUpdateSecurityPolicy(
1858 callingUid,
1859 selAddrFamily,
1860 direction,
1861 transformInfo.getConfig().getSourceAddress(),
1862 transformInfo.getConfig().getDestinationAddress(),
1863 spi, // If outbound, also add SPI to the policy.
1864 mark, // Must always set policy mark; ikey/okey for VTIs
1865 0xffffffff,
1866 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001867 }
1868
1869 // Update SA with tunnel mark (ikey or okey based on direction)
1870 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1871 } catch (ServiceSpecificException e) {
1872 if (e.errorCode == EINVAL) {
1873 throw new IllegalArgumentException(e.toString());
1874 } else {
1875 throw e;
1876 }
1877 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001878 }
1879
Nathan Harold031acb82017-03-07 13:23:36 -08001880 @Override
ludi89194d62017-05-22 10:52:23 -07001881 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001882 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludi89194d62017-05-22 10:52:23 -07001883
1884 pw.println("IpSecService dump:");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001885 pw.println();
ludi89194d62017-05-22 10:52:23 -07001886
Benedict Wong6855aee2017-11-16 15:27:22 -08001887 pw.println("mUserResourceTracker:");
1888 pw.println(mUserResourceTracker);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001889 }
1890}