blob: ea91e6471cb521f7ebacc7cf33fa0aaf499e3e1e [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;
Lorenzo Colittib38fef82023-02-06 15:22:01 +090020import static android.Manifest.permission.NETWORK_SETTINGS;
Yan Yana48dcd92022-10-18 00:03:20 +000021import static android.net.IpSecManager.FEATURE_IPSEC_TUNNEL_MIGRATION;
Nathan Harold031acb82017-03-07 13:23:36 -080022import static android.net.IpSecManager.INVALID_RESOURCE_ID;
Nathan Harold80865392017-04-04 19:37:48 -070023import static android.system.OsConstants.AF_INET;
evitayan43d93a02018-03-22 17:53:08 -070024import static android.system.OsConstants.AF_INET6;
25import static android.system.OsConstants.AF_UNSPEC;
manojboopathiac927fe2017-11-30 17:11:49 -080026import static android.system.OsConstants.EINVAL;
Nathan Harold80865392017-04-04 19:37:48 -070027import static android.system.OsConstants.IPPROTO_UDP;
28import static android.system.OsConstants.SOCK_DGRAM;
Benedict Wong683441d2018-07-25 18:46:19 -070029
Nathan Harold65ef8432018-03-15 18:06:06 -070030import android.annotation.NonNull;
31import android.app.AppOpsManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080032import android.content.Context;
Benedict Wong4aac3e92019-02-25 12:33:22 -080033import android.content.pm.PackageManager;
Yan Yana2f3b492020-09-29 23:38:00 -070034import android.net.ConnectivityManager;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080035import android.net.IIpSecService;
36import android.net.INetd;
Serik Beketayev7f507332020-12-06 22:31:23 -080037import android.net.InetAddresses;
Nathan Harold031acb82017-03-07 13:23:36 -080038import android.net.IpSecAlgorithm;
39import android.net.IpSecConfig;
40import android.net.IpSecManager;
Yan Yane114b382021-02-16 16:29:48 -080041import android.net.IpSecMigrateInfoParcel;
Nathan Harold80865392017-04-04 19:37:48 -070042import android.net.IpSecSpiResponse;
Nathan Harold031acb82017-03-07 13:23:36 -080043import android.net.IpSecTransform;
Nathan Harold80865392017-04-04 19:37:48 -070044import android.net.IpSecTransformResponse;
Yan Yanab5006c2023-11-15 21:41:51 +000045import android.net.IpSecTransformState;
Benedict Wong8bc90732018-01-18 18:31:45 -080046import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold80865392017-04-04 19:37:48 -070047import android.net.IpSecUdpEncapResponse;
Benedict Wong97c3c942018-03-01 18:53:07 -080048import android.net.LinkAddress;
Yan Yana2f3b492020-09-29 23:38:00 -070049import android.net.LinkProperties;
Benedict Wong8bc90732018-01-18 18:31:45 -080050import android.net.Network;
Benedict Wong083faee2017-12-03 19:42:36 -080051import android.net.TrafficStats;
Nathan Harold031acb82017-03-07 13:23:36 -080052import android.os.Binder;
Nathan Harold031acb82017-03-07 13:23:36 -080053import android.os.IBinder;
54import android.os.ParcelFileDescriptor;
Benedict Wong908d34e2021-04-15 11:59:16 -070055import android.os.Process;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080056import android.os.RemoteException;
Nathan Harold031acb82017-03-07 13:23:36 -080057import android.os.ServiceSpecificException;
Nathan Harold80865392017-04-04 19:37:48 -070058import android.system.ErrnoException;
59import android.system.Os;
60import android.system.OsConstants;
Nathan Harold19b99d92017-08-23 13:46:33 -070061import android.text.TextUtils;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080062import android.util.Log;
lucaslin7eb76592021-03-11 17:39:49 +080063import android.util.Range;
Nathan Harold031acb82017-03-07 13:23:36 -080064import android.util.SparseArray;
Benedict Wong8bc90732018-01-18 18:31:45 -080065import android.util.SparseBooleanArray;
Nathan Harold19b99d92017-08-23 13:46:33 -070066
Nathan Harold031acb82017-03-07 13:23:36 -080067import com.android.internal.annotations.GuardedBy;
ludi5e623ea2017-05-12 09:15:00 -070068import com.android.internal.annotations.VisibleForTesting;
Benedict Wong70867e52017-11-06 20:49:10 -080069import com.android.internal.util.Preconditions;
Lorenzo Colittib38fef82023-02-06 15:22:01 +090070import com.android.modules.utils.build.SdkLevel;
paulhu00e34562021-10-26 09:00:50 +000071import com.android.net.module.util.BinderUtils;
lucaslinff6fe7b2021-02-03 23:59:45 +080072import com.android.net.module.util.NetdUtils;
Benedict Wong908d34e2021-04-15 11:59:16 -070073import com.android.net.module.util.PermissionUtils;
Yan Yanab5006c2023-11-15 21:41:51 +000074import com.android.net.module.util.netlink.xfrm.XfrmNetlinkNewSaMessage;
Nathan Harold19b99d92017-08-23 13:46:33 -070075
Benedict Wong683441d2018-07-25 18:46:19 -070076import libcore.io.IoUtils;
77
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080078import java.io.FileDescriptor;
Nathan Harold80865392017-04-04 19:37:48 -070079import java.io.IOException;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080080import java.io.PrintWriter;
evitayan43d93a02018-03-22 17:53:08 -070081import java.net.Inet4Address;
82import java.net.Inet6Address;
Nathan Harold80865392017-04-04 19:37:48 -070083import java.net.InetAddress;
84import java.net.InetSocketAddress;
85import java.net.UnknownHostException;
Benedict Wong02346822017-10-26 19:41:43 -070086import java.util.ArrayList;
87import java.util.List;
Benedict Wong529e8aa2020-02-11 23:49:36 -080088import java.util.Objects;
Nathan Harold19b99d92017-08-23 13:46:33 -070089
Benedict Wong02346822017-10-26 19:41:43 -070090/**
91 * A service to manage multiple clients that want to access the IpSec API. The service is
92 * responsible for maintaining a list of clients and managing the resources (and related quotas)
93 * that each of them own.
94 *
95 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
96 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
97 * thread is ever running at a time.
98 *
99 * @hide
100 */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800101public class IpSecService extends IIpSecService.Stub {
102 private static final String TAG = "IpSecService";
103 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Benedict Wong38e52972018-05-07 20:06:44 -0700104 private static final int[] ADDRESS_FAMILIES =
105 new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800106
ludi5e623ea2017-05-12 09:15:00 -0700107 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold80865392017-04-04 19:37:48 -0700108 private static final InetAddress INADDR_ANY;
Lorenzo Colittib38fef82023-02-06 15:22:01 +0900109 private static final InetAddress IN6ADDR_ANY;
Nathan Harold80865392017-04-04 19:37:48 -0700110
Benedict Wong29c30772019-03-20 09:44:09 -0700111 @VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
112
Aaron Huang2617cf52021-11-29 16:31:32 +0800113 private final INetd mNetd;
Yan Yanab5006c2023-11-15 21:41:51 +0000114 private final IpSecXfrmController mIpSecXfrmCtrl;
Aaron Huang2617cf52021-11-29 16:31:32 +0800115
Nathan Harold80865392017-04-04 19:37:48 -0700116 static {
117 try {
118 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
Lorenzo Colittib38fef82023-02-06 15:22:01 +0900119 IN6ADDR_ANY = InetAddress.getByAddress(
120 new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
Nathan Harold80865392017-04-04 19:37:48 -0700121 } catch (UnknownHostException e) {
122 throw new RuntimeException(e);
123 }
124 }
125
126 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
127 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
128
129 /* Binder context for this service */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800130 private final Context mContext;
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800131 private final Dependencies mDeps;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800132
Nathan Haroldfdafce22017-12-13 19:16:33 -0800133 /**
Nathan Harold5a19b952018-01-05 19:25:13 -0800134 * The next non-repeating global ID for tracking resources between users, this service, and
135 * kernel data structures. Accessing this variable is not thread safe, so it is only read or
136 * modified within blocks synchronized on IpSecService.this. We want to avoid -1
137 * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
Nathan Haroldfdafce22017-12-13 19:16:33 -0800138 */
139 @GuardedBy("IpSecService.this")
140 private int mNextResourceId = 1;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800141
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800142 /**
143 * Dependencies of IpSecService, for injection in tests.
144 */
145 @VisibleForTesting
146 public static class Dependencies {
147 /**
148 * Get a reference to INetd.
149 */
150 public INetd getNetdInstance(Context context) throws RemoteException {
151 final INetd netd = INetd.Stub.asInterface((IBinder)
152 context.getSystemService(Context.NETD_SERVICE));
153 if (netd == null) {
154 throw new RemoteException("Failed to Get Netd Instance");
155 }
156 return netd;
157 }
Yan Yanab5006c2023-11-15 21:41:51 +0000158
159 /** Get a instance of IpSecXfrmController */
160 public IpSecXfrmController getIpSecXfrmController() {
161 return new IpSecXfrmController();
162 }
ludi5e623ea2017-05-12 09:15:00 -0700163 }
164
Benedict Wong083faee2017-12-03 19:42:36 -0800165 final UidFdTagger mUidFdTagger;
ludi5e623ea2017-05-12 09:15:00 -0700166
Benedict Wong02346822017-10-26 19:41:43 -0700167 /**
168 * Interface for user-reference and kernel-resource cleanup.
169 *
170 * <p>This interface must be implemented for a resource to be reference counted.
171 */
172 @VisibleForTesting
173 public interface IResource {
174 /**
175 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
176 * objects dependent on it.
177 *
178 * <p>Implementations of this method are expected to remove references to the IResource
179 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
180 * the resource is considered invalid for user access or allocation or use in other
181 * resources.
182 *
183 * <p>References to the IResource object may be held by other RefcountedResource objects,
Benedict Wongcbd329b2017-12-13 17:16:53 -0800184 * and as such, the underlying resources and quota may not be cleaned up.
Benedict Wong02346822017-10-26 19:41:43 -0700185 */
186 void invalidate() throws RemoteException;
187
188 /**
189 * Releases underlying resources and related quotas.
190 *
191 * <p>Implementations of this method are expected to remove all system resources that are
192 * tracked by the IResource object. Due to other RefcountedResource objects potentially
Benedict Wong6855aee2017-11-16 15:27:22 -0800193 * having references to the IResource object, freeUnderlyingResources may not always be
Benedict Wong02346822017-10-26 19:41:43 -0700194 * called from releaseIfUnreferencedRecursively().
195 */
196 void freeUnderlyingResources() throws RemoteException;
197 }
198
199 /**
200 * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
201 *
202 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
203 * RefcountedResource object creates an explicit reference that must be freed by calling
204 * userRelease(). Additionally, adding this object as a child of another RefcountedResource
205 * object will add an implicit reference.
206 *
207 * <p>Resources are cleaned up when all references, both implicit and explicit, are released
208 * (ie, when userRelease() is called and when all parents have called releaseReference() on this
209 * object.)
210 */
211 @VisibleForTesting
212 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000213 private final T mResource;
214 private final List<RefcountedResource> mChildren;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800215 int mRefCount = 1; // starts at 1 for user's reference.
Junyu Lai75eabfe2019-04-26 01:38:04 +0000216 IBinder mBinder;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800217
Junyu Lai75eabfe2019-04-26 01:38:04 +0000218 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
Benedict Wong02346822017-10-26 19:41:43 -0700219 synchronized (IpSecService.this) {
220 this.mResource = resource;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000221 this.mChildren = new ArrayList<>(children.length);
Benedict Wong02346822017-10-26 19:41:43 -0700222 this.mBinder = binder;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000223
Benedict Wong02346822017-10-26 19:41:43 -0700224 for (RefcountedResource child : children) {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000225 mChildren.add(child);
Benedict Wong02346822017-10-26 19:41:43 -0700226 child.mRefCount++;
227 }
228
229 try {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000230 mBinder.linkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700231 } catch (RemoteException e) {
232 binderDied();
Benedict Wong04738f52019-02-28 20:28:48 -0800233 e.rethrowFromSystemServer();
Benedict Wong02346822017-10-26 19:41:43 -0700234 }
235 }
236 }
237
238 /**
239 * If the Binder object dies, this function is called to free the system resources that are
Benedict Wong6855aee2017-11-16 15:27:22 -0800240 * being tracked by this record and to subsequently release this record for garbage
Benedict Wong02346822017-10-26 19:41:43 -0700241 * collection
242 */
243 @Override
244 public void binderDied() {
245 synchronized (IpSecService.this) {
246 try {
247 userRelease();
248 } catch (Exception e) {
249 Log.e(TAG, "Failed to release resource: " + e);
250 }
251 }
252 }
253
254 public T getResource() {
255 return mResource;
256 }
257
258 /**
259 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
260 * arrays)
261 *
262 * <p>If this method has been previously called, the RefcountedResource's binder field will
263 * be null, and the method will return without performing the cleanup a second time.
264 *
265 * <p>Note that calling this function does not imply that kernel resources will be freed at
266 * this time, or that the related quota will be returned. Such actions will only be
267 * performed upon the reference count reaching zero.
268 */
269 @GuardedBy("IpSecService.this")
270 public void userRelease() throws RemoteException {
271 // Prevent users from putting reference counts into a bad state by calling
272 // userRelease() multiple times.
273 if (mBinder == null) {
274 return;
275 }
276
Junyu Lai75eabfe2019-04-26 01:38:04 +0000277 mBinder.unlinkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700278 mBinder = null;
279
280 mResource.invalidate();
Junyu Lai75eabfe2019-04-26 01:38:04 +0000281
Benedict Wong02346822017-10-26 19:41:43 -0700282 releaseReference();
283 }
284
285 /**
286 * Removes a reference to this resource. If the resultant reference count is zero, the
287 * underlying resources are freed, and references to all child resources are also dropped
288 * recursively (resulting in them freeing their resources and children, etcetera)
289 *
290 * <p>This method also sets the reference count to an invalid value (-1) to signify that it
291 * has been fully released. Any subsequent calls to this method will result in an
292 * IllegalStateException being thrown due to resource already having been previously
293 * released
294 */
295 @VisibleForTesting
296 @GuardedBy("IpSecService.this")
297 public void releaseReference() throws RemoteException {
298 mRefCount--;
299
300 if (mRefCount > 0) {
301 return;
302 } else if (mRefCount < 0) {
303 throw new IllegalStateException(
304 "Invalid operation - resource has already been released.");
305 }
306
307 // Cleanup own resources
308 mResource.freeUnderlyingResources();
309
310 // Cleanup child resources as needed
311 for (RefcountedResource<? extends IResource> child : mChildren) {
312 child.releaseReference();
313 }
314
315 // Enforce that resource cleanup can only be called once
316 // By decrementing the refcount (from 0 to -1), the next call will throw an
317 // IllegalStateException - it has already been released fully.
318 mRefCount--;
319 }
320
321 @Override
322 public String toString() {
323 return new StringBuilder()
324 .append("{mResource=")
325 .append(mResource)
326 .append(", mRefCount=")
327 .append(mRefCount)
328 .append(", mChildren=")
329 .append(mChildren)
330 .append("}")
331 .toString();
332 }
333 }
334
Benedict Wongcbd329b2017-12-13 17:16:53 -0800335 /**
336 * Very simple counting class that looks much like a counting semaphore
337 *
338 * <p>This class is not thread-safe, and expects that that users of this class will ensure
339 * synchronization and thread safety by holding the IpSecService.this instance lock.
340 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800341 @VisibleForTesting
342 static class ResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700343 private final int mMax;
344 int mCurrent;
345
346 ResourceTracker(int max) {
347 mMax = max;
348 mCurrent = 0;
349 }
350
Benedict Wong6855aee2017-11-16 15:27:22 -0800351 boolean isAvailable() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700352 return (mCurrent < mMax);
353 }
354
Benedict Wong6855aee2017-11-16 15:27:22 -0800355 void take() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700356 if (!isAvailable()) {
357 Log.wtf(TAG, "Too many resources allocated!");
358 }
359 mCurrent++;
360 }
361
Benedict Wong6855aee2017-11-16 15:27:22 -0800362 void give() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700363 if (mCurrent <= 0) {
364 Log.wtf(TAG, "We've released this resource too many times");
365 }
366 mCurrent--;
367 }
ludi529fdec2017-08-10 15:44:40 -0700368
369 @Override
370 public String toString() {
371 return new StringBuilder()
372 .append("{mCurrent=")
373 .append(mCurrent)
374 .append(", mMax=")
375 .append(mMax)
376 .append("}")
377 .toString();
378 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700379 }
380
Benedict Wong6855aee2017-11-16 15:27:22 -0800381 @VisibleForTesting
382 static final class UserRecord {
Benedict Wong6855aee2017-11-16 15:27:22 -0800383 /* Maximum number of each type of resource that a single UID may possess */
Benedict Wong7bcf9c22020-02-11 23:36:42 -0800384
385 // Up to 4 active VPNs/IWLAN with potential soft handover.
386 public static final int MAX_NUM_TUNNEL_INTERFACES = 8;
387 public static final int MAX_NUM_ENCAP_SOCKETS = 16;
388
389 // SPIs and Transforms are both cheap, and are 1:1 correlated.
390 public static final int MAX_NUM_TRANSFORMS = 64;
391 public static final int MAX_NUM_SPIS = 64;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700392
Benedict Wongcbd329b2017-12-13 17:16:53 -0800393 /**
394 * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
395 * and explicit (user) reference management.
396 *
397 * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
398 *
399 * <p>Resources are removed from this array when the user releases their explicit reference
400 * by calling one of the releaseResource() methods.
401 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800402 final RefcountedResourceArray<SpiRecord> mSpiRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800403 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800404 final RefcountedResourceArray<TransformRecord> mTransformRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800405 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800406 final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800407 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
Benedict Wong8bc90732018-01-18 18:31:45 -0800408 final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
409 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
Benedict Wongcbd329b2017-12-13 17:16:53 -0800410
411 /**
412 * Trackers for quotas for each of the OwnedResource types.
413 *
414 * <p>These trackers are separate from the resource arrays, since they are incremented and
415 * decremented at different points in time. Specifically, quota is only returned upon final
416 * resource deallocation (after all explicit and implicit references are released). Note
417 * that it is possible that calls to releaseResource() will not return the used quota if
418 * there are other resources that depend on (are parents of) the resource being released.
419 */
420 final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
421 final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
Benedict Wong6855aee2017-11-16 15:27:22 -0800422 final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
Benedict Wong8bc90732018-01-18 18:31:45 -0800423 final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
Benedict Wong6855aee2017-11-16 15:27:22 -0800424
425 void removeSpiRecord(int resourceId) {
426 mSpiRecords.remove(resourceId);
Nathan Harold6e4681c2017-04-24 16:16:34 -0700427 }
428
Benedict Wong6855aee2017-11-16 15:27:22 -0800429 void removeTransformRecord(int resourceId) {
430 mTransformRecords.remove(resourceId);
431 }
432
Benedict Wong8bc90732018-01-18 18:31:45 -0800433 void removeTunnelInterfaceRecord(int resourceId) {
434 mTunnelInterfaceRecords.remove(resourceId);
435 }
436
Benedict Wong6855aee2017-11-16 15:27:22 -0800437 void removeEncapSocketRecord(int resourceId) {
438 mEncapSocketRecords.remove(resourceId);
439 }
440
441 @Override
442 public String toString() {
443 return new StringBuilder()
444 .append("{mSpiQuotaTracker=")
445 .append(mSpiQuotaTracker)
446 .append(", mTransformQuotaTracker=")
447 .append(mTransformQuotaTracker)
448 .append(", mSocketQuotaTracker=")
449 .append(mSocketQuotaTracker)
Benedict Wong76603702018-01-24 15:31:39 -0800450 .append(", mTunnelQuotaTracker=")
451 .append(mTunnelQuotaTracker)
Benedict Wong6855aee2017-11-16 15:27:22 -0800452 .append(", mSpiRecords=")
453 .append(mSpiRecords)
454 .append(", mTransformRecords=")
455 .append(mTransformRecords)
456 .append(", mEncapSocketRecords=")
457 .append(mEncapSocketRecords)
Benedict Wong76603702018-01-24 15:31:39 -0800458 .append(", mTunnelInterfaceRecords=")
459 .append(mTunnelInterfaceRecords)
Benedict Wong6855aee2017-11-16 15:27:22 -0800460 .append("}")
461 .toString();
462 }
463 }
464
Benedict Wongcbd329b2017-12-13 17:16:53 -0800465 /**
466 * This class is not thread-safe, and expects that that users of this class will ensure
467 * synchronization and thread safety by holding the IpSecService.this instance lock.
468 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800469 @VisibleForTesting
470 static final class UserResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700471 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
472
Benedict Wongcbd329b2017-12-13 17:16:53 -0800473 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
Benedict Wong6855aee2017-11-16 15:27:22 -0800474 public UserRecord getUserRecord(int uid) {
475 checkCallerUid(uid);
476
Nathan Harold6e4681c2017-04-24 16:16:34 -0700477 UserRecord r = mUserRecords.get(uid);
478 if (r == null) {
479 r = new UserRecord();
480 mUserRecords.put(uid, r);
481 }
482 return r;
483 }
ludi529fdec2017-08-10 15:44:40 -0700484
Benedict Wong6855aee2017-11-16 15:27:22 -0800485 /** Safety method; guards against access of other user's UserRecords */
486 private void checkCallerUid(int uid) {
Benedict Wong908d34e2021-04-15 11:59:16 -0700487 if (uid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800488 throw new SecurityException("Attempted access of unowned resources");
489 }
490 }
491
ludi529fdec2017-08-10 15:44:40 -0700492 @Override
493 public String toString() {
494 return mUserRecords.toString();
495 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700496 }
497
Benedict Wong6855aee2017-11-16 15:27:22 -0800498 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700499
Nathan Harold80865392017-04-04 19:37:48 -0700500 /**
Benedict Wongcbd329b2017-12-13 17:16:53 -0800501 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
Benedict Wong6855aee2017-11-16 15:27:22 -0800502 * resources. It relies on a provided resourceId that should uniquely identify the kernel
503 * resource. To use this class, the user should implement the invalidate() and
504 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
Benedict Wongcbd329b2017-12-13 17:16:53 -0800505 * tracking arrays and kernel resources, respectively.
506 *
507 * <p>This class associates kernel resources with the UID that owns and controls them.
Nathan Harold80865392017-04-04 19:37:48 -0700508 */
Benedict Wongcbd329b2017-12-13 17:16:53 -0800509 private abstract class OwnedResourceRecord implements IResource {
Aaron Huangfbae3082021-12-06 15:18:42 +0800510 final int mPid;
511 final int mUid;
Benedict Wong6855aee2017-11-16 15:27:22 -0800512 protected final int mResourceId;
Nathan Harold031acb82017-03-07 13:23:36 -0800513
Benedict Wongcbd329b2017-12-13 17:16:53 -0800514 OwnedResourceRecord(int resourceId) {
Nathan Harold031acb82017-03-07 13:23:36 -0800515 super();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700516 if (resourceId == INVALID_RESOURCE_ID) {
517 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
518 }
Nathan Harold80865392017-04-04 19:37:48 -0700519 mResourceId = resourceId;
Aaron Huangfbae3082021-12-06 15:18:42 +0800520 mPid = Binder.getCallingPid();
521 mUid = Binder.getCallingUid();
Nathan Harold031acb82017-03-07 13:23:36 -0800522
Nathan Harold6e4681c2017-04-24 16:16:34 -0700523 getResourceTracker().take();
Nathan Harold031acb82017-03-07 13:23:36 -0800524 }
525
Benedict Wong6855aee2017-11-16 15:27:22 -0800526 @Override
527 public abstract void invalidate() throws RemoteException;
528
529 /** Convenience method; retrieves the user resource record for the stored UID. */
530 protected UserRecord getUserRecord() {
Aaron Huangfbae3082021-12-06 15:18:42 +0800531 return mUserResourceTracker.getUserRecord(mUid);
Nathan Harold80865392017-04-04 19:37:48 -0700532 }
533
Benedict Wong6855aee2017-11-16 15:27:22 -0800534 @Override
535 public abstract void freeUnderlyingResources() throws RemoteException;
ludi89194d62017-05-22 10:52:23 -0700536
Nathan Harold6e4681c2017-04-24 16:16:34 -0700537 /** Get the resource tracker for this resource */
538 protected abstract ResourceTracker getResourceTracker();
539
ludi89194d62017-05-22 10:52:23 -0700540 @Override
541 public String toString() {
542 return new StringBuilder()
543 .append("{mResourceId=")
544 .append(mResourceId)
545 .append(", pid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800546 .append(mPid)
ludi89194d62017-05-22 10:52:23 -0700547 .append(", uid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800548 .append(mUid)
ludi89194d62017-05-22 10:52:23 -0700549 .append("}")
550 .toString();
551 }
Nathan Harold031acb82017-03-07 13:23:36 -0800552 };
553
Nathan Harold80865392017-04-04 19:37:48 -0700554 /**
Benedict Wong6855aee2017-11-16 15:27:22 -0800555 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
556 *
557 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
558 * if a key is not found during a retrieval process.
Nathan Harold80865392017-04-04 19:37:48 -0700559 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800560 static class RefcountedResourceArray<T extends IResource> {
561 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
562 private final String mTypeName;
Nathan Harold031acb82017-03-07 13:23:36 -0800563
Aaron Huangfbae3082021-12-06 15:18:42 +0800564 RefcountedResourceArray(String typeName) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800565 this.mTypeName = typeName;
Nathan Harold80865392017-04-04 19:37:48 -0700566 }
567
Benedict Wong6855aee2017-11-16 15:27:22 -0800568 /**
569 * Accessor method to get inner resource object.
570 *
571 * @throws IllegalArgumentException if no resource with provided key is found.
572 */
573 T getResourceOrThrow(int key) {
574 return getRefcountedResourceOrThrow(key).getResource();
575 }
576
577 /**
578 * Accessor method to get reference counting wrapper.
579 *
580 * @throws IllegalArgumentException if no resource with provided key is found.
581 */
582 RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
583 RefcountedResource<T> resource = mArray.get(key);
584 if (resource == null) {
585 throw new IllegalArgumentException(
586 String.format("No such %s found for given id: %d", mTypeName, key));
587 }
588
589 return resource;
590 }
591
592 void put(int key, RefcountedResource<T> obj) {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +0000593 Objects.requireNonNull(obj, "Null resources cannot be added");
Nathan Harold80865392017-04-04 19:37:48 -0700594 mArray.put(key, obj);
595 }
596
597 void remove(int key) {
598 mArray.remove(key);
599 }
ludi89194d62017-05-22 10:52:23 -0700600
601 @Override
602 public String toString() {
603 return mArray.toString();
604 }
Nathan Harold80865392017-04-04 19:37:48 -0700605 }
606
Benedict Wongcbd329b2017-12-13 17:16:53 -0800607 /**
Yan Yane114b382021-02-16 16:29:48 -0800608 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is created, the
609 * SpiRecord that originally tracked the SAs will reliquish the responsibility of freeing the
610 * underlying SA to this class via the mOwnedByTransform flag.
611 *
612 * <p>This class is not thread-safe, and expects that that users of this class will ensure
613 * synchronization and thread safety by holding the IpSecService.this instance lock
Benedict Wongcbd329b2017-12-13 17:16:53 -0800614 */
615 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700616 private final IpSecConfig mConfig;
Nathan Harold5a19b952018-01-05 19:25:13 -0800617 private final SpiRecord mSpi;
Benedict Wong6855aee2017-11-16 15:27:22 -0800618 private final EncapSocketRecord mSocket;
Yan Yane114b382021-02-16 16:29:48 -0800619 private String mNewSourceAddress = null;
620 private String mNewDestinationAddress = null;
Nathan Harold80865392017-04-04 19:37:48 -0700621
622 TransformRecord(
Nathan Harold5a19b952018-01-05 19:25:13 -0800623 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800624 super(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -0800625 mConfig = config;
Nathan Harold5a19b952018-01-05 19:25:13 -0800626 mSpi = spi;
Nathan Harold80865392017-04-04 19:37:48 -0700627 mSocket = socket;
Benedict Wong68aac2a2017-12-13 18:26:40 -0800628
629 spi.setOwnedByTransform();
Nathan Harold031acb82017-03-07 13:23:36 -0800630 }
631
632 public IpSecConfig getConfig() {
633 return mConfig;
634 }
635
Nathan Harold5a19b952018-01-05 19:25:13 -0800636 public SpiRecord getSpiRecord() {
637 return mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700638 }
639
Benedict Wong8edc5572018-01-19 17:36:02 -0800640 public EncapSocketRecord getSocketRecord() {
641 return mSocket;
642 }
643
Yan Yane114b382021-02-16 16:29:48 -0800644 @GuardedBy("IpSecService.this")
645 public String getNewSourceAddress() {
646 return mNewSourceAddress;
647 }
648
649 @GuardedBy("IpSecService.this")
650 public String getNewDestinationAddress() {
651 return mNewDestinationAddress;
652 }
653
654 private void verifyTunnelModeOrThrow() {
655 if (mConfig.getMode() != IpSecTransform.MODE_TUNNEL) {
656 throw new UnsupportedOperationException(
657 "Migration requested/called on non-tunnel-mode transform");
658 }
659 }
660
661 /** Start migrating this transform to new source and destination addresses */
662 @GuardedBy("IpSecService.this")
663 public void startMigration(String newSourceAddress, String newDestinationAddress) {
664 verifyTunnelModeOrThrow();
665 Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
666 Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
667 mNewSourceAddress = newSourceAddress;
668 mNewDestinationAddress = newDestinationAddress;
669 }
670
671 /** Finish migration and update addresses. */
672 @GuardedBy("IpSecService.this")
673 public void finishMigration() {
674 verifyTunnelModeOrThrow();
675 mConfig.setSourceAddress(mNewSourceAddress);
676 mConfig.setDestinationAddress(mNewDestinationAddress);
677 mNewSourceAddress = null;
678 mNewDestinationAddress = null;
679 }
680
681 /** Return if this transform is going to be migrated. */
682 @GuardedBy("IpSecService.this")
683 public boolean isMigrating() {
684 verifyTunnelModeOrThrow();
685
686 return mNewSourceAddress != null;
687 }
688
Nathan Harold80865392017-04-04 19:37:48 -0700689 /** always guarded by IpSecService#this */
Nathan Harold031acb82017-03-07 13:23:36 -0800690 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800691 public void freeUnderlyingResources() {
Nathan Harold5a19b952018-01-05 19:25:13 -0800692 int spi = mSpi.getSpi();
693 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800694 mNetd.ipSecDeleteSecurityAssociation(
695 mUid,
696 mConfig.getSourceAddress(),
697 mConfig.getDestinationAddress(),
698 spi,
699 mConfig.getMarkValue(),
700 mConfig.getMarkMask(),
701 mConfig.getXfrmInterfaceId());
Benedict Wong97c3c942018-03-01 18:53:07 -0800702 } catch (RemoteException | ServiceSpecificException e) {
703 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800704 }
Nathan Harold031acb82017-03-07 13:23:36 -0800705
Benedict Wong6855aee2017-11-16 15:27:22 -0800706 getResourceTracker().give();
Nathan Harold031acb82017-03-07 13:23:36 -0800707 }
ludi89194d62017-05-22 10:52:23 -0700708
Benedict Wong6855aee2017-11-16 15:27:22 -0800709 @Override
710 public void invalidate() throws RemoteException {
711 getUserRecord().removeTransformRecord(mResourceId);
712 }
713
714 @Override
Nathan Harold6e4681c2017-04-24 16:16:34 -0700715 protected ResourceTracker getResourceTracker() {
Benedict Wong6855aee2017-11-16 15:27:22 -0800716 return getUserRecord().mTransformQuotaTracker;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700717 }
718
ludi89194d62017-05-22 10:52:23 -0700719 @Override
720 public String toString() {
721 StringBuilder strBuilder = new StringBuilder();
722 strBuilder
723 .append("{super=")
724 .append(super.toString())
725 .append(", mSocket=")
726 .append(mSocket)
Nathan Harold5a19b952018-01-05 19:25:13 -0800727 .append(", mSpi.mResourceId=")
728 .append(mSpi.mResourceId)
ludi89194d62017-05-22 10:52:23 -0700729 .append(", mConfig=")
730 .append(mConfig)
731 .append("}");
732 return strBuilder.toString();
733 }
Nathan Harold031acb82017-03-07 13:23:36 -0800734 }
735
Benedict Wongcbd329b2017-12-13 17:16:53 -0800736 /**
737 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
738 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
739 * object
740 */
741 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harold5a19b952018-01-05 19:25:13 -0800742 private final String mSourceAddress;
743 private final String mDestinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800744 private int mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700745
746 private boolean mOwnedByTransform = false;
Nathan Harold031acb82017-03-07 13:23:36 -0800747
Aaron Huang2617cf52021-11-29 16:31:32 +0800748 SpiRecord(int resourceId, String sourceAddress,
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800749 String destinationAddress, int spi) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800750 super(resourceId);
Nathan Harold5a19b952018-01-05 19:25:13 -0800751 mSourceAddress = sourceAddress;
752 mDestinationAddress = destinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800753 mSpi = spi;
Nathan Harold031acb82017-03-07 13:23:36 -0800754 }
755
Nathan Harold80865392017-04-04 19:37:48 -0700756 /** always guarded by IpSecService#this */
757 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800758 public void freeUnderlyingResources() {
Nathan Harold031acb82017-03-07 13:23:36 -0800759 try {
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800760 if (!mOwnedByTransform) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800761 mNetd.ipSecDeleteSecurityAssociation(
762 mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
763 0 /* mask */, 0 /* if_id */);
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800764 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800765 } catch (ServiceSpecificException | RemoteException e) {
766 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800767 }
Nathan Harold80865392017-04-04 19:37:48 -0700768
769 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -0800770
Benedict Wong6855aee2017-11-16 15:27:22 -0800771 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700772 }
773
Nathan Harold80865392017-04-04 19:37:48 -0700774 public int getSpi() {
775 return mSpi;
776 }
777
Nathan Harold5a19b952018-01-05 19:25:13 -0800778 public String getDestinationAddress() {
779 return mDestinationAddress;
780 }
781
Nathan Harold80865392017-04-04 19:37:48 -0700782 public void setOwnedByTransform() {
783 if (mOwnedByTransform) {
784 // Programming error
Andreas Gampeafb01e22017-07-11 10:25:09 -0700785 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold80865392017-04-04 19:37:48 -0700786 }
787
788 mOwnedByTransform = true;
Nathan Harold031acb82017-03-07 13:23:36 -0800789 }
ludi89194d62017-05-22 10:52:23 -0700790
Benedict Wong68aac2a2017-12-13 18:26:40 -0800791 public boolean getOwnedByTransform() {
792 return mOwnedByTransform;
793 }
794
ludi89194d62017-05-22 10:52:23 -0700795 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800796 public void invalidate() throws RemoteException {
797 getUserRecord().removeSpiRecord(mResourceId);
798 }
799
800 @Override
801 protected ResourceTracker getResourceTracker() {
802 return getUserRecord().mSpiQuotaTracker;
803 }
804
805 @Override
ludi89194d62017-05-22 10:52:23 -0700806 public String toString() {
807 StringBuilder strBuilder = new StringBuilder();
808 strBuilder
809 .append("{super=")
810 .append(super.toString())
811 .append(", mSpi=")
812 .append(mSpi)
Nathan Harold5a19b952018-01-05 19:25:13 -0800813 .append(", mSourceAddress=")
814 .append(mSourceAddress)
815 .append(", mDestinationAddress=")
816 .append(mDestinationAddress)
ludi89194d62017-05-22 10:52:23 -0700817 .append(", mOwnedByTransform=")
818 .append(mOwnedByTransform)
819 .append("}");
820 return strBuilder.toString();
821 }
Nathan Harold031acb82017-03-07 13:23:36 -0800822 }
823
Benedict Wong8bc90732018-01-18 18:31:45 -0800824 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
lucaslin7eb76592021-03-11 17:39:49 +0800825 final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
826 private int mNextTunnelNetId = mNetIdRange.getLower();
Benedict Wong8bc90732018-01-18 18:31:45 -0800827
828 /**
829 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
830 *
831 * <p>This method should only be called from Binder threads. Do not call this from within the
832 * system server as it will crash the system on failure.
833 *
834 * @return an integer key within the netId range, if successful
835 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
836 */
837 @VisibleForTesting
838 int reserveNetId() {
lucaslin7eb76592021-03-11 17:39:49 +0800839 final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
Benedict Wong8bc90732018-01-18 18:31:45 -0800840 synchronized (mTunnelNetIds) {
lucaslin7eb76592021-03-11 17:39:49 +0800841 for (int i = 0; i < range; i++) {
842 final int netId = mNextTunnelNetId;
843 if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
844 mNextTunnelNetId = mNetIdRange.getLower();
845 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800846 if (!mTunnelNetIds.get(netId)) {
847 mTunnelNetIds.put(netId, true);
848 return netId;
849 }
850 }
851 }
852 throw new IllegalStateException("No free netIds to allocate");
853 }
854
855 @VisibleForTesting
856 void releaseNetId(int netId) {
857 synchronized (mTunnelNetIds) {
858 mTunnelNetIds.delete(netId);
859 }
860 }
861
Yan Yana2f3b492020-09-29 23:38:00 -0700862 /**
863 * Tracks an tunnel interface, and manages cleanup paths.
864 *
865 * <p>This class is not thread-safe, and expects that that users of this class will ensure
866 * synchronization and thread safety by holding the IpSecService.this instance lock
867 */
868 @VisibleForTesting
869 final class TunnelInterfaceRecord extends OwnedResourceRecord {
Benedict Wong8bc90732018-01-18 18:31:45 -0800870 private final String mInterfaceName;
Benedict Wong8bc90732018-01-18 18:31:45 -0800871
872 // outer addresses
873 private final String mLocalAddress;
874 private final String mRemoteAddress;
875
876 private final int mIkey;
877 private final int mOkey;
878
Benedict Wong5d749842018-09-06 11:31:25 -0700879 private final int mIfId;
880
Yan Yana2f3b492020-09-29 23:38:00 -0700881 private Network mUnderlyingNetwork;
882
Benedict Wong8bc90732018-01-18 18:31:45 -0800883 TunnelInterfaceRecord(
884 int resourceId,
885 String interfaceName,
886 Network underlyingNetwork,
887 String localAddr,
888 String remoteAddr,
889 int ikey,
Benedict Wong5d749842018-09-06 11:31:25 -0700890 int okey,
891 int intfId) {
Benedict Wong8bc90732018-01-18 18:31:45 -0800892 super(resourceId);
893
894 mInterfaceName = interfaceName;
895 mUnderlyingNetwork = underlyingNetwork;
896 mLocalAddress = localAddr;
897 mRemoteAddress = remoteAddr;
898 mIkey = ikey;
899 mOkey = okey;
Benedict Wong5d749842018-09-06 11:31:25 -0700900 mIfId = intfId;
Benedict Wong8bc90732018-01-18 18:31:45 -0800901 }
902
903 /** always guarded by IpSecService#this */
904 @Override
905 public void freeUnderlyingResources() {
Benedict Wong8edc5572018-01-19 17:36:02 -0800906 // Calls to netd
Benedict Wong8bc90732018-01-18 18:31:45 -0800907 // Teardown VTI
908 // Delete global policies
Benedict Wong8edc5572018-01-19 17:36:02 -0800909 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800910 mNetd.ipSecRemoveTunnelInterface(mInterfaceName);
Benedict Wong8edc5572018-01-19 17:36:02 -0800911
Benedict Wong38e52972018-05-07 20:06:44 -0700912 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800913 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800914 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700915 selAddrFamily,
916 IpSecManager.DIRECTION_OUT,
917 mOkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700918 0xffffffff,
919 mIfId);
Aaron Huang2617cf52021-11-29 16:31:32 +0800920 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800921 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700922 selAddrFamily,
923 IpSecManager.DIRECTION_IN,
924 mIkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700925 0xffffffff,
926 mIfId);
Yan Yanf4eceba2022-10-31 20:41:13 +0000927 mNetd.ipSecDeleteSecurityPolicy(
928 mUid,
929 selAddrFamily,
930 IpSecManager.DIRECTION_FWD,
931 mIkey,
932 0xffffffff,
933 mIfId);
Benedict Wong8edc5572018-01-19 17:36:02 -0800934 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800935 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong8edc5572018-01-19 17:36:02 -0800936 Log.e(
937 TAG,
938 "Failed to delete VTI with interface name: "
939 + mInterfaceName
940 + " and id: "
Benedict Wong97c3c942018-03-01 18:53:07 -0800941 + mResourceId, e);
Benedict Wong8edc5572018-01-19 17:36:02 -0800942 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800943
944 getResourceTracker().give();
945 releaseNetId(mIkey);
946 releaseNetId(mOkey);
947 }
948
Yan Yana2f3b492020-09-29 23:38:00 -0700949 @GuardedBy("IpSecService.this")
950 public void setUnderlyingNetwork(Network underlyingNetwork) {
951 // When #applyTunnelModeTransform is called, this new underlying network will be used to
952 // update the output mark of the input transform.
953 mUnderlyingNetwork = underlyingNetwork;
Benedict Wong8bc90732018-01-18 18:31:45 -0800954 }
955
Yan Yana2f3b492020-09-29 23:38:00 -0700956 @GuardedBy("IpSecService.this")
Benedict Wong8bc90732018-01-18 18:31:45 -0800957 public Network getUnderlyingNetwork() {
958 return mUnderlyingNetwork;
959 }
960
Yan Yana2f3b492020-09-29 23:38:00 -0700961 public String getInterfaceName() {
962 return mInterfaceName;
963 }
964
Benedict Wong8bc90732018-01-18 18:31:45 -0800965 /** Returns the local, outer address for the tunnelInterface */
966 public String getLocalAddress() {
967 return mLocalAddress;
968 }
969
970 /** Returns the remote, outer address for the tunnelInterface */
971 public String getRemoteAddress() {
972 return mRemoteAddress;
973 }
974
975 public int getIkey() {
976 return mIkey;
977 }
978
979 public int getOkey() {
980 return mOkey;
981 }
982
Benedict Wong5d749842018-09-06 11:31:25 -0700983 public int getIfId() {
984 return mIfId;
985 }
986
Benedict Wong8bc90732018-01-18 18:31:45 -0800987 @Override
988 protected ResourceTracker getResourceTracker() {
989 return getUserRecord().mTunnelQuotaTracker;
990 }
991
992 @Override
993 public void invalidate() {
994 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
995 }
996
997 @Override
998 public String toString() {
999 return new StringBuilder()
1000 .append("{super=")
1001 .append(super.toString())
1002 .append(", mInterfaceName=")
1003 .append(mInterfaceName)
1004 .append(", mUnderlyingNetwork=")
1005 .append(mUnderlyingNetwork)
1006 .append(", mLocalAddress=")
1007 .append(mLocalAddress)
1008 .append(", mRemoteAddress=")
1009 .append(mRemoteAddress)
1010 .append(", mIkey=")
1011 .append(mIkey)
1012 .append(", mOkey=")
1013 .append(mOkey)
1014 .append("}")
1015 .toString();
1016 }
1017 }
1018
Benedict Wongcbd329b2017-12-13 17:16:53 -08001019 /**
1020 * Tracks a UDP encap socket, and manages cleanup paths
1021 *
1022 * <p>While this class does not manage non-kernel resources, race conditions around socket
1023 * binding require that the service creates the encap socket, binds it and applies the socket
1024 * policy before handing it to a user.
1025 */
1026 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -07001027 private FileDescriptor mSocket;
1028 private final int mPort;
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001029 private final int mFamily; // TODO: what about IPV6_ADDRFORM?
Nathan Harold031acb82017-03-07 13:23:36 -08001030
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001031 EncapSocketRecord(int resourceId, FileDescriptor socket, int port, int family) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001032 super(resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001033 mSocket = socket;
1034 mPort = port;
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001035 mFamily = family;
Nathan Harold80865392017-04-04 19:37:48 -07001036 }
1037
1038 /** always guarded by IpSecService#this */
1039 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001040 public void freeUnderlyingResources() {
Nathan Harold80865392017-04-04 19:37:48 -07001041 Log.d(TAG, "Closing port " + mPort);
1042 IoUtils.closeQuietly(mSocket);
1043 mSocket = null;
Nathan Harold80865392017-04-04 19:37:48 -07001044
Benedict Wong6855aee2017-11-16 15:27:22 -08001045 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -07001046 }
1047
Nathan Harold80865392017-04-04 19:37:48 -07001048 public int getPort() {
1049 return mPort;
1050 }
1051
Benedict Wonga386e372018-03-27 16:55:48 -07001052 public FileDescriptor getFileDescriptor() {
Nathan Harold80865392017-04-04 19:37:48 -07001053 return mSocket;
1054 }
ludi89194d62017-05-22 10:52:23 -07001055
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001056 public int getFamily() {
1057 return mFamily;
1058 }
1059
ludi89194d62017-05-22 10:52:23 -07001060 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001061 protected ResourceTracker getResourceTracker() {
1062 return getUserRecord().mSocketQuotaTracker;
1063 }
1064
1065 @Override
1066 public void invalidate() {
1067 getUserRecord().removeEncapSocketRecord(mResourceId);
1068 }
1069
1070 @Override
ludi89194d62017-05-22 10:52:23 -07001071 public String toString() {
1072 return new StringBuilder()
1073 .append("{super=")
1074 .append(super.toString())
1075 .append(", mSocket=")
1076 .append(mSocket)
1077 .append(", mPort=")
1078 .append(mPort)
1079 .append("}")
1080 .toString();
1081 }
Nathan Harold80865392017-04-04 19:37:48 -07001082 }
Nathan Harold031acb82017-03-07 13:23:36 -08001083
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001084 /**
1085 * Constructs a new IpSecService instance
1086 *
1087 * @param context Binder context for this service
1088 */
Aaron Huangb944ff12022-01-12 15:11:01 +08001089 public IpSecService(Context context) {
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001090 this(context, new Dependencies());
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001091 }
1092
Nathan Harold65ef8432018-03-15 18:06:06 -07001093 @NonNull
1094 private AppOpsManager getAppOpsManager() {
1095 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Aaron Huangfbae3082021-12-06 15:18:42 +08001096 if (appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
Nathan Harold65ef8432018-03-15 18:06:06 -07001097 return appOps;
1098 }
1099
ludi5e623ea2017-05-12 09:15:00 -07001100 /** @hide */
1101 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001102 public IpSecService(Context context, Dependencies deps) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001103 this(
1104 context,
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001105 deps,
Nathan Harold5a19b952018-01-05 19:25:13 -08001106 (fd, uid) -> {
1107 try {
1108 TrafficStats.setThreadStatsUid(uid);
1109 TrafficStats.tagFileDescriptor(fd);
1110 } finally {
1111 TrafficStats.clearThreadStatsUid();
1112 }
1113 });
Benedict Wong083faee2017-12-03 19:42:36 -08001114 }
1115
1116 /** @hide */
1117 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001118 public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
ludi5e623ea2017-05-12 09:15:00 -07001119 mContext = context;
Aaron Huang2617cf52021-11-29 16:31:32 +08001120 mDeps = Objects.requireNonNull(deps, "Missing dependencies.");
Benedict Wong083faee2017-12-03 19:42:36 -08001121 mUidFdTagger = uidFdTagger;
Yan Yanab5006c2023-11-15 21:41:51 +00001122 mIpSecXfrmCtrl = mDeps.getIpSecXfrmController();
Aaron Huang2617cf52021-11-29 16:31:32 +08001123 try {
1124 mNetd = mDeps.getNetdInstance(mContext);
1125 } catch (RemoteException e) {
1126 throw e.rethrowFromSystemServer();
1127 }
ludi5e623ea2017-05-12 09:15:00 -07001128 }
1129
Nathan Harold19b99d92017-08-23 13:46:33 -07001130 /**
1131 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1132 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1133 */
1134 private static void checkInetAddress(String inetAddress) {
1135 if (TextUtils.isEmpty(inetAddress)) {
1136 throw new IllegalArgumentException("Unspecified address");
1137 }
1138
Serik Beketayev7f507332020-12-06 22:31:23 -08001139 InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
Nathan Harold19b99d92017-08-23 13:46:33 -07001140
1141 if (checkAddr.isAnyLocalAddress()) {
1142 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1143 }
1144 }
1145
1146 /**
1147 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1148 * DIRECTION_IN or DIRECTION_OUT
1149 */
Benedict Wong908d34e2021-04-15 11:59:16 -07001150 private void checkDirection(int direction) {
Nathan Harold19b99d92017-08-23 13:46:33 -07001151 switch (direction) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001152 case IpSecManager.DIRECTION_OUT:
1153 case IpSecManager.DIRECTION_IN:
Nathan Harold19b99d92017-08-23 13:46:33 -07001154 return;
Benedict Wong908d34e2021-04-15 11:59:16 -07001155 case IpSecManager.DIRECTION_FWD:
Benedict Wong47b528c2021-05-10 18:26:02 -07001156 // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
Benedict Wong908d34e2021-04-15 11:59:16 -07001157 PermissionUtils.enforceNetworkStackPermission(mContext);
1158 return;
Nathan Harold19b99d92017-08-23 13:46:33 -07001159 }
1160 throw new IllegalArgumentException("Invalid Direction: " + direction);
1161 }
1162
Nathan Harold031acb82017-03-07 13:23:36 -08001163 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri20e96c52017-11-16 10:58:01 -08001164 @Override
1165 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harold5a19b952018-01-05 19:25:13 -08001166 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1167 checkInetAddress(destinationAddress);
Nathan Harold1b88f0e2018-03-28 08:52:51 -07001168 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1169 if (requestedSpi > 0 && requestedSpi < 256) {
1170 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1171 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001172 Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harold19b99d92017-08-23 13:46:33 -07001173
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001174 int callingUid = Binder.getCallingUid();
1175 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001176 final int resourceId = mNextResourceId++;
Nathan Harold031acb82017-03-07 13:23:36 -08001177
1178 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -08001179 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001180 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001181 return new IpSecSpiResponse(
Nathan Harold19b99d92017-08-23 13:46:33 -07001182 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold6e4681c2017-04-24 16:16:34 -07001183 }
Nathan Harold5a19b952018-01-05 19:25:13 -08001184
Aaron Huang2617cf52021-11-29 16:31:32 +08001185 spi = mNetd.ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold031acb82017-03-07 13:23:36 -08001186 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong6855aee2017-11-16 15:27:22 -08001187 userRecord.mSpiRecords.put(
Nathan Harold80865392017-04-04 19:37:48 -07001188 resourceId,
Benedict Wong6855aee2017-11-16 15:27:22 -08001189 new RefcountedResource<SpiRecord>(
Aaron Huang2617cf52021-11-29 16:31:32 +08001190 new SpiRecord(resourceId, "",
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001191 destinationAddress, spi), binder));
Nathan Harold031acb82017-03-07 13:23:36 -08001192 } catch (ServiceSpecificException e) {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001193 if (e.errorCode == OsConstants.ENOENT) {
1194 return new IpSecSpiResponse(
1195 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1196 }
1197 throw e;
Nathan Harold031acb82017-03-07 13:23:36 -08001198 } catch (RemoteException e) {
1199 throw e.rethrowFromSystemServer();
1200 }
Nathan Harold80865392017-04-04 19:37:48 -07001201 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1202 }
1203
1204 /* This method should only be called from Binder threads. Do not call this from
1205 * within the system server as it will crash the system on failure.
1206 */
Benedict Wong6855aee2017-11-16 15:27:22 -08001207 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold80865392017-04-04 19:37:48 -07001208 throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001209 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold031acb82017-03-07 13:23:36 -08001210 }
1211
1212 /** Release a previously allocated SPI that has been registered with the system server */
1213 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001214 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1215 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1216 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001217 }
1218
1219 /**
1220 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1221 * be unbound.
1222 *
1223 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1224 * a random open port and then bind by number, this function creates a temp socket, binds to a
1225 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1226 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1227 * FileHandle.
1228 *
1229 * <p>The loop in this function handles the inherent race window between un-binding to a port
1230 * and re-binding, during which the system could *technically* hand that port out to someone
1231 * else.
1232 */
Lorenzo Colittib31a85a2023-02-08 15:03:27 +09001233 private int bindToRandomPort(FileDescriptor sockFd, int family, InetAddress localAddr)
1234 throws IOException {
Nathan Harold80865392017-04-04 19:37:48 -07001235 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1236 try {
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001237 FileDescriptor probeSocket = Os.socket(family, SOCK_DGRAM, IPPROTO_UDP);
Lorenzo Colittib31a85a2023-02-08 15:03:27 +09001238 Os.bind(probeSocket, localAddr, 0);
Nathan Harold80865392017-04-04 19:37:48 -07001239 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1240 Os.close(probeSocket);
1241 Log.v(TAG, "Binding to port " + port);
Lorenzo Colittib31a85a2023-02-08 15:03:27 +09001242 Os.bind(sockFd, localAddr, port);
Benedict Wongc423cc82017-10-10 20:44:28 -07001243 return port;
Nathan Harold80865392017-04-04 19:37:48 -07001244 } catch (ErrnoException e) {
1245 // Someone miraculously claimed the port just after we closed probeSocket.
1246 if (e.errno == OsConstants.EADDRINUSE) {
1247 continue;
1248 }
1249 throw e.rethrowAsIOException();
1250 }
1251 }
1252 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1253 }
Nathan Harold031acb82017-03-07 13:23:36 -08001254
1255 /**
Benedict Wong083faee2017-12-03 19:42:36 -08001256 * Functional interface to do traffic tagging of given sockets to UIDs.
1257 *
1258 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1259 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1260 *
1261 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1262 * methods that cannot be easily mocked/tested.
1263 */
1264 @VisibleForTesting
1265 public interface UidFdTagger {
1266 /**
1267 * Sets socket tag to assign all traffic to the provided UID.
1268 *
1269 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1270 * should be accounted to the UID of the unprivileged application.
1271 */
Aaron Huangfbae3082021-12-06 15:18:42 +08001272 void tag(FileDescriptor fd, int uid) throws IOException;
Benedict Wong083faee2017-12-03 19:42:36 -08001273 }
1274
1275 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001276 * Open a socket via the system server and bind it to the specified port (random if port=0).
1277 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1278 * cache the socket and a record of its owner so that it can and must be freed when no longer
1279 * needed.
1280 */
1281 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001282 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1283 throws RemoteException {
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001284 // Experimental support for IPv6 UDP encap.
1285 final int family;
1286 final InetAddress localAddr;
1287 if (SdkLevel.isAtLeastU() && port >= 65536) {
1288 PermissionUtils.enforceNetworkStackPermissionOr(mContext, NETWORK_SETTINGS);
1289 port -= 65536;
1290 family = AF_INET6;
1291 localAddr = IN6ADDR_ANY;
1292 } else {
1293 family = AF_INET;
1294 localAddr = INADDR_ANY;
1295 }
1296
Nathan Harold80865392017-04-04 19:37:48 -07001297 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1298 throw new IllegalArgumentException(
1299 "Specified port number must be a valid non-reserved UDP port");
1300 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001301 Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
Nathan Harold19b99d92017-08-23 13:46:33 -07001302
Benedict Wong083faee2017-12-03 19:42:36 -08001303 int callingUid = Binder.getCallingUid();
1304 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001305 final int resourceId = mNextResourceId++;
Aaron Huangb01254f2021-12-23 10:47:05 +08001306
1307 ParcelFileDescriptor pFd = null;
Nathan Harold80865392017-04-04 19:37:48 -07001308 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001309 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001310 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1311 }
1312
Aaron Huangb01254f2021-12-23 10:47:05 +08001313 FileDescriptor sockFd = null;
1314 try {
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001315 sockFd = Os.socket(family, SOCK_DGRAM, IPPROTO_UDP);
Aaron Huangb01254f2021-12-23 10:47:05 +08001316 pFd = ParcelFileDescriptor.dup(sockFd);
1317 } finally {
1318 IoUtils.closeQuietly(sockFd);
1319 }
Nathan Harold80865392017-04-04 19:37:48 -07001320
Aaron Huangb01254f2021-12-23 10:47:05 +08001321 mUidFdTagger.tag(pFd.getFileDescriptor(), callingUid);
Nathan Harold80865392017-04-04 19:37:48 -07001322 // This code is common to both the unspecified and specified port cases
1323 Os.setsockoptInt(
Aaron Huangb01254f2021-12-23 10:47:05 +08001324 pFd.getFileDescriptor(),
Nathan Harold80865392017-04-04 19:37:48 -07001325 OsConstants.IPPROTO_UDP,
1326 OsConstants.UDP_ENCAP,
1327 OsConstants.UDP_ENCAP_ESPINUDP);
1328
Aaron Huangb01254f2021-12-23 10:47:05 +08001329 mNetd.ipSecSetEncapSocketOwner(pFd, callingUid);
Benedict Wong17687442017-12-06 21:56:35 -08001330 if (port != 0) {
1331 Log.v(TAG, "Binding to port " + port);
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001332 Os.bind(pFd.getFileDescriptor(), localAddr, port);
Benedict Wong17687442017-12-06 21:56:35 -08001333 } else {
Lorenzo Colittib31a85a2023-02-08 15:03:27 +09001334 port = bindToRandomPort(pFd.getFileDescriptor(), family, localAddr);
Benedict Wong17687442017-12-06 21:56:35 -08001335 }
1336
Benedict Wong6855aee2017-11-16 15:27:22 -08001337 userRecord.mEncapSocketRecords.put(
1338 resourceId,
1339 new RefcountedResource<EncapSocketRecord>(
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001340 new EncapSocketRecord(resourceId, pFd.getFileDescriptor(), port,
1341 family),
Aaron Huangb01254f2021-12-23 10:47:05 +08001342 binder));
1343 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port,
1344 pFd.getFileDescriptor());
Nathan Harold80865392017-04-04 19:37:48 -07001345 } catch (IOException | ErrnoException e) {
Aaron Huangb01254f2021-12-23 10:47:05 +08001346 try {
1347 if (pFd != null) {
1348 pFd.close();
1349 }
1350 } catch (IOException ex) {
1351 // Nothing can be done at this point
1352 Log.e(TAG, "Failed to close pFd.");
1353 }
Nathan Harold80865392017-04-04 19:37:48 -07001354 }
1355 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1356 // The only reasonable condition that would cause that is resource unavailable.
1357 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold031acb82017-03-07 13:23:36 -08001358 }
1359
1360 /** close a socket that has been been allocated by and registered with the system server */
1361 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001362 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1363 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1364 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001365 }
Nathan Harold031acb82017-03-07 13:23:36 -08001366
Benedict Wong8bc90732018-01-18 18:31:45 -08001367 /**
1368 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1369 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1370 * needed.
1371 */
1372 @Override
1373 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001374 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1375 String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001376 enforceTunnelFeatureAndPermissions(callingPackage);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001377 Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
1378 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
Benedict Wong8bc90732018-01-18 18:31:45 -08001379 checkInetAddress(localAddr);
1380 checkInetAddress(remoteAddr);
1381
1382 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1383 // network (b/72316676).
1384
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001385 int callerUid = Binder.getCallingUid();
1386 UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001387 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1388 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1389 }
1390
1391 final int resourceId = mNextResourceId++;
1392 final int ikey = reserveNetId();
1393 final int okey = reserveNetId();
Nathan Harold7be7f452018-04-26 11:47:14 -07001394 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001395
Benedict Wong8edc5572018-01-19 17:36:02 -08001396 try {
1397 // Calls to netd:
1398 // Create VTI
1399 // Add inbound/outbound global policies
1400 // (use reqid = 0)
Aaron Huang2617cf52021-11-29 16:31:32 +08001401 mNetd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001402
paulhu00e34562021-10-26 09:00:50 +00001403 BinderUtils.withCleanCallingIdentity(() -> {
Aaron Huang2617cf52021-11-29 16:31:32 +08001404 NetdUtils.setInterfaceUp(mNetd, intfName);
Benedict Wong529e8aa2020-02-11 23:49:36 -08001405 });
1406
Benedict Wong38e52972018-05-07 20:06:44 -07001407 for (int selAddrFamily : ADDRESS_FAMILIES) {
1408 // Always send down correct local/remote addresses for template.
Aaron Huang2617cf52021-11-29 16:31:32 +08001409 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001410 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001411 selAddrFamily,
1412 IpSecManager.DIRECTION_OUT,
1413 localAddr,
1414 remoteAddr,
1415 0,
1416 okey,
Benedict Wong5d749842018-09-06 11:31:25 -07001417 0xffffffff,
1418 resourceId);
Aaron Huang2617cf52021-11-29 16:31:32 +08001419 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001420 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001421 selAddrFamily,
1422 IpSecManager.DIRECTION_IN,
1423 remoteAddr,
1424 localAddr,
1425 0,
1426 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001427 0xffffffff,
1428 resourceId);
Benedict Wong47b528c2021-05-10 18:26:02 -07001429
1430 // Add a forwarding policy on the tunnel interface. In order to support forwarding
1431 // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
1432 //
1433 // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
1434 // forwarding will be blocked by default (as would be the case if this policy was
1435 // absent).
1436 //
1437 // This is necessary only on the tunnel interface, and not any the interface to
1438 // which traffic will be forwarded to.
Aaron Huang2617cf52021-11-29 16:31:32 +08001439 mNetd.ipSecAddSecurityPolicy(
Benedict Wong908d34e2021-04-15 11:59:16 -07001440 callerUid,
1441 selAddrFamily,
1442 IpSecManager.DIRECTION_FWD,
1443 remoteAddr,
1444 localAddr,
1445 0,
1446 ikey,
1447 0xffffffff,
1448 resourceId);
Benedict Wong8edc5572018-01-19 17:36:02 -08001449 }
1450
1451 userRecord.mTunnelInterfaceRecords.put(
1452 resourceId,
1453 new RefcountedResource<TunnelInterfaceRecord>(
1454 new TunnelInterfaceRecord(
1455 resourceId,
1456 intfName,
1457 underlyingNetwork,
1458 localAddr,
1459 remoteAddr,
1460 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001461 okey,
1462 resourceId),
Benedict Wong8edc5572018-01-19 17:36:02 -08001463 binder));
1464 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1465 } catch (RemoteException e) {
1466 // Release keys if we got an error.
1467 releaseNetId(ikey);
1468 releaseNetId(okey);
1469 throw e.rethrowFromSystemServer();
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001470 } catch (Throwable t) {
1471 // Release keys if we got an error.
1472 releaseNetId(ikey);
1473 releaseNetId(okey);
1474 throw t;
Benedict Wong8edc5572018-01-19 17:36:02 -08001475 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001476 }
1477
1478 /**
1479 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1480 * from multiple local IP addresses over the same tunnel.
1481 */
1482 @Override
Benedict Wong97c3c942018-03-01 18:53:07 -08001483 public synchronized void addAddressToTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001484 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
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
1488 // Get tunnelInterface record; if no such interface is found, will throw
1489 // IllegalArgumentException
1490 TunnelInterfaceRecord tunnelInterfaceInfo =
1491 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1492
Benedict Wong97c3c942018-03-01 18:53:07 -08001493 try {
1494 // We can assume general validity of the IP address, since we get them as a
1495 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001496 mNetd.interfaceAddAddress(
1497 tunnelInterfaceInfo.mInterfaceName,
1498 localAddr.getAddress().getHostAddress(),
1499 localAddr.getPrefixLength());
Benedict Wong97c3c942018-03-01 18:53:07 -08001500 } catch (RemoteException e) {
1501 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001502 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001503 }
1504
1505 /**
1506 * Remove a new local address from the tunnel interface. After removal, the address will no
1507 * longer be available to send from, or receive on.
1508 */
1509 @Override
1510 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001511 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001512 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001513
Nathan Harold65ef8432018-03-15 18:06:06 -07001514 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8bc90732018-01-18 18:31:45 -08001515 // Get tunnelInterface record; if no such interface is found, will throw
1516 // IllegalArgumentException
1517 TunnelInterfaceRecord tunnelInterfaceInfo =
1518 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1519
Benedict Wong97c3c942018-03-01 18:53:07 -08001520 try {
1521 // We can assume general validity of the IP address, since we get them as a
1522 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001523 mNetd.interfaceDelAddress(
Benedict Wong97c3c942018-03-01 18:53:07 -08001524 tunnelInterfaceInfo.mInterfaceName,
1525 localAddr.getAddress().getHostAddress(),
1526 localAddr.getPrefixLength());
1527 } catch (RemoteException e) {
1528 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001529 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001530 }
1531
Yan Yana2f3b492020-09-29 23:38:00 -07001532 /** Set TunnelInterface to use a specific underlying network. */
1533 @Override
1534 public synchronized void setNetworkForTunnelInterface(
1535 int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
1536 enforceTunnelFeatureAndPermissions(callingPackage);
1537 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1538
1539 final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1540
1541 // Get tunnelInterface record; if no such interface is found, will throw
1542 // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
1543 final TunnelInterfaceRecord tunnelInterfaceInfo =
1544 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1545
1546 final ConnectivityManager connectivityManager =
1547 mContext.getSystemService(ConnectivityManager.class);
1548 final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
Yan Yanbe3eb3d2022-05-16 17:13:45 -07001549 if (lp == null) {
1550 throw new IllegalArgumentException(
1551 "LinkProperties is null. The underlyingNetwork may not be functional");
1552 }
1553
Yan Yana2f3b492020-09-29 23:38:00 -07001554 if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
1555 throw new IllegalArgumentException(
1556 "Underlying network cannot be the network being exposed by this tunnel");
1557 }
1558
1559 // It is meaningless to check if the network exists or is valid because the network might
1560 // disconnect at any time after it passes the check.
1561
1562 tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
1563 }
1564
Benedict Wong8bc90732018-01-18 18:31:45 -08001565 /**
1566 * Delete a TunnelInterface that has been been allocated by and registered with the system
1567 * server
1568 */
1569 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001570 public synchronized void deleteTunnelInterface(
1571 int resourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001572 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001573 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1574 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1575 }
1576
Benedict Wong70867e52017-11-06 20:49:10 -08001577 @VisibleForTesting
Nathan Harold5a19b952018-01-05 19:25:13 -08001578 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1579 IpSecAlgorithm auth = config.getAuthentication();
1580 IpSecAlgorithm crypt = config.getEncryption();
1581 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong70867e52017-11-06 20:49:10 -08001582
Nathan Harold5a19b952018-01-05 19:25:13 -08001583 // Validate the algorithm set
1584 Preconditions.checkArgument(
1585 aead != null || crypt != null || auth != null,
1586 "No Encryption or Authentication algorithms specified");
1587 Preconditions.checkArgument(
1588 auth == null || auth.isAuthentication(),
1589 "Unsupported algorithm for Authentication");
1590 Preconditions.checkArgument(
Benedict Wong70867e52017-11-06 20:49:10 -08001591 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harold5a19b952018-01-05 19:25:13 -08001592 Preconditions.checkArgument(
1593 aead == null || aead.isAead(),
1594 "Unsupported algorithm for Authenticated Encryption");
1595 Preconditions.checkArgument(
1596 aead == null || (auth == null && crypt == null),
1597 "Authenticated Encryption is mutually exclusive with other Authentication "
1598 + "or Encryption algorithms");
Benedict Wong70867e52017-11-06 20:49:10 -08001599 }
1600
evitayan43d93a02018-03-22 17:53:08 -07001601 private int getFamily(String inetAddress) {
1602 int family = AF_UNSPEC;
Serik Beketayev7f507332020-12-06 22:31:23 -08001603 InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
evitayan43d93a02018-03-22 17:53:08 -07001604 if (checkAddress instanceof Inet4Address) {
1605 family = AF_INET;
1606 } else if (checkAddress instanceof Inet6Address) {
1607 family = AF_INET6;
1608 }
1609 return family;
1610 }
1611
Nathan Harold031acb82017-03-07 13:23:36 -08001612 /**
Chiachang Wang2fea4a72020-08-12 12:23:59 +08001613 * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
Nathan Harold19b99d92017-08-23 13:46:33 -07001614 * IllegalArgumentException if they are not.
1615 */
1616 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001617 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001618 EncapSocketRecord encapSocketRecord = null;
Benedict Wong6855aee2017-11-16 15:27:22 -08001619
Nathan Harold19b99d92017-08-23 13:46:33 -07001620 switch (config.getEncapType()) {
1621 case IpSecTransform.ENCAP_NONE:
1622 break;
1623 case IpSecTransform.ENCAP_ESPINUDP:
1624 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong6855aee2017-11-16 15:27:22 -08001625 // Retrieve encap socket record; will throw IllegalArgumentException if not found
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001626 encapSocketRecord = userRecord.mEncapSocketRecords.getResourceOrThrow(
Benedict Wong6855aee2017-11-16 15:27:22 -08001627 config.getEncapSocketResourceId());
Nathan Harold19b99d92017-08-23 13:46:33 -07001628
1629 int port = config.getEncapRemotePort();
1630 if (port <= 0 || port > 0xFFFF) {
1631 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1632 }
1633 break;
1634 default:
1635 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1636 }
1637
Nathan Harold5a19b952018-01-05 19:25:13 -08001638 validateAlgorithms(config);
Nathan Harold19b99d92017-08-23 13:46:33 -07001639
Nathan Harold5a19b952018-01-05 19:25:13 -08001640 // Retrieve SPI record; will throw IllegalArgumentException if not found
1641 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1642
Benedict Wong68aac2a2017-12-13 18:26:40 -08001643 // Check to ensure that SPI has not already been used.
1644 if (s.getOwnedByTransform()) {
1645 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1646 }
1647
Nathan Harold5a19b952018-01-05 19:25:13 -08001648 // If no remote address is supplied, then use one from the SPI.
1649 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1650 config.setDestinationAddress(s.getDestinationAddress());
1651 }
1652
1653 // All remote addresses must match
1654 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1655 throw new IllegalArgumentException("Mismatched remote addresseses.");
1656 }
1657
1658 // This check is technically redundant due to the chain of custody between the SPI and
1659 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1660 // the transform, this will prevent us from messing up.
1661 checkInetAddress(config.getDestinationAddress());
1662
1663 // Require a valid source address for all transforms.
1664 checkInetAddress(config.getSourceAddress());
1665
evitayan43d93a02018-03-22 17:53:08 -07001666 // Check to ensure source and destination have the same address family.
1667 String sourceAddress = config.getSourceAddress();
1668 String destinationAddress = config.getDestinationAddress();
1669 int sourceFamily = getFamily(sourceAddress);
1670 int destinationFamily = getFamily(destinationAddress);
1671 if (sourceFamily != destinationFamily) {
1672 throw new IllegalArgumentException(
1673 "Source address ("
1674 + sourceAddress
1675 + ") and destination address ("
1676 + destinationAddress
1677 + ") have different address families.");
1678 }
1679
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001680 if (encapSocketRecord != null && encapSocketRecord.getFamily() != destinationFamily) {
evitayan43d93a02018-03-22 17:53:08 -07001681 throw new IllegalArgumentException(
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001682 "UDP encapsulation socket and destination address families must match");
evitayan43d93a02018-03-22 17:53:08 -07001683 }
1684
Nathan Harold5a19b952018-01-05 19:25:13 -08001685 switch (config.getMode()) {
1686 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold025aae12018-02-02 18:34:25 -08001687 break;
Nathan Harold5a19b952018-01-05 19:25:13 -08001688 case IpSecTransform.MODE_TUNNEL:
1689 break;
1690 default:
1691 throw new IllegalArgumentException(
1692 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harold19b99d92017-08-23 13:46:33 -07001693 }
Benedict Wong683441d2018-07-25 18:46:19 -07001694
1695 config.setMarkValue(0);
1696 config.setMarkMask(0);
Nathan Harold19b99d92017-08-23 13:46:33 -07001697 }
1698
Benedict Wong2b6a14e2018-09-13 16:45:12 -07001699 private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
Nathan Harold7c250ae2018-05-15 19:18:38 -07001700
Benedict Wonge9763752018-11-08 19:45:34 -08001701 private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1702 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1703 throw new UnsupportedOperationException(
1704 "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1705 }
1706
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001707 Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Benedict Wongc85b7b02019-11-12 22:31:51 -08001708
1709 // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
1710 // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
1711 // permission or is the System Server.
1712 if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
1713 TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1714 return;
Nathan Harold65ef8432018-03-15 18:06:06 -07001715 }
Benedict Wongc85b7b02019-11-12 22:31:51 -08001716 mContext.enforceCallingOrSelfPermission(
1717 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
Nathan Harold025aae12018-02-02 18:34:25 -08001718 }
1719
Yan Yana48dcd92022-10-18 00:03:20 +00001720 private void enforceMigrateFeature() {
1721 if (!mContext.getPackageManager().hasSystemFeature(FEATURE_IPSEC_TUNNEL_MIGRATION)) {
1722 throw new UnsupportedOperationException(
1723 "IPsec Tunnel migration requires"
1724 + " PackageManager.FEATURE_IPSEC_TUNNEL_MIGRATION");
1725 }
1726 }
1727
Benedict Wong8edc5572018-01-19 17:36:02 -08001728 private void createOrUpdateTransform(
1729 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1730 throws RemoteException {
1731
1732 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1733 if (encapType != IpSecTransform.ENCAP_NONE) {
1734 encapLocalPort = socketRecord.getPort();
1735 encapRemotePort = c.getEncapRemotePort();
1736 }
1737
1738 IpSecAlgorithm auth = c.getAuthentication();
1739 IpSecAlgorithm crypt = c.getEncryption();
1740 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1741
Benedict Wong778327e2018-03-15 19:41:41 -07001742 String cryptName;
1743 if (crypt == null) {
1744 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1745 } else {
1746 cryptName = crypt.getName();
1747 }
1748
Aaron Huang2617cf52021-11-29 16:31:32 +08001749 mNetd.ipSecAddSecurityAssociation(
1750 Binder.getCallingUid(),
1751 c.getMode(),
1752 c.getSourceAddress(),
1753 c.getDestinationAddress(),
1754 (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
1755 spiRecord.getSpi(),
1756 c.getMarkValue(),
1757 c.getMarkMask(),
1758 (auth != null) ? auth.getName() : "",
1759 (auth != null) ? auth.getKey() : new byte[] {},
1760 (auth != null) ? auth.getTruncationLengthBits() : 0,
1761 cryptName,
1762 (crypt != null) ? crypt.getKey() : new byte[] {},
1763 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1764 (authCrypt != null) ? authCrypt.getName() : "",
1765 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1766 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1767 encapType,
1768 encapLocalPort,
1769 encapRemotePort,
1770 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001771 }
1772
Nathan Harold19b99d92017-08-23 13:46:33 -07001773 /**
Benedict Wong8edc5572018-01-19 17:36:02 -08001774 * Create a IPsec transform, which represents a single security association in the kernel. The
1775 * transform will be cached by the system server and must be freed when no longer needed. It is
1776 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1777 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold031acb82017-03-07 13:23:36 -08001778 */
1779 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001780 public synchronized IpSecTransformResponse createTransform(
1781 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001782 Objects.requireNonNull(c);
Nathan Harold65ef8432018-03-15 18:06:06 -07001783 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
Benedict Wonge9763752018-11-08 19:45:34 -08001784 enforceTunnelFeatureAndPermissions(callingPackage);
Nathan Harold65ef8432018-03-15 18:06:06 -07001785 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001786 checkIpSecConfig(c);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001787 Objects.requireNonNull(binder, "Null Binder passed to createTransform");
Nathan Haroldfdafce22017-12-13 19:16:33 -08001788 final int resourceId = mNextResourceId++;
Benedict Wong6855aee2017-11-16 15:27:22 -08001789
1790 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wongcbd329b2017-12-13 17:16:53 -08001791 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong6855aee2017-11-16 15:27:22 -08001792
1793 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001794 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1795 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001796
Benedict Wong6855aee2017-11-16 15:27:22 -08001797 EncapSocketRecord socketRecord = null;
Benedict Wong8edc5572018-01-19 17:36:02 -08001798 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001799 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1800 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1801 c.getEncapSocketResourceId());
1802 dependencies.add(refcountedSocketRecord);
Benedict Wong6855aee2017-11-16 15:27:22 -08001803 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold80865392017-04-04 19:37:48 -07001804 }
1805
Nathan Harold5a19b952018-01-05 19:25:13 -08001806 RefcountedResource<SpiRecord> refcountedSpiRecord =
1807 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1808 dependencies.add(refcountedSpiRecord);
1809 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong6855aee2017-11-16 15:27:22 -08001810
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001811 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong8edc5572018-01-19 17:36:02 -08001812
1813 // SA was created successfully, time to construct a record and lock it away
Benedict Wong6855aee2017-11-16 15:27:22 -08001814 userRecord.mTransformRecords.put(
1815 resourceId,
1816 new RefcountedResource<TransformRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001817 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong6855aee2017-11-16 15:27:22 -08001818 binder,
1819 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold80865392017-04-04 19:37:48 -07001820 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001821 }
1822
1823 /**
Yan Yane114b382021-02-16 16:29:48 -08001824 * Migrate an active Tunnel Mode IPsec Transform to new source/destination addresses.
1825 *
1826 * <p>Begins the process of migrating a transform and cache the new addresses. To complete the
1827 * migration once started, callers MUST apply the same transform to the appropriate tunnel using
1828 * {@link #applyTunnelModeTransform}. Otherwise, the address update will not be committed and
1829 * the transform will still only process traffic between the current source and destination
1830 * address. One common use case is that the control plane will start the migration process and
1831 * then hand off the transform to the IPsec caller to perform the actual migration when the
1832 * tunnel is ready.
1833 *
1834 * <p>If this method is called multiple times before {@link #applyTunnelModeTransform} is
1835 * called, when the transform is applied, it will be migrated to the addresses from the last
1836 * call.
1837 *
1838 * <p>The provided source and destination addresses MUST share the same address family, but they
1839 * can have a different family from the current addresses.
1840 *
1841 * <p>Transform migration is only supported for tunnel mode transforms. Calling this method on
1842 * other types of transforms will throw an {@code UnsupportedOperationException}.
1843 */
1844 @Override
1845 public synchronized void migrateTransform(
1846 int transformId,
1847 String newSourceAddress,
1848 String newDestinationAddress,
1849 String callingPackage) {
1850 Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
1851 Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
1852
1853 enforceTunnelFeatureAndPermissions(callingPackage);
Yan Yana48dcd92022-10-18 00:03:20 +00001854 enforceMigrateFeature();
Yan Yane114b382021-02-16 16:29:48 -08001855
1856 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1857 TransformRecord transformInfo =
1858 userRecord.mTransformRecords.getResourceOrThrow(transformId);
1859 transformInfo.startMigration(newSourceAddress, newDestinationAddress);
1860 }
1861
1862 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001863 * Delete a transport mode transform that was previously allocated by + registered with the
1864 * system server. If this is called on an inactive (or non-existent) transform, it will not
1865 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1866 * other reasons.
1867 */
1868 @Override
Benedict Wong0fff56e2018-01-18 14:38:16 -08001869 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001870 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1871 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001872 }
1873
Yan Yanab5006c2023-11-15 21:41:51 +00001874 @Override
1875 public synchronized IpSecTransformState getTransformState(int transformId)
1876 throws IllegalStateException, RemoteException {
1877 mContext.enforceCallingOrSelfPermission(
1878 android.Manifest.permission.ACCESS_NETWORK_STATE, "IpsecService#getTransformState");
1879
1880 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1881 TransformRecord transformInfo =
1882 userRecord.mTransformRecords.getResourceOrThrow(transformId);
1883
1884 final int spi = transformInfo.getSpiRecord().getSpi();
1885 final InetAddress destAddress =
1886 InetAddresses.parseNumericAddress(
1887 transformInfo.getConfig().getDestinationAddress());
1888 Log.d(TAG, "getTransformState for spi " + spi + " destAddress " + destAddress);
1889
1890 // Make netlink call
1891 final XfrmNetlinkNewSaMessage xfrmNewSaMsg;
1892 try {
1893 xfrmNewSaMsg = mIpSecXfrmCtrl.ipSecGetSa(destAddress, Integer.toUnsignedLong(spi));
1894 } catch (ErrnoException | IOException e) {
1895 Log.e(TAG, "getTransformState: failed to get IpSecTransformState" + e.toString());
1896 throw new IllegalStateException("Failed to get IpSecTransformState", e);
1897 }
1898
1899 // Keep the netlink socket open to save time for the next call. It is cheap to have a
1900 // persistent netlink socket in the system server
1901
1902 if (xfrmNewSaMsg == null) {
1903 Log.e(TAG, "getTransformState: failed to get IpSecTransformState xfrmNewSaMsg is null");
1904 throw new IllegalStateException("Failed to get IpSecTransformState");
1905 }
1906
1907 return new IpSecTransformState.Builder()
1908 .setTxHighestSequenceNumber(xfrmNewSaMsg.getTxSequenceNumber())
1909 .setRxHighestSequenceNumber(xfrmNewSaMsg.getRxSequenceNumber())
1910 .setPacketCount(xfrmNewSaMsg.getPacketCount())
1911 .setByteCount(xfrmNewSaMsg.getByteCount())
1912 .setReplayBitmap(xfrmNewSaMsg.getBitmap())
1913 .build();
1914 }
1915
Nathan Harold031acb82017-03-07 13:23:36 -08001916 /**
1917 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1918 * association as a correspondent policy to the provided socket
1919 */
1920 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001921 public synchronized void applyTransportModeTransform(
Nathan Harold5a19b952018-01-05 19:25:13 -08001922 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001923 int callingUid = Binder.getCallingUid();
1924 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harold5a19b952018-01-05 19:25:13 -08001925 checkDirection(direction);
Benedict Wong6855aee2017-11-16 15:27:22 -08001926 // Get transform record; if no transform is found, will throw IllegalArgumentException
1927 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001928
Nathan Harold80865392017-04-04 19:37:48 -07001929 // TODO: make this a function.
Aaron Huangfbae3082021-12-06 15:18:42 +08001930 if (info.mPid != getCallingPid() || info.mUid != callingUid) {
Nathan Harold80865392017-04-04 19:37:48 -07001931 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1932 }
1933
Benedict Wong8bc90732018-01-18 18:31:45 -08001934 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold80865392017-04-04 19:37:48 -07001935 IpSecConfig c = info.getConfig();
Benedict Wong8bc90732018-01-18 18:31:45 -08001936 Preconditions.checkArgument(
1937 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1938 "Transform mode was not Transport mode; cannot be applied to a socket");
1939
Aaron Huang2617cf52021-11-29 16:31:32 +08001940 mNetd.ipSecApplyTransportModeTransform(
1941 socket,
1942 callingUid,
1943 direction,
1944 c.getSourceAddress(),
1945 c.getDestinationAddress(),
1946 info.getSpiRecord().getSpi());
Nathan Harold031acb82017-03-07 13:23:36 -08001947 }
Nathan Harold80865392017-04-04 19:37:48 -07001948
Nathan Harold031acb82017-03-07 13:23:36 -08001949 /**
Nathan Harold5a19b952018-01-05 19:25:13 -08001950 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1951 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1952 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1953 * reserved for future improved input validation.
Nathan Harold031acb82017-03-07 13:23:36 -08001954 */
1955 @Override
Nathan Harold0d483b72018-01-17 01:00:20 -08001956 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1957 throws RemoteException {
Aaron Huang2617cf52021-11-29 16:31:32 +08001958 mNetd.ipSecRemoveTransportModeTransform(socket);
Nathan Harold031acb82017-03-07 13:23:36 -08001959 }
1960
Benedict Wong8bc90732018-01-18 18:31:45 -08001961 /**
1962 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
Yan Yane114b382021-02-16 16:29:48 -08001963 * security association as a correspondent policy to the provided interface.
1964 *
1965 * <p>If the transform is migrating, migrate the IPsec security association to new
1966 * source/destination addresses, and mark the migration as finished.
Benedict Wong8bc90732018-01-18 18:31:45 -08001967 */
1968 @Override
1969 public synchronized void applyTunnelModeTransform(
Yan Yane114b382021-02-16 16:29:48 -08001970 int tunnelResourceId, int direction, int transformResourceId, String callingPackage)
1971 throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001972 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001973 checkDirection(direction);
1974
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001975 int callingUid = Binder.getCallingUid();
1976 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001977
1978 // Get transform record; if no transform is found, will throw IllegalArgumentException
1979 TransformRecord transformInfo =
1980 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1981
1982 // Get tunnelInterface record; if no such interface is found, will throw
1983 // IllegalArgumentException
1984 TunnelInterfaceRecord tunnelInterfaceInfo =
1985 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1986
1987 // Get config and check that to-be-applied transform has the correct mode
1988 IpSecConfig c = transformInfo.getConfig();
1989 Preconditions.checkArgument(
1990 c.getMode() == IpSecTransform.MODE_TUNNEL,
1991 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1992
Benedict Wong8edc5572018-01-19 17:36:02 -08001993 EncapSocketRecord socketRecord = null;
1994 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1995 socketRecord =
1996 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1997 }
Benedict Wongec2e2e22019-10-03 11:09:00 -07001998 SpiRecord spiRecord = transformInfo.getSpiRecord();
Benedict Wong8edc5572018-01-19 17:36:02 -08001999
Benedict Wong8bc90732018-01-18 18:31:45 -08002000 int mark =
Benedict Wong38e52972018-05-07 20:06:44 -07002001 (direction == IpSecManager.DIRECTION_OUT)
2002 ? tunnelInterfaceInfo.getOkey()
Benedict Wong908d34e2021-04-15 11:59:16 -07002003 : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
Benedict Wong8bc90732018-01-18 18:31:45 -08002004
Benedict Wong8edc5572018-01-19 17:36:02 -08002005 try {
Benedict Wong5d749842018-09-06 11:31:25 -07002006 // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
2007 // SPI matching as part of the template resolution.
2008 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
2009 c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
2010
Benedict Wong683441d2018-07-25 18:46:19 -07002011 // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
2012 // (and backporting) would allow us to narrow the mark space, and ensure that the SA
2013 // and SPs have matching marks (as VTI are meant to be built).
2014 // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
2015 // config matches the actual allocated resources in the kernel.
Benedict Wongc6fcedd2018-11-21 21:24:55 -08002016 // All SAs will have zero marks (from creation time), and any policy that matches the
2017 // same src/dst could match these SAs. Non-IpSecService governed processes that
2018 // establish floating policies with the same src/dst may result in undefined
2019 // behavior. This is generally limited to vendor code due to the permissions
2020 // (CAP_NET_ADMIN) required.
Benedict Wong683441d2018-07-25 18:46:19 -07002021 //
2022 // c.setMarkValue(mark);
2023 // c.setMarkMask(0xffffffff);
Benedict Wong8edc5572018-01-19 17:36:02 -08002024
2025 if (direction == IpSecManager.DIRECTION_OUT) {
2026 // Set output mark via underlying network (output only)
2027 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
2028
Benedict Wong5d749842018-09-06 11:31:25 -07002029 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
2030 // but want to guarantee outbound packets are sent over the new SA.
Benedict Wongec2e2e22019-10-03 11:09:00 -07002031 spi = spiRecord.getSpi();
Benedict Wong5d749842018-09-06 11:31:25 -07002032 }
2033
2034 // Always update the policy with the relevant XFRM_IF_ID
2035 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +08002036 mNetd.ipSecUpdateSecurityPolicy(
2037 callingUid,
2038 selAddrFamily,
2039 direction,
2040 transformInfo.getConfig().getSourceAddress(),
2041 transformInfo.getConfig().getDestinationAddress(),
2042 spi, // If outbound, also add SPI to the policy.
2043 mark, // Must always set policy mark; ikey/okey for VTIs
2044 0xffffffff,
2045 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08002046 }
2047
2048 // Update SA with tunnel mark (ikey or okey based on direction)
2049 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
Yan Yane114b382021-02-16 16:29:48 -08002050
2051 if (transformInfo.isMigrating()) {
Yan Yana48dcd92022-10-18 00:03:20 +00002052 if (!mContext.getPackageManager()
2053 .hasSystemFeature(FEATURE_IPSEC_TUNNEL_MIGRATION)) {
2054 Log.wtf(
2055 TAG,
2056 "Attempted to migrate a transform without"
2057 + " FEATURE_IPSEC_TUNNEL_MIGRATION");
2058 }
2059
Yan Yane114b382021-02-16 16:29:48 -08002060 for (int selAddrFamily : ADDRESS_FAMILIES) {
2061 final IpSecMigrateInfoParcel migrateInfo =
2062 new IpSecMigrateInfoParcel(
2063 Binder.getCallingUid(),
2064 selAddrFamily,
2065 direction,
2066 c.getSourceAddress(),
2067 c.getDestinationAddress(),
2068 transformInfo.getNewSourceAddress(),
2069 transformInfo.getNewDestinationAddress(),
2070 c.getXfrmInterfaceId());
2071
2072 mNetd.ipSecMigrate(migrateInfo);
2073 }
2074 transformInfo.finishMigration();
2075 }
Benedict Wong8edc5572018-01-19 17:36:02 -08002076 } catch (ServiceSpecificException e) {
2077 if (e.errorCode == EINVAL) {
2078 throw new IllegalArgumentException(e.toString());
2079 } else {
2080 throw e;
2081 }
2082 }
Benedict Wong8bc90732018-01-18 18:31:45 -08002083 }
2084
Nathan Harold031acb82017-03-07 13:23:36 -08002085 @Override
ludi89194d62017-05-22 10:52:23 -07002086 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08002087 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludi89194d62017-05-22 10:52:23 -07002088
2089 pw.println("IpSecService dump:");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08002090 pw.println();
ludi89194d62017-05-22 10:52:23 -07002091
Benedict Wong6855aee2017-11-16 15:27:22 -08002092 pw.println("mUserResourceTracker:");
2093 pw.println(mUserResourceTracker);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08002094 }
2095}