blob: 1a51e91c19ef12f517e8c3590612cdf5f0b40e8b [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;
Benedict Wong8bc90732018-01-18 18:31:45 -080045import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold80865392017-04-04 19:37:48 -070046import android.net.IpSecUdpEncapResponse;
Benedict Wong97c3c942018-03-01 18:53:07 -080047import android.net.LinkAddress;
Yan Yana2f3b492020-09-29 23:38:00 -070048import android.net.LinkProperties;
Benedict Wong8bc90732018-01-18 18:31:45 -080049import android.net.Network;
Benedict Wong083faee2017-12-03 19:42:36 -080050import android.net.TrafficStats;
Nathan Harold031acb82017-03-07 13:23:36 -080051import android.os.Binder;
Nathan Harold031acb82017-03-07 13:23:36 -080052import android.os.IBinder;
53import android.os.ParcelFileDescriptor;
Benedict Wong908d34e2021-04-15 11:59:16 -070054import android.os.Process;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080055import android.os.RemoteException;
Nathan Harold031acb82017-03-07 13:23:36 -080056import android.os.ServiceSpecificException;
Nathan Harold80865392017-04-04 19:37:48 -070057import android.system.ErrnoException;
58import android.system.Os;
59import android.system.OsConstants;
Nathan Harold19b99d92017-08-23 13:46:33 -070060import android.text.TextUtils;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080061import android.util.Log;
lucaslin7eb76592021-03-11 17:39:49 +080062import android.util.Range;
Nathan Harold031acb82017-03-07 13:23:36 -080063import android.util.SparseArray;
Benedict Wong8bc90732018-01-18 18:31:45 -080064import android.util.SparseBooleanArray;
Nathan Harold19b99d92017-08-23 13:46:33 -070065
Nathan Harold031acb82017-03-07 13:23:36 -080066import com.android.internal.annotations.GuardedBy;
ludi5e623ea2017-05-12 09:15:00 -070067import com.android.internal.annotations.VisibleForTesting;
Benedict Wong70867e52017-11-06 20:49:10 -080068import com.android.internal.util.Preconditions;
Lorenzo Colittib38fef82023-02-06 15:22:01 +090069import com.android.modules.utils.build.SdkLevel;
paulhu00e34562021-10-26 09:00:50 +000070import com.android.net.module.util.BinderUtils;
lucaslinff6fe7b2021-02-03 23:59:45 +080071import com.android.net.module.util.NetdUtils;
Benedict Wong908d34e2021-04-15 11:59:16 -070072import com.android.net.module.util.PermissionUtils;
Nathan Harold19b99d92017-08-23 13:46:33 -070073
Benedict Wong683441d2018-07-25 18:46:19 -070074import libcore.io.IoUtils;
75
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080076import java.io.FileDescriptor;
Nathan Harold80865392017-04-04 19:37:48 -070077import java.io.IOException;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080078import java.io.PrintWriter;
evitayan43d93a02018-03-22 17:53:08 -070079import java.net.Inet4Address;
80import java.net.Inet6Address;
Nathan Harold80865392017-04-04 19:37:48 -070081import java.net.InetAddress;
82import java.net.InetSocketAddress;
83import java.net.UnknownHostException;
Benedict Wong02346822017-10-26 19:41:43 -070084import java.util.ArrayList;
85import java.util.List;
Benedict Wong529e8aa2020-02-11 23:49:36 -080086import java.util.Objects;
Nathan Harold19b99d92017-08-23 13:46:33 -070087
Benedict Wong02346822017-10-26 19:41:43 -070088/**
89 * A service to manage multiple clients that want to access the IpSec API. The service is
90 * responsible for maintaining a list of clients and managing the resources (and related quotas)
91 * that each of them own.
92 *
93 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
94 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
95 * thread is ever running at a time.
96 *
97 * @hide
98 */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -080099public class IpSecService extends IIpSecService.Stub {
100 private static final String TAG = "IpSecService";
101 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Benedict Wong38e52972018-05-07 20:06:44 -0700102 private static final int[] ADDRESS_FAMILIES =
103 new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800104
ludi5e623ea2017-05-12 09:15:00 -0700105 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold80865392017-04-04 19:37:48 -0700106 private static final InetAddress INADDR_ANY;
Lorenzo Colittib38fef82023-02-06 15:22:01 +0900107 private static final InetAddress IN6ADDR_ANY;
Nathan Harold80865392017-04-04 19:37:48 -0700108
Benedict Wong29c30772019-03-20 09:44:09 -0700109 @VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
110
Aaron Huang2617cf52021-11-29 16:31:32 +0800111 private final INetd mNetd;
112
Nathan Harold80865392017-04-04 19:37:48 -0700113 static {
114 try {
115 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
Lorenzo Colittib38fef82023-02-06 15:22:01 +0900116 IN6ADDR_ANY = InetAddress.getByAddress(
117 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 -0700118 } catch (UnknownHostException e) {
119 throw new RuntimeException(e);
120 }
121 }
122
123 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
124 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
125
126 /* Binder context for this service */
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800127 private final Context mContext;
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800128 private final Dependencies mDeps;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800129
Nathan Haroldfdafce22017-12-13 19:16:33 -0800130 /**
Nathan Harold5a19b952018-01-05 19:25:13 -0800131 * The next non-repeating global ID for tracking resources between users, this service, and
132 * kernel data structures. Accessing this variable is not thread safe, so it is only read or
133 * modified within blocks synchronized on IpSecService.this. We want to avoid -1
134 * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
Nathan Haroldfdafce22017-12-13 19:16:33 -0800135 */
136 @GuardedBy("IpSecService.this")
137 private int mNextResourceId = 1;
Nathan Haroldd2a1dad2017-03-01 18:55:06 -0800138
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800139 /**
140 * Dependencies of IpSecService, for injection in tests.
141 */
142 @VisibleForTesting
143 public static class Dependencies {
144 /**
145 * Get a reference to INetd.
146 */
147 public INetd getNetdInstance(Context context) throws RemoteException {
148 final INetd netd = INetd.Stub.asInterface((IBinder)
149 context.getSystemService(Context.NETD_SERVICE));
150 if (netd == null) {
151 throw new RemoteException("Failed to Get Netd Instance");
152 }
153 return netd;
154 }
ludi5e623ea2017-05-12 09:15:00 -0700155 }
156
Benedict Wong083faee2017-12-03 19:42:36 -0800157 final UidFdTagger mUidFdTagger;
ludi5e623ea2017-05-12 09:15:00 -0700158
Benedict Wong02346822017-10-26 19:41:43 -0700159 /**
160 * Interface for user-reference and kernel-resource cleanup.
161 *
162 * <p>This interface must be implemented for a resource to be reference counted.
163 */
164 @VisibleForTesting
165 public interface IResource {
166 /**
167 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
168 * objects dependent on it.
169 *
170 * <p>Implementations of this method are expected to remove references to the IResource
171 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
172 * the resource is considered invalid for user access or allocation or use in other
173 * resources.
174 *
175 * <p>References to the IResource object may be held by other RefcountedResource objects,
Benedict Wongcbd329b2017-12-13 17:16:53 -0800176 * and as such, the underlying resources and quota may not be cleaned up.
Benedict Wong02346822017-10-26 19:41:43 -0700177 */
178 void invalidate() throws RemoteException;
179
180 /**
181 * Releases underlying resources and related quotas.
182 *
183 * <p>Implementations of this method are expected to remove all system resources that are
184 * tracked by the IResource object. Due to other RefcountedResource objects potentially
Benedict Wong6855aee2017-11-16 15:27:22 -0800185 * having references to the IResource object, freeUnderlyingResources may not always be
Benedict Wong02346822017-10-26 19:41:43 -0700186 * called from releaseIfUnreferencedRecursively().
187 */
188 void freeUnderlyingResources() throws RemoteException;
189 }
190
191 /**
192 * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
193 *
194 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
195 * RefcountedResource object creates an explicit reference that must be freed by calling
196 * userRelease(). Additionally, adding this object as a child of another RefcountedResource
197 * object will add an implicit reference.
198 *
199 * <p>Resources are cleaned up when all references, both implicit and explicit, are released
200 * (ie, when userRelease() is called and when all parents have called releaseReference() on this
201 * object.)
202 */
203 @VisibleForTesting
204 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000205 private final T mResource;
206 private final List<RefcountedResource> mChildren;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800207 int mRefCount = 1; // starts at 1 for user's reference.
Junyu Lai75eabfe2019-04-26 01:38:04 +0000208 IBinder mBinder;
Benedict Wong4aac3e92019-02-25 12:33:22 -0800209
Junyu Lai75eabfe2019-04-26 01:38:04 +0000210 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
Benedict Wong02346822017-10-26 19:41:43 -0700211 synchronized (IpSecService.this) {
212 this.mResource = resource;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000213 this.mChildren = new ArrayList<>(children.length);
Benedict Wong02346822017-10-26 19:41:43 -0700214 this.mBinder = binder;
Junyu Lai75eabfe2019-04-26 01:38:04 +0000215
Benedict Wong02346822017-10-26 19:41:43 -0700216 for (RefcountedResource child : children) {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000217 mChildren.add(child);
Benedict Wong02346822017-10-26 19:41:43 -0700218 child.mRefCount++;
219 }
220
221 try {
Junyu Lai75eabfe2019-04-26 01:38:04 +0000222 mBinder.linkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700223 } catch (RemoteException e) {
224 binderDied();
Benedict Wong04738f52019-02-28 20:28:48 -0800225 e.rethrowFromSystemServer();
Benedict Wong02346822017-10-26 19:41:43 -0700226 }
227 }
228 }
229
230 /**
231 * If the Binder object dies, this function is called to free the system resources that are
Benedict Wong6855aee2017-11-16 15:27:22 -0800232 * being tracked by this record and to subsequently release this record for garbage
Benedict Wong02346822017-10-26 19:41:43 -0700233 * collection
234 */
235 @Override
236 public void binderDied() {
237 synchronized (IpSecService.this) {
238 try {
239 userRelease();
240 } catch (Exception e) {
241 Log.e(TAG, "Failed to release resource: " + e);
242 }
243 }
244 }
245
246 public T getResource() {
247 return mResource;
248 }
249
250 /**
251 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
252 * arrays)
253 *
254 * <p>If this method has been previously called, the RefcountedResource's binder field will
255 * be null, and the method will return without performing the cleanup a second time.
256 *
257 * <p>Note that calling this function does not imply that kernel resources will be freed at
258 * this time, or that the related quota will be returned. Such actions will only be
259 * performed upon the reference count reaching zero.
260 */
261 @GuardedBy("IpSecService.this")
262 public void userRelease() throws RemoteException {
263 // Prevent users from putting reference counts into a bad state by calling
264 // userRelease() multiple times.
265 if (mBinder == null) {
266 return;
267 }
268
Junyu Lai75eabfe2019-04-26 01:38:04 +0000269 mBinder.unlinkToDeath(this, 0);
Benedict Wong02346822017-10-26 19:41:43 -0700270 mBinder = null;
271
272 mResource.invalidate();
Junyu Lai75eabfe2019-04-26 01:38:04 +0000273
Benedict Wong02346822017-10-26 19:41:43 -0700274 releaseReference();
275 }
276
277 /**
278 * Removes a reference to this resource. If the resultant reference count is zero, the
279 * underlying resources are freed, and references to all child resources are also dropped
280 * recursively (resulting in them freeing their resources and children, etcetera)
281 *
282 * <p>This method also sets the reference count to an invalid value (-1) to signify that it
283 * has been fully released. Any subsequent calls to this method will result in an
284 * IllegalStateException being thrown due to resource already having been previously
285 * released
286 */
287 @VisibleForTesting
288 @GuardedBy("IpSecService.this")
289 public void releaseReference() throws RemoteException {
290 mRefCount--;
291
292 if (mRefCount > 0) {
293 return;
294 } else if (mRefCount < 0) {
295 throw new IllegalStateException(
296 "Invalid operation - resource has already been released.");
297 }
298
299 // Cleanup own resources
300 mResource.freeUnderlyingResources();
301
302 // Cleanup child resources as needed
303 for (RefcountedResource<? extends IResource> child : mChildren) {
304 child.releaseReference();
305 }
306
307 // Enforce that resource cleanup can only be called once
308 // By decrementing the refcount (from 0 to -1), the next call will throw an
309 // IllegalStateException - it has already been released fully.
310 mRefCount--;
311 }
312
313 @Override
314 public String toString() {
315 return new StringBuilder()
316 .append("{mResource=")
317 .append(mResource)
318 .append(", mRefCount=")
319 .append(mRefCount)
320 .append(", mChildren=")
321 .append(mChildren)
322 .append("}")
323 .toString();
324 }
325 }
326
Benedict Wongcbd329b2017-12-13 17:16:53 -0800327 /**
328 * Very simple counting class that looks much like a counting semaphore
329 *
330 * <p>This class is not thread-safe, and expects that that users of this class will ensure
331 * synchronization and thread safety by holding the IpSecService.this instance lock.
332 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800333 @VisibleForTesting
334 static class ResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700335 private final int mMax;
336 int mCurrent;
337
338 ResourceTracker(int max) {
339 mMax = max;
340 mCurrent = 0;
341 }
342
Benedict Wong6855aee2017-11-16 15:27:22 -0800343 boolean isAvailable() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700344 return (mCurrent < mMax);
345 }
346
Benedict Wong6855aee2017-11-16 15:27:22 -0800347 void take() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700348 if (!isAvailable()) {
349 Log.wtf(TAG, "Too many resources allocated!");
350 }
351 mCurrent++;
352 }
353
Benedict Wong6855aee2017-11-16 15:27:22 -0800354 void give() {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700355 if (mCurrent <= 0) {
356 Log.wtf(TAG, "We've released this resource too many times");
357 }
358 mCurrent--;
359 }
ludi529fdec2017-08-10 15:44:40 -0700360
361 @Override
362 public String toString() {
363 return new StringBuilder()
364 .append("{mCurrent=")
365 .append(mCurrent)
366 .append(", mMax=")
367 .append(mMax)
368 .append("}")
369 .toString();
370 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700371 }
372
Benedict Wong6855aee2017-11-16 15:27:22 -0800373 @VisibleForTesting
374 static final class UserRecord {
Benedict Wong6855aee2017-11-16 15:27:22 -0800375 /* Maximum number of each type of resource that a single UID may possess */
Benedict Wong7bcf9c22020-02-11 23:36:42 -0800376
377 // Up to 4 active VPNs/IWLAN with potential soft handover.
378 public static final int MAX_NUM_TUNNEL_INTERFACES = 8;
379 public static final int MAX_NUM_ENCAP_SOCKETS = 16;
380
381 // SPIs and Transforms are both cheap, and are 1:1 correlated.
382 public static final int MAX_NUM_TRANSFORMS = 64;
383 public static final int MAX_NUM_SPIS = 64;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700384
Benedict Wongcbd329b2017-12-13 17:16:53 -0800385 /**
386 * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
387 * and explicit (user) reference management.
388 *
389 * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
390 *
391 * <p>Resources are removed from this array when the user releases their explicit reference
392 * by calling one of the releaseResource() methods.
393 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800394 final RefcountedResourceArray<SpiRecord> mSpiRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800395 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800396 final RefcountedResourceArray<TransformRecord> mTransformRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800397 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
Benedict Wong6855aee2017-11-16 15:27:22 -0800398 final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
Benedict Wongcbd329b2017-12-13 17:16:53 -0800399 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
Benedict Wong8bc90732018-01-18 18:31:45 -0800400 final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
401 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
Benedict Wongcbd329b2017-12-13 17:16:53 -0800402
403 /**
404 * Trackers for quotas for each of the OwnedResource types.
405 *
406 * <p>These trackers are separate from the resource arrays, since they are incremented and
407 * decremented at different points in time. Specifically, quota is only returned upon final
408 * resource deallocation (after all explicit and implicit references are released). Note
409 * that it is possible that calls to releaseResource() will not return the used quota if
410 * there are other resources that depend on (are parents of) the resource being released.
411 */
412 final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
413 final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
Benedict Wong6855aee2017-11-16 15:27:22 -0800414 final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
Benedict Wong8bc90732018-01-18 18:31:45 -0800415 final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
Benedict Wong6855aee2017-11-16 15:27:22 -0800416
417 void removeSpiRecord(int resourceId) {
418 mSpiRecords.remove(resourceId);
Nathan Harold6e4681c2017-04-24 16:16:34 -0700419 }
420
Benedict Wong6855aee2017-11-16 15:27:22 -0800421 void removeTransformRecord(int resourceId) {
422 mTransformRecords.remove(resourceId);
423 }
424
Benedict Wong8bc90732018-01-18 18:31:45 -0800425 void removeTunnelInterfaceRecord(int resourceId) {
426 mTunnelInterfaceRecords.remove(resourceId);
427 }
428
Benedict Wong6855aee2017-11-16 15:27:22 -0800429 void removeEncapSocketRecord(int resourceId) {
430 mEncapSocketRecords.remove(resourceId);
431 }
432
433 @Override
434 public String toString() {
435 return new StringBuilder()
436 .append("{mSpiQuotaTracker=")
437 .append(mSpiQuotaTracker)
438 .append(", mTransformQuotaTracker=")
439 .append(mTransformQuotaTracker)
440 .append(", mSocketQuotaTracker=")
441 .append(mSocketQuotaTracker)
Benedict Wong76603702018-01-24 15:31:39 -0800442 .append(", mTunnelQuotaTracker=")
443 .append(mTunnelQuotaTracker)
Benedict Wong6855aee2017-11-16 15:27:22 -0800444 .append(", mSpiRecords=")
445 .append(mSpiRecords)
446 .append(", mTransformRecords=")
447 .append(mTransformRecords)
448 .append(", mEncapSocketRecords=")
449 .append(mEncapSocketRecords)
Benedict Wong76603702018-01-24 15:31:39 -0800450 .append(", mTunnelInterfaceRecords=")
451 .append(mTunnelInterfaceRecords)
Benedict Wong6855aee2017-11-16 15:27:22 -0800452 .append("}")
453 .toString();
454 }
455 }
456
Benedict Wongcbd329b2017-12-13 17:16:53 -0800457 /**
458 * This class is not thread-safe, and expects that that users of this class will ensure
459 * synchronization and thread safety by holding the IpSecService.this instance lock.
460 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800461 @VisibleForTesting
462 static final class UserResourceTracker {
Nathan Harold6e4681c2017-04-24 16:16:34 -0700463 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
464
Benedict Wongcbd329b2017-12-13 17:16:53 -0800465 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
Benedict Wong6855aee2017-11-16 15:27:22 -0800466 public UserRecord getUserRecord(int uid) {
467 checkCallerUid(uid);
468
Nathan Harold6e4681c2017-04-24 16:16:34 -0700469 UserRecord r = mUserRecords.get(uid);
470 if (r == null) {
471 r = new UserRecord();
472 mUserRecords.put(uid, r);
473 }
474 return r;
475 }
ludi529fdec2017-08-10 15:44:40 -0700476
Benedict Wong6855aee2017-11-16 15:27:22 -0800477 /** Safety method; guards against access of other user's UserRecords */
478 private void checkCallerUid(int uid) {
Benedict Wong908d34e2021-04-15 11:59:16 -0700479 if (uid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800480 throw new SecurityException("Attempted access of unowned resources");
481 }
482 }
483
ludi529fdec2017-08-10 15:44:40 -0700484 @Override
485 public String toString() {
486 return mUserRecords.toString();
487 }
Nathan Harold6e4681c2017-04-24 16:16:34 -0700488 }
489
Benedict Wong6855aee2017-11-16 15:27:22 -0800490 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700491
Nathan Harold80865392017-04-04 19:37:48 -0700492 /**
Benedict Wongcbd329b2017-12-13 17:16:53 -0800493 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
Benedict Wong6855aee2017-11-16 15:27:22 -0800494 * resources. It relies on a provided resourceId that should uniquely identify the kernel
495 * resource. To use this class, the user should implement the invalidate() and
496 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
Benedict Wongcbd329b2017-12-13 17:16:53 -0800497 * tracking arrays and kernel resources, respectively.
498 *
499 * <p>This class associates kernel resources with the UID that owns and controls them.
Nathan Harold80865392017-04-04 19:37:48 -0700500 */
Benedict Wongcbd329b2017-12-13 17:16:53 -0800501 private abstract class OwnedResourceRecord implements IResource {
Aaron Huangfbae3082021-12-06 15:18:42 +0800502 final int mPid;
503 final int mUid;
Benedict Wong6855aee2017-11-16 15:27:22 -0800504 protected final int mResourceId;
Nathan Harold031acb82017-03-07 13:23:36 -0800505
Benedict Wongcbd329b2017-12-13 17:16:53 -0800506 OwnedResourceRecord(int resourceId) {
Nathan Harold031acb82017-03-07 13:23:36 -0800507 super();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700508 if (resourceId == INVALID_RESOURCE_ID) {
509 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
510 }
Nathan Harold80865392017-04-04 19:37:48 -0700511 mResourceId = resourceId;
Aaron Huangfbae3082021-12-06 15:18:42 +0800512 mPid = Binder.getCallingPid();
513 mUid = Binder.getCallingUid();
Nathan Harold031acb82017-03-07 13:23:36 -0800514
Nathan Harold6e4681c2017-04-24 16:16:34 -0700515 getResourceTracker().take();
Nathan Harold031acb82017-03-07 13:23:36 -0800516 }
517
Benedict Wong6855aee2017-11-16 15:27:22 -0800518 @Override
519 public abstract void invalidate() throws RemoteException;
520
521 /** Convenience method; retrieves the user resource record for the stored UID. */
522 protected UserRecord getUserRecord() {
Aaron Huangfbae3082021-12-06 15:18:42 +0800523 return mUserResourceTracker.getUserRecord(mUid);
Nathan Harold80865392017-04-04 19:37:48 -0700524 }
525
Benedict Wong6855aee2017-11-16 15:27:22 -0800526 @Override
527 public abstract void freeUnderlyingResources() throws RemoteException;
ludi89194d62017-05-22 10:52:23 -0700528
Nathan Harold6e4681c2017-04-24 16:16:34 -0700529 /** Get the resource tracker for this resource */
530 protected abstract ResourceTracker getResourceTracker();
531
ludi89194d62017-05-22 10:52:23 -0700532 @Override
533 public String toString() {
534 return new StringBuilder()
535 .append("{mResourceId=")
536 .append(mResourceId)
537 .append(", pid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800538 .append(mPid)
ludi89194d62017-05-22 10:52:23 -0700539 .append(", uid=")
Aaron Huangfbae3082021-12-06 15:18:42 +0800540 .append(mUid)
ludi89194d62017-05-22 10:52:23 -0700541 .append("}")
542 .toString();
543 }
Nathan Harold031acb82017-03-07 13:23:36 -0800544 };
545
Nathan Harold80865392017-04-04 19:37:48 -0700546 /**
Benedict Wong6855aee2017-11-16 15:27:22 -0800547 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
548 *
549 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
550 * if a key is not found during a retrieval process.
Nathan Harold80865392017-04-04 19:37:48 -0700551 */
Benedict Wong6855aee2017-11-16 15:27:22 -0800552 static class RefcountedResourceArray<T extends IResource> {
553 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
554 private final String mTypeName;
Nathan Harold031acb82017-03-07 13:23:36 -0800555
Aaron Huangfbae3082021-12-06 15:18:42 +0800556 RefcountedResourceArray(String typeName) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800557 this.mTypeName = typeName;
Nathan Harold80865392017-04-04 19:37:48 -0700558 }
559
Benedict Wong6855aee2017-11-16 15:27:22 -0800560 /**
561 * Accessor method to get inner resource object.
562 *
563 * @throws IllegalArgumentException if no resource with provided key is found.
564 */
565 T getResourceOrThrow(int key) {
566 return getRefcountedResourceOrThrow(key).getResource();
567 }
568
569 /**
570 * Accessor method to get reference counting wrapper.
571 *
572 * @throws IllegalArgumentException if no resource with provided key is found.
573 */
574 RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
575 RefcountedResource<T> resource = mArray.get(key);
576 if (resource == null) {
577 throw new IllegalArgumentException(
578 String.format("No such %s found for given id: %d", mTypeName, key));
579 }
580
581 return resource;
582 }
583
584 void put(int key, RefcountedResource<T> obj) {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +0000585 Objects.requireNonNull(obj, "Null resources cannot be added");
Nathan Harold80865392017-04-04 19:37:48 -0700586 mArray.put(key, obj);
587 }
588
589 void remove(int key) {
590 mArray.remove(key);
591 }
ludi89194d62017-05-22 10:52:23 -0700592
593 @Override
594 public String toString() {
595 return mArray.toString();
596 }
Nathan Harold80865392017-04-04 19:37:48 -0700597 }
598
Benedict Wongcbd329b2017-12-13 17:16:53 -0800599 /**
Yan Yane114b382021-02-16 16:29:48 -0800600 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is created, the
601 * SpiRecord that originally tracked the SAs will reliquish the responsibility of freeing the
602 * underlying SA to this class via the mOwnedByTransform flag.
603 *
604 * <p>This class is not thread-safe, and expects that that users of this class will ensure
605 * synchronization and thread safety by holding the IpSecService.this instance lock
Benedict Wongcbd329b2017-12-13 17:16:53 -0800606 */
607 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -0700608 private final IpSecConfig mConfig;
Nathan Harold5a19b952018-01-05 19:25:13 -0800609 private final SpiRecord mSpi;
Benedict Wong6855aee2017-11-16 15:27:22 -0800610 private final EncapSocketRecord mSocket;
Yan Yane114b382021-02-16 16:29:48 -0800611 private String mNewSourceAddress = null;
612 private String mNewDestinationAddress = null;
Nathan Harold80865392017-04-04 19:37:48 -0700613
614 TransformRecord(
Nathan Harold5a19b952018-01-05 19:25:13 -0800615 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800616 super(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -0800617 mConfig = config;
Nathan Harold5a19b952018-01-05 19:25:13 -0800618 mSpi = spi;
Nathan Harold80865392017-04-04 19:37:48 -0700619 mSocket = socket;
Benedict Wong68aac2a2017-12-13 18:26:40 -0800620
621 spi.setOwnedByTransform();
Nathan Harold031acb82017-03-07 13:23:36 -0800622 }
623
624 public IpSecConfig getConfig() {
625 return mConfig;
626 }
627
Nathan Harold5a19b952018-01-05 19:25:13 -0800628 public SpiRecord getSpiRecord() {
629 return mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700630 }
631
Benedict Wong8edc5572018-01-19 17:36:02 -0800632 public EncapSocketRecord getSocketRecord() {
633 return mSocket;
634 }
635
Yan Yane114b382021-02-16 16:29:48 -0800636 @GuardedBy("IpSecService.this")
637 public String getNewSourceAddress() {
638 return mNewSourceAddress;
639 }
640
641 @GuardedBy("IpSecService.this")
642 public String getNewDestinationAddress() {
643 return mNewDestinationAddress;
644 }
645
646 private void verifyTunnelModeOrThrow() {
647 if (mConfig.getMode() != IpSecTransform.MODE_TUNNEL) {
648 throw new UnsupportedOperationException(
649 "Migration requested/called on non-tunnel-mode transform");
650 }
651 }
652
653 /** Start migrating this transform to new source and destination addresses */
654 @GuardedBy("IpSecService.this")
655 public void startMigration(String newSourceAddress, String newDestinationAddress) {
656 verifyTunnelModeOrThrow();
657 Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
658 Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
659 mNewSourceAddress = newSourceAddress;
660 mNewDestinationAddress = newDestinationAddress;
661 }
662
663 /** Finish migration and update addresses. */
664 @GuardedBy("IpSecService.this")
665 public void finishMigration() {
666 verifyTunnelModeOrThrow();
667 mConfig.setSourceAddress(mNewSourceAddress);
668 mConfig.setDestinationAddress(mNewDestinationAddress);
669 mNewSourceAddress = null;
670 mNewDestinationAddress = null;
671 }
672
673 /** Return if this transform is going to be migrated. */
674 @GuardedBy("IpSecService.this")
675 public boolean isMigrating() {
676 verifyTunnelModeOrThrow();
677
678 return mNewSourceAddress != null;
679 }
680
Nathan Harold80865392017-04-04 19:37:48 -0700681 /** always guarded by IpSecService#this */
Nathan Harold031acb82017-03-07 13:23:36 -0800682 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800683 public void freeUnderlyingResources() {
Nathan Harold5a19b952018-01-05 19:25:13 -0800684 int spi = mSpi.getSpi();
685 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800686 mNetd.ipSecDeleteSecurityAssociation(
687 mUid,
688 mConfig.getSourceAddress(),
689 mConfig.getDestinationAddress(),
690 spi,
691 mConfig.getMarkValue(),
692 mConfig.getMarkMask(),
693 mConfig.getXfrmInterfaceId());
Benedict Wong97c3c942018-03-01 18:53:07 -0800694 } catch (RemoteException | ServiceSpecificException e) {
695 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800696 }
Nathan Harold031acb82017-03-07 13:23:36 -0800697
Benedict Wong6855aee2017-11-16 15:27:22 -0800698 getResourceTracker().give();
Nathan Harold031acb82017-03-07 13:23:36 -0800699 }
ludi89194d62017-05-22 10:52:23 -0700700
Benedict Wong6855aee2017-11-16 15:27:22 -0800701 @Override
702 public void invalidate() throws RemoteException {
703 getUserRecord().removeTransformRecord(mResourceId);
704 }
705
706 @Override
Nathan Harold6e4681c2017-04-24 16:16:34 -0700707 protected ResourceTracker getResourceTracker() {
Benedict Wong6855aee2017-11-16 15:27:22 -0800708 return getUserRecord().mTransformQuotaTracker;
Nathan Harold6e4681c2017-04-24 16:16:34 -0700709 }
710
ludi89194d62017-05-22 10:52:23 -0700711 @Override
712 public String toString() {
713 StringBuilder strBuilder = new StringBuilder();
714 strBuilder
715 .append("{super=")
716 .append(super.toString())
717 .append(", mSocket=")
718 .append(mSocket)
Nathan Harold5a19b952018-01-05 19:25:13 -0800719 .append(", mSpi.mResourceId=")
720 .append(mSpi.mResourceId)
ludi89194d62017-05-22 10:52:23 -0700721 .append(", mConfig=")
722 .append(mConfig)
723 .append("}");
724 return strBuilder.toString();
725 }
Nathan Harold031acb82017-03-07 13:23:36 -0800726 }
727
Benedict Wongcbd329b2017-12-13 17:16:53 -0800728 /**
729 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
730 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
731 * object
732 */
733 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harold5a19b952018-01-05 19:25:13 -0800734 private final String mSourceAddress;
735 private final String mDestinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800736 private int mSpi;
Nathan Harold80865392017-04-04 19:37:48 -0700737
738 private boolean mOwnedByTransform = false;
Nathan Harold031acb82017-03-07 13:23:36 -0800739
Aaron Huang2617cf52021-11-29 16:31:32 +0800740 SpiRecord(int resourceId, String sourceAddress,
Aaron Huang9b27b0e2021-11-27 00:30:35 +0800741 String destinationAddress, int spi) {
Benedict Wong6855aee2017-11-16 15:27:22 -0800742 super(resourceId);
Nathan Harold5a19b952018-01-05 19:25:13 -0800743 mSourceAddress = sourceAddress;
744 mDestinationAddress = destinationAddress;
Nathan Harold031acb82017-03-07 13:23:36 -0800745 mSpi = spi;
Nathan Harold031acb82017-03-07 13:23:36 -0800746 }
747
Nathan Harold80865392017-04-04 19:37:48 -0700748 /** always guarded by IpSecService#this */
749 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800750 public void freeUnderlyingResources() {
Nathan Harold031acb82017-03-07 13:23:36 -0800751 try {
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800752 if (!mOwnedByTransform) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800753 mNetd.ipSecDeleteSecurityAssociation(
754 mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
755 0 /* mask */, 0 /* if_id */);
Nathan Haroldfdde4d62018-02-27 19:19:40 -0800756 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800757 } catch (ServiceSpecificException | RemoteException e) {
758 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
Nathan Harold031acb82017-03-07 13:23:36 -0800759 }
Nathan Harold80865392017-04-04 19:37:48 -0700760
761 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -0800762
Benedict Wong6855aee2017-11-16 15:27:22 -0800763 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -0700764 }
765
Nathan Harold80865392017-04-04 19:37:48 -0700766 public int getSpi() {
767 return mSpi;
768 }
769
Nathan Harold5a19b952018-01-05 19:25:13 -0800770 public String getDestinationAddress() {
771 return mDestinationAddress;
772 }
773
Nathan Harold80865392017-04-04 19:37:48 -0700774 public void setOwnedByTransform() {
775 if (mOwnedByTransform) {
776 // Programming error
Andreas Gampeafb01e22017-07-11 10:25:09 -0700777 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold80865392017-04-04 19:37:48 -0700778 }
779
780 mOwnedByTransform = true;
Nathan Harold031acb82017-03-07 13:23:36 -0800781 }
ludi89194d62017-05-22 10:52:23 -0700782
Benedict Wong68aac2a2017-12-13 18:26:40 -0800783 public boolean getOwnedByTransform() {
784 return mOwnedByTransform;
785 }
786
ludi89194d62017-05-22 10:52:23 -0700787 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -0800788 public void invalidate() throws RemoteException {
789 getUserRecord().removeSpiRecord(mResourceId);
790 }
791
792 @Override
793 protected ResourceTracker getResourceTracker() {
794 return getUserRecord().mSpiQuotaTracker;
795 }
796
797 @Override
ludi89194d62017-05-22 10:52:23 -0700798 public String toString() {
799 StringBuilder strBuilder = new StringBuilder();
800 strBuilder
801 .append("{super=")
802 .append(super.toString())
803 .append(", mSpi=")
804 .append(mSpi)
Nathan Harold5a19b952018-01-05 19:25:13 -0800805 .append(", mSourceAddress=")
806 .append(mSourceAddress)
807 .append(", mDestinationAddress=")
808 .append(mDestinationAddress)
ludi89194d62017-05-22 10:52:23 -0700809 .append(", mOwnedByTransform=")
810 .append(mOwnedByTransform)
811 .append("}");
812 return strBuilder.toString();
813 }
Nathan Harold031acb82017-03-07 13:23:36 -0800814 }
815
Benedict Wong8bc90732018-01-18 18:31:45 -0800816 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
lucaslin7eb76592021-03-11 17:39:49 +0800817 final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
818 private int mNextTunnelNetId = mNetIdRange.getLower();
Benedict Wong8bc90732018-01-18 18:31:45 -0800819
820 /**
821 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
822 *
823 * <p>This method should only be called from Binder threads. Do not call this from within the
824 * system server as it will crash the system on failure.
825 *
826 * @return an integer key within the netId range, if successful
827 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
828 */
829 @VisibleForTesting
830 int reserveNetId() {
lucaslin7eb76592021-03-11 17:39:49 +0800831 final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
Benedict Wong8bc90732018-01-18 18:31:45 -0800832 synchronized (mTunnelNetIds) {
lucaslin7eb76592021-03-11 17:39:49 +0800833 for (int i = 0; i < range; i++) {
834 final int netId = mNextTunnelNetId;
835 if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
836 mNextTunnelNetId = mNetIdRange.getLower();
837 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800838 if (!mTunnelNetIds.get(netId)) {
839 mTunnelNetIds.put(netId, true);
840 return netId;
841 }
842 }
843 }
844 throw new IllegalStateException("No free netIds to allocate");
845 }
846
847 @VisibleForTesting
848 void releaseNetId(int netId) {
849 synchronized (mTunnelNetIds) {
850 mTunnelNetIds.delete(netId);
851 }
852 }
853
Yan Yana2f3b492020-09-29 23:38:00 -0700854 /**
855 * Tracks an tunnel interface, and manages cleanup paths.
856 *
857 * <p>This class is not thread-safe, and expects that that users of this class will ensure
858 * synchronization and thread safety by holding the IpSecService.this instance lock
859 */
860 @VisibleForTesting
861 final class TunnelInterfaceRecord extends OwnedResourceRecord {
Benedict Wong8bc90732018-01-18 18:31:45 -0800862 private final String mInterfaceName;
Benedict Wong8bc90732018-01-18 18:31:45 -0800863
864 // outer addresses
865 private final String mLocalAddress;
866 private final String mRemoteAddress;
867
868 private final int mIkey;
869 private final int mOkey;
870
Benedict Wong5d749842018-09-06 11:31:25 -0700871 private final int mIfId;
872
Yan Yana2f3b492020-09-29 23:38:00 -0700873 private Network mUnderlyingNetwork;
874
Benedict Wong8bc90732018-01-18 18:31:45 -0800875 TunnelInterfaceRecord(
876 int resourceId,
877 String interfaceName,
878 Network underlyingNetwork,
879 String localAddr,
880 String remoteAddr,
881 int ikey,
Benedict Wong5d749842018-09-06 11:31:25 -0700882 int okey,
883 int intfId) {
Benedict Wong8bc90732018-01-18 18:31:45 -0800884 super(resourceId);
885
886 mInterfaceName = interfaceName;
887 mUnderlyingNetwork = underlyingNetwork;
888 mLocalAddress = localAddr;
889 mRemoteAddress = remoteAddr;
890 mIkey = ikey;
891 mOkey = okey;
Benedict Wong5d749842018-09-06 11:31:25 -0700892 mIfId = intfId;
Benedict Wong8bc90732018-01-18 18:31:45 -0800893 }
894
895 /** always guarded by IpSecService#this */
896 @Override
897 public void freeUnderlyingResources() {
Benedict Wong8edc5572018-01-19 17:36:02 -0800898 // Calls to netd
Benedict Wong8bc90732018-01-18 18:31:45 -0800899 // Teardown VTI
900 // Delete global policies
Benedict Wong8edc5572018-01-19 17:36:02 -0800901 try {
Aaron Huang2617cf52021-11-29 16:31:32 +0800902 mNetd.ipSecRemoveTunnelInterface(mInterfaceName);
Benedict Wong8edc5572018-01-19 17:36:02 -0800903
Benedict Wong38e52972018-05-07 20:06:44 -0700904 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +0800905 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800906 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700907 selAddrFamily,
908 IpSecManager.DIRECTION_OUT,
909 mOkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700910 0xffffffff,
911 mIfId);
Aaron Huang2617cf52021-11-29 16:31:32 +0800912 mNetd.ipSecDeleteSecurityPolicy(
Aaron Huangfbae3082021-12-06 15:18:42 +0800913 mUid,
Benedict Wong38e52972018-05-07 20:06:44 -0700914 selAddrFamily,
915 IpSecManager.DIRECTION_IN,
916 mIkey,
Benedict Wong5d749842018-09-06 11:31:25 -0700917 0xffffffff,
918 mIfId);
Yan Yanf4eceba2022-10-31 20:41:13 +0000919 mNetd.ipSecDeleteSecurityPolicy(
920 mUid,
921 selAddrFamily,
922 IpSecManager.DIRECTION_FWD,
923 mIkey,
924 0xffffffff,
925 mIfId);
Benedict Wong8edc5572018-01-19 17:36:02 -0800926 }
Benedict Wong97c3c942018-03-01 18:53:07 -0800927 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong8edc5572018-01-19 17:36:02 -0800928 Log.e(
929 TAG,
930 "Failed to delete VTI with interface name: "
931 + mInterfaceName
932 + " and id: "
Benedict Wong97c3c942018-03-01 18:53:07 -0800933 + mResourceId, e);
Benedict Wong8edc5572018-01-19 17:36:02 -0800934 }
Benedict Wong8bc90732018-01-18 18:31:45 -0800935
936 getResourceTracker().give();
937 releaseNetId(mIkey);
938 releaseNetId(mOkey);
939 }
940
Yan Yana2f3b492020-09-29 23:38:00 -0700941 @GuardedBy("IpSecService.this")
942 public void setUnderlyingNetwork(Network underlyingNetwork) {
943 // When #applyTunnelModeTransform is called, this new underlying network will be used to
944 // update the output mark of the input transform.
945 mUnderlyingNetwork = underlyingNetwork;
Benedict Wong8bc90732018-01-18 18:31:45 -0800946 }
947
Yan Yana2f3b492020-09-29 23:38:00 -0700948 @GuardedBy("IpSecService.this")
Benedict Wong8bc90732018-01-18 18:31:45 -0800949 public Network getUnderlyingNetwork() {
950 return mUnderlyingNetwork;
951 }
952
Yan Yana2f3b492020-09-29 23:38:00 -0700953 public String getInterfaceName() {
954 return mInterfaceName;
955 }
956
Benedict Wong8bc90732018-01-18 18:31:45 -0800957 /** Returns the local, outer address for the tunnelInterface */
958 public String getLocalAddress() {
959 return mLocalAddress;
960 }
961
962 /** Returns the remote, outer address for the tunnelInterface */
963 public String getRemoteAddress() {
964 return mRemoteAddress;
965 }
966
967 public int getIkey() {
968 return mIkey;
969 }
970
971 public int getOkey() {
972 return mOkey;
973 }
974
Benedict Wong5d749842018-09-06 11:31:25 -0700975 public int getIfId() {
976 return mIfId;
977 }
978
Benedict Wong8bc90732018-01-18 18:31:45 -0800979 @Override
980 protected ResourceTracker getResourceTracker() {
981 return getUserRecord().mTunnelQuotaTracker;
982 }
983
984 @Override
985 public void invalidate() {
986 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
987 }
988
989 @Override
990 public String toString() {
991 return new StringBuilder()
992 .append("{super=")
993 .append(super.toString())
994 .append(", mInterfaceName=")
995 .append(mInterfaceName)
996 .append(", mUnderlyingNetwork=")
997 .append(mUnderlyingNetwork)
998 .append(", mLocalAddress=")
999 .append(mLocalAddress)
1000 .append(", mRemoteAddress=")
1001 .append(mRemoteAddress)
1002 .append(", mIkey=")
1003 .append(mIkey)
1004 .append(", mOkey=")
1005 .append(mOkey)
1006 .append("}")
1007 .toString();
1008 }
1009 }
1010
Benedict Wongcbd329b2017-12-13 17:16:53 -08001011 /**
1012 * Tracks a UDP encap socket, and manages cleanup paths
1013 *
1014 * <p>While this class does not manage non-kernel resources, race conditions around socket
1015 * binding require that the service creates the encap socket, binds it and applies the socket
1016 * policy before handing it to a user.
1017 */
1018 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold80865392017-04-04 19:37:48 -07001019 private FileDescriptor mSocket;
1020 private final int mPort;
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001021 private final int mFamily; // TODO: what about IPV6_ADDRFORM?
Nathan Harold031acb82017-03-07 13:23:36 -08001022
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001023 EncapSocketRecord(int resourceId, FileDescriptor socket, int port, int family) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001024 super(resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001025 mSocket = socket;
1026 mPort = port;
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001027 mFamily = family;
Nathan Harold80865392017-04-04 19:37:48 -07001028 }
1029
1030 /** always guarded by IpSecService#this */
1031 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001032 public void freeUnderlyingResources() {
Nathan Harold80865392017-04-04 19:37:48 -07001033 Log.d(TAG, "Closing port " + mPort);
1034 IoUtils.closeQuietly(mSocket);
1035 mSocket = null;
Nathan Harold80865392017-04-04 19:37:48 -07001036
Benedict Wong6855aee2017-11-16 15:27:22 -08001037 getResourceTracker().give();
Nathan Harold6e4681c2017-04-24 16:16:34 -07001038 }
1039
Nathan Harold80865392017-04-04 19:37:48 -07001040 public int getPort() {
1041 return mPort;
1042 }
1043
Benedict Wonga386e372018-03-27 16:55:48 -07001044 public FileDescriptor getFileDescriptor() {
Nathan Harold80865392017-04-04 19:37:48 -07001045 return mSocket;
1046 }
ludi89194d62017-05-22 10:52:23 -07001047
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001048 public int getFamily() {
1049 return mFamily;
1050 }
1051
ludi89194d62017-05-22 10:52:23 -07001052 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001053 protected ResourceTracker getResourceTracker() {
1054 return getUserRecord().mSocketQuotaTracker;
1055 }
1056
1057 @Override
1058 public void invalidate() {
1059 getUserRecord().removeEncapSocketRecord(mResourceId);
1060 }
1061
1062 @Override
ludi89194d62017-05-22 10:52:23 -07001063 public String toString() {
1064 return new StringBuilder()
1065 .append("{super=")
1066 .append(super.toString())
1067 .append(", mSocket=")
1068 .append(mSocket)
1069 .append(", mPort=")
1070 .append(mPort)
1071 .append("}")
1072 .toString();
1073 }
Nathan Harold80865392017-04-04 19:37:48 -07001074 }
Nathan Harold031acb82017-03-07 13:23:36 -08001075
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001076 /**
1077 * Constructs a new IpSecService instance
1078 *
1079 * @param context Binder context for this service
1080 */
Aaron Huangb944ff12022-01-12 15:11:01 +08001081 public IpSecService(Context context) {
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001082 this(context, new Dependencies());
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08001083 }
1084
Nathan Harold65ef8432018-03-15 18:06:06 -07001085 @NonNull
1086 private AppOpsManager getAppOpsManager() {
1087 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Aaron Huangfbae3082021-12-06 15:18:42 +08001088 if (appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
Nathan Harold65ef8432018-03-15 18:06:06 -07001089 return appOps;
1090 }
1091
ludi5e623ea2017-05-12 09:15:00 -07001092 /** @hide */
1093 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001094 public IpSecService(Context context, Dependencies deps) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001095 this(
1096 context,
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001097 deps,
Nathan Harold5a19b952018-01-05 19:25:13 -08001098 (fd, uid) -> {
1099 try {
1100 TrafficStats.setThreadStatsUid(uid);
1101 TrafficStats.tagFileDescriptor(fd);
1102 } finally {
1103 TrafficStats.clearThreadStatsUid();
1104 }
1105 });
Benedict Wong083faee2017-12-03 19:42:36 -08001106 }
1107
1108 /** @hide */
1109 @VisibleForTesting
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001110 public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
ludi5e623ea2017-05-12 09:15:00 -07001111 mContext = context;
Aaron Huang2617cf52021-11-29 16:31:32 +08001112 mDeps = Objects.requireNonNull(deps, "Missing dependencies.");
Benedict Wong083faee2017-12-03 19:42:36 -08001113 mUidFdTagger = uidFdTagger;
Aaron Huang2617cf52021-11-29 16:31:32 +08001114 try {
1115 mNetd = mDeps.getNetdInstance(mContext);
1116 } catch (RemoteException e) {
1117 throw e.rethrowFromSystemServer();
1118 }
ludi5e623ea2017-05-12 09:15:00 -07001119 }
1120
Nathan Harold19b99d92017-08-23 13:46:33 -07001121 /**
1122 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1123 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1124 */
1125 private static void checkInetAddress(String inetAddress) {
1126 if (TextUtils.isEmpty(inetAddress)) {
1127 throw new IllegalArgumentException("Unspecified address");
1128 }
1129
Serik Beketayev7f507332020-12-06 22:31:23 -08001130 InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
Nathan Harold19b99d92017-08-23 13:46:33 -07001131
1132 if (checkAddr.isAnyLocalAddress()) {
1133 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1134 }
1135 }
1136
1137 /**
1138 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1139 * DIRECTION_IN or DIRECTION_OUT
1140 */
Benedict Wong908d34e2021-04-15 11:59:16 -07001141 private void checkDirection(int direction) {
Nathan Harold19b99d92017-08-23 13:46:33 -07001142 switch (direction) {
Nathan Harold5a19b952018-01-05 19:25:13 -08001143 case IpSecManager.DIRECTION_OUT:
1144 case IpSecManager.DIRECTION_IN:
Nathan Harold19b99d92017-08-23 13:46:33 -07001145 return;
Benedict Wong908d34e2021-04-15 11:59:16 -07001146 case IpSecManager.DIRECTION_FWD:
Benedict Wong47b528c2021-05-10 18:26:02 -07001147 // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
Benedict Wong908d34e2021-04-15 11:59:16 -07001148 PermissionUtils.enforceNetworkStackPermission(mContext);
1149 return;
Nathan Harold19b99d92017-08-23 13:46:33 -07001150 }
1151 throw new IllegalArgumentException("Invalid Direction: " + direction);
1152 }
1153
Nathan Harold031acb82017-03-07 13:23:36 -08001154 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri20e96c52017-11-16 10:58:01 -08001155 @Override
1156 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harold5a19b952018-01-05 19:25:13 -08001157 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1158 checkInetAddress(destinationAddress);
Nathan Harold1b88f0e2018-03-28 08:52:51 -07001159 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1160 if (requestedSpi > 0 && requestedSpi < 256) {
1161 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1162 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001163 Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harold19b99d92017-08-23 13:46:33 -07001164
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001165 int callingUid = Binder.getCallingUid();
1166 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001167 final int resourceId = mNextResourceId++;
Nathan Harold031acb82017-03-07 13:23:36 -08001168
1169 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold031acb82017-03-07 13:23:36 -08001170 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001171 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001172 return new IpSecSpiResponse(
Nathan Harold19b99d92017-08-23 13:46:33 -07001173 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold6e4681c2017-04-24 16:16:34 -07001174 }
Nathan Harold5a19b952018-01-05 19:25:13 -08001175
Aaron Huang2617cf52021-11-29 16:31:32 +08001176 spi = mNetd.ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold031acb82017-03-07 13:23:36 -08001177 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong6855aee2017-11-16 15:27:22 -08001178 userRecord.mSpiRecords.put(
Nathan Harold80865392017-04-04 19:37:48 -07001179 resourceId,
Benedict Wong6855aee2017-11-16 15:27:22 -08001180 new RefcountedResource<SpiRecord>(
Aaron Huang2617cf52021-11-29 16:31:32 +08001181 new SpiRecord(resourceId, "",
Aaron Huang9b27b0e2021-11-27 00:30:35 +08001182 destinationAddress, spi), binder));
Nathan Harold031acb82017-03-07 13:23:36 -08001183 } catch (ServiceSpecificException e) {
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001184 if (e.errorCode == OsConstants.ENOENT) {
1185 return new IpSecSpiResponse(
1186 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1187 }
1188 throw e;
Nathan Harold031acb82017-03-07 13:23:36 -08001189 } catch (RemoteException e) {
1190 throw e.rethrowFromSystemServer();
1191 }
Nathan Harold80865392017-04-04 19:37:48 -07001192 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1193 }
1194
1195 /* This method should only be called from Binder threads. Do not call this from
1196 * within the system server as it will crash the system on failure.
1197 */
Benedict Wong6855aee2017-11-16 15:27:22 -08001198 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold80865392017-04-04 19:37:48 -07001199 throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001200 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold031acb82017-03-07 13:23:36 -08001201 }
1202
1203 /** Release a previously allocated SPI that has been registered with the system server */
1204 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001205 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1206 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1207 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001208 }
1209
1210 /**
1211 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1212 * be unbound.
1213 *
1214 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1215 * a random open port and then bind by number, this function creates a temp socket, binds to a
1216 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1217 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1218 * FileHandle.
1219 *
1220 * <p>The loop in this function handles the inherent race window between un-binding to a port
1221 * and re-binding, during which the system could *technically* hand that port out to someone
1222 * else.
1223 */
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001224 private int bindToRandomPort(FileDescriptor sockFd, int family) throws IOException {
1225 final InetAddress any = (family == AF_INET6) ? IN6ADDR_ANY : INADDR_ANY;
Nathan Harold80865392017-04-04 19:37:48 -07001226 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1227 try {
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001228 FileDescriptor probeSocket = Os.socket(family, SOCK_DGRAM, IPPROTO_UDP);
1229 Os.bind(probeSocket, any, 0);
Nathan Harold80865392017-04-04 19:37:48 -07001230 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1231 Os.close(probeSocket);
1232 Log.v(TAG, "Binding to port " + port);
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001233 Os.bind(sockFd, any, port);
Benedict Wongc423cc82017-10-10 20:44:28 -07001234 return port;
Nathan Harold80865392017-04-04 19:37:48 -07001235 } catch (ErrnoException e) {
1236 // Someone miraculously claimed the port just after we closed probeSocket.
1237 if (e.errno == OsConstants.EADDRINUSE) {
1238 continue;
1239 }
1240 throw e.rethrowAsIOException();
1241 }
1242 }
1243 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1244 }
Nathan Harold031acb82017-03-07 13:23:36 -08001245
1246 /**
Benedict Wong083faee2017-12-03 19:42:36 -08001247 * Functional interface to do traffic tagging of given sockets to UIDs.
1248 *
1249 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1250 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1251 *
1252 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1253 * methods that cannot be easily mocked/tested.
1254 */
1255 @VisibleForTesting
1256 public interface UidFdTagger {
1257 /**
1258 * Sets socket tag to assign all traffic to the provided UID.
1259 *
1260 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1261 * should be accounted to the UID of the unprivileged application.
1262 */
Aaron Huangfbae3082021-12-06 15:18:42 +08001263 void tag(FileDescriptor fd, int uid) throws IOException;
Benedict Wong083faee2017-12-03 19:42:36 -08001264 }
1265
1266 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001267 * Open a socket via the system server and bind it to the specified port (random if port=0).
1268 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1269 * cache the socket and a record of its owner so that it can and must be freed when no longer
1270 * needed.
1271 */
1272 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001273 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1274 throws RemoteException {
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001275 // Experimental support for IPv6 UDP encap.
1276 final int family;
1277 final InetAddress localAddr;
1278 if (SdkLevel.isAtLeastU() && port >= 65536) {
1279 PermissionUtils.enforceNetworkStackPermissionOr(mContext, NETWORK_SETTINGS);
1280 port -= 65536;
1281 family = AF_INET6;
1282 localAddr = IN6ADDR_ANY;
1283 } else {
1284 family = AF_INET;
1285 localAddr = INADDR_ANY;
1286 }
1287
Nathan Harold80865392017-04-04 19:37:48 -07001288 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1289 throw new IllegalArgumentException(
1290 "Specified port number must be a valid non-reserved UDP port");
1291 }
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001292 Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
Nathan Harold19b99d92017-08-23 13:46:33 -07001293
Benedict Wong083faee2017-12-03 19:42:36 -08001294 int callingUid = Binder.getCallingUid();
1295 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldfdafce22017-12-13 19:16:33 -08001296 final int resourceId = mNextResourceId++;
Aaron Huangb01254f2021-12-23 10:47:05 +08001297
1298 ParcelFileDescriptor pFd = null;
Nathan Harold80865392017-04-04 19:37:48 -07001299 try {
Benedict Wong6855aee2017-11-16 15:27:22 -08001300 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001301 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1302 }
1303
Aaron Huangb01254f2021-12-23 10:47:05 +08001304 FileDescriptor sockFd = null;
1305 try {
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001306 sockFd = Os.socket(family, SOCK_DGRAM, IPPROTO_UDP);
Aaron Huangb01254f2021-12-23 10:47:05 +08001307 pFd = ParcelFileDescriptor.dup(sockFd);
1308 } finally {
1309 IoUtils.closeQuietly(sockFd);
1310 }
Nathan Harold80865392017-04-04 19:37:48 -07001311
Aaron Huangb01254f2021-12-23 10:47:05 +08001312 mUidFdTagger.tag(pFd.getFileDescriptor(), callingUid);
Nathan Harold80865392017-04-04 19:37:48 -07001313 // This code is common to both the unspecified and specified port cases
1314 Os.setsockoptInt(
Aaron Huangb01254f2021-12-23 10:47:05 +08001315 pFd.getFileDescriptor(),
Nathan Harold80865392017-04-04 19:37:48 -07001316 OsConstants.IPPROTO_UDP,
1317 OsConstants.UDP_ENCAP,
1318 OsConstants.UDP_ENCAP_ESPINUDP);
1319
Aaron Huangb01254f2021-12-23 10:47:05 +08001320 mNetd.ipSecSetEncapSocketOwner(pFd, callingUid);
Benedict Wong17687442017-12-06 21:56:35 -08001321 if (port != 0) {
1322 Log.v(TAG, "Binding to port " + port);
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001323 Os.bind(pFd.getFileDescriptor(), localAddr, port);
Benedict Wong17687442017-12-06 21:56:35 -08001324 } else {
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001325 port = bindToRandomPort(pFd.getFileDescriptor(), family);
Benedict Wong17687442017-12-06 21:56:35 -08001326 }
1327
Benedict Wong6855aee2017-11-16 15:27:22 -08001328 userRecord.mEncapSocketRecords.put(
1329 resourceId,
1330 new RefcountedResource<EncapSocketRecord>(
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001331 new EncapSocketRecord(resourceId, pFd.getFileDescriptor(), port,
1332 family),
Aaron Huangb01254f2021-12-23 10:47:05 +08001333 binder));
1334 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port,
1335 pFd.getFileDescriptor());
Nathan Harold80865392017-04-04 19:37:48 -07001336 } catch (IOException | ErrnoException e) {
Aaron Huangb01254f2021-12-23 10:47:05 +08001337 try {
1338 if (pFd != null) {
1339 pFd.close();
1340 }
1341 } catch (IOException ex) {
1342 // Nothing can be done at this point
1343 Log.e(TAG, "Failed to close pFd.");
1344 }
Nathan Harold80865392017-04-04 19:37:48 -07001345 }
1346 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1347 // The only reasonable condition that would cause that is resource unavailable.
1348 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold031acb82017-03-07 13:23:36 -08001349 }
1350
1351 /** close a socket that has been been allocated by and registered with the system server */
1352 @Override
Benedict Wong6855aee2017-11-16 15:27:22 -08001353 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1354 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1355 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold80865392017-04-04 19:37:48 -07001356 }
Nathan Harold031acb82017-03-07 13:23:36 -08001357
Benedict Wong8bc90732018-01-18 18:31:45 -08001358 /**
1359 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1360 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1361 * needed.
1362 */
1363 @Override
1364 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001365 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1366 String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001367 enforceTunnelFeatureAndPermissions(callingPackage);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001368 Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
1369 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
Benedict Wong8bc90732018-01-18 18:31:45 -08001370 checkInetAddress(localAddr);
1371 checkInetAddress(remoteAddr);
1372
1373 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1374 // network (b/72316676).
1375
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001376 int callerUid = Binder.getCallingUid();
1377 UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001378 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1379 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1380 }
1381
1382 final int resourceId = mNextResourceId++;
1383 final int ikey = reserveNetId();
1384 final int okey = reserveNetId();
Nathan Harold7be7f452018-04-26 11:47:14 -07001385 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001386
Benedict Wong8edc5572018-01-19 17:36:02 -08001387 try {
1388 // Calls to netd:
1389 // Create VTI
1390 // Add inbound/outbound global policies
1391 // (use reqid = 0)
Aaron Huang2617cf52021-11-29 16:31:32 +08001392 mNetd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
Benedict Wong8bc90732018-01-18 18:31:45 -08001393
paulhu00e34562021-10-26 09:00:50 +00001394 BinderUtils.withCleanCallingIdentity(() -> {
Aaron Huang2617cf52021-11-29 16:31:32 +08001395 NetdUtils.setInterfaceUp(mNetd, intfName);
Benedict Wong529e8aa2020-02-11 23:49:36 -08001396 });
1397
Benedict Wong38e52972018-05-07 20:06:44 -07001398 for (int selAddrFamily : ADDRESS_FAMILIES) {
1399 // Always send down correct local/remote addresses for template.
Aaron Huang2617cf52021-11-29 16:31:32 +08001400 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001401 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001402 selAddrFamily,
1403 IpSecManager.DIRECTION_OUT,
1404 localAddr,
1405 remoteAddr,
1406 0,
1407 okey,
Benedict Wong5d749842018-09-06 11:31:25 -07001408 0xffffffff,
1409 resourceId);
Aaron Huang2617cf52021-11-29 16:31:32 +08001410 mNetd.ipSecAddSecurityPolicy(
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001411 callerUid,
Benedict Wong38e52972018-05-07 20:06:44 -07001412 selAddrFamily,
1413 IpSecManager.DIRECTION_IN,
1414 remoteAddr,
1415 localAddr,
1416 0,
1417 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001418 0xffffffff,
1419 resourceId);
Benedict Wong47b528c2021-05-10 18:26:02 -07001420
1421 // Add a forwarding policy on the tunnel interface. In order to support forwarding
1422 // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
1423 //
1424 // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
1425 // forwarding will be blocked by default (as would be the case if this policy was
1426 // absent).
1427 //
1428 // This is necessary only on the tunnel interface, and not any the interface to
1429 // which traffic will be forwarded to.
Aaron Huang2617cf52021-11-29 16:31:32 +08001430 mNetd.ipSecAddSecurityPolicy(
Benedict Wong908d34e2021-04-15 11:59:16 -07001431 callerUid,
1432 selAddrFamily,
1433 IpSecManager.DIRECTION_FWD,
1434 remoteAddr,
1435 localAddr,
1436 0,
1437 ikey,
1438 0xffffffff,
1439 resourceId);
Benedict Wong8edc5572018-01-19 17:36:02 -08001440 }
1441
1442 userRecord.mTunnelInterfaceRecords.put(
1443 resourceId,
1444 new RefcountedResource<TunnelInterfaceRecord>(
1445 new TunnelInterfaceRecord(
1446 resourceId,
1447 intfName,
1448 underlyingNetwork,
1449 localAddr,
1450 remoteAddr,
1451 ikey,
Benedict Wong5d749842018-09-06 11:31:25 -07001452 okey,
1453 resourceId),
Benedict Wong8edc5572018-01-19 17:36:02 -08001454 binder));
1455 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1456 } catch (RemoteException e) {
1457 // Release keys if we got an error.
1458 releaseNetId(ikey);
1459 releaseNetId(okey);
1460 throw e.rethrowFromSystemServer();
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001461 } catch (Throwable t) {
1462 // Release keys if we got an error.
1463 releaseNetId(ikey);
1464 releaseNetId(okey);
1465 throw t;
Benedict Wong8edc5572018-01-19 17:36:02 -08001466 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001467 }
1468
1469 /**
1470 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1471 * from multiple local IP addresses over the same tunnel.
1472 */
1473 @Override
Benedict Wong97c3c942018-03-01 18:53:07 -08001474 public synchronized void addAddressToTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001475 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001476 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001477 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1478
1479 // Get tunnelInterface record; if no such interface is found, will throw
1480 // IllegalArgumentException
1481 TunnelInterfaceRecord tunnelInterfaceInfo =
1482 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1483
Benedict Wong97c3c942018-03-01 18:53:07 -08001484 try {
1485 // We can assume general validity of the IP address, since we get them as a
1486 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001487 mNetd.interfaceAddAddress(
1488 tunnelInterfaceInfo.mInterfaceName,
1489 localAddr.getAddress().getHostAddress(),
1490 localAddr.getPrefixLength());
Benedict Wong97c3c942018-03-01 18:53:07 -08001491 } catch (RemoteException e) {
1492 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001493 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001494 }
1495
1496 /**
1497 * Remove a new local address from the tunnel interface. After removal, the address will no
1498 * longer be available to send from, or receive on.
1499 */
1500 @Override
1501 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold65ef8432018-03-15 18:06:06 -07001502 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
Benedict Wonge9763752018-11-08 19:45:34 -08001503 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001504
Nathan Harold65ef8432018-03-15 18:06:06 -07001505 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8bc90732018-01-18 18:31:45 -08001506 // Get tunnelInterface record; if no such interface is found, will throw
1507 // IllegalArgumentException
1508 TunnelInterfaceRecord tunnelInterfaceInfo =
1509 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1510
Benedict Wong97c3c942018-03-01 18:53:07 -08001511 try {
1512 // We can assume general validity of the IP address, since we get them as a
1513 // LinkAddress, which does some validation.
Aaron Huang2617cf52021-11-29 16:31:32 +08001514 mNetd.interfaceDelAddress(
Benedict Wong97c3c942018-03-01 18:53:07 -08001515 tunnelInterfaceInfo.mInterfaceName,
1516 localAddr.getAddress().getHostAddress(),
1517 localAddr.getPrefixLength());
1518 } catch (RemoteException e) {
1519 throw e.rethrowFromSystemServer();
Benedict Wong97c3c942018-03-01 18:53:07 -08001520 }
Benedict Wong8bc90732018-01-18 18:31:45 -08001521 }
1522
Yan Yana2f3b492020-09-29 23:38:00 -07001523 /** Set TunnelInterface to use a specific underlying network. */
1524 @Override
1525 public synchronized void setNetworkForTunnelInterface(
1526 int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
1527 enforceTunnelFeatureAndPermissions(callingPackage);
1528 Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1529
1530 final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1531
1532 // Get tunnelInterface record; if no such interface is found, will throw
1533 // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
1534 final TunnelInterfaceRecord tunnelInterfaceInfo =
1535 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1536
1537 final ConnectivityManager connectivityManager =
1538 mContext.getSystemService(ConnectivityManager.class);
1539 final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
Yan Yanbe3eb3d2022-05-16 17:13:45 -07001540 if (lp == null) {
1541 throw new IllegalArgumentException(
1542 "LinkProperties is null. The underlyingNetwork may not be functional");
1543 }
1544
Yan Yana2f3b492020-09-29 23:38:00 -07001545 if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
1546 throw new IllegalArgumentException(
1547 "Underlying network cannot be the network being exposed by this tunnel");
1548 }
1549
1550 // It is meaningless to check if the network exists or is valid because the network might
1551 // disconnect at any time after it passes the check.
1552
1553 tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
1554 }
1555
Benedict Wong8bc90732018-01-18 18:31:45 -08001556 /**
1557 * Delete a TunnelInterface that has been been allocated by and registered with the system
1558 * server
1559 */
1560 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001561 public synchronized void deleteTunnelInterface(
1562 int resourceId, String callingPackage) throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001563 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001564 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1565 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1566 }
1567
Benedict Wong70867e52017-11-06 20:49:10 -08001568 @VisibleForTesting
Nathan Harold5a19b952018-01-05 19:25:13 -08001569 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1570 IpSecAlgorithm auth = config.getAuthentication();
1571 IpSecAlgorithm crypt = config.getEncryption();
1572 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong70867e52017-11-06 20:49:10 -08001573
Nathan Harold5a19b952018-01-05 19:25:13 -08001574 // Validate the algorithm set
1575 Preconditions.checkArgument(
1576 aead != null || crypt != null || auth != null,
1577 "No Encryption or Authentication algorithms specified");
1578 Preconditions.checkArgument(
1579 auth == null || auth.isAuthentication(),
1580 "Unsupported algorithm for Authentication");
1581 Preconditions.checkArgument(
Benedict Wong70867e52017-11-06 20:49:10 -08001582 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harold5a19b952018-01-05 19:25:13 -08001583 Preconditions.checkArgument(
1584 aead == null || aead.isAead(),
1585 "Unsupported algorithm for Authenticated Encryption");
1586 Preconditions.checkArgument(
1587 aead == null || (auth == null && crypt == null),
1588 "Authenticated Encryption is mutually exclusive with other Authentication "
1589 + "or Encryption algorithms");
Benedict Wong70867e52017-11-06 20:49:10 -08001590 }
1591
evitayan43d93a02018-03-22 17:53:08 -07001592 private int getFamily(String inetAddress) {
1593 int family = AF_UNSPEC;
Serik Beketayev7f507332020-12-06 22:31:23 -08001594 InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
evitayan43d93a02018-03-22 17:53:08 -07001595 if (checkAddress instanceof Inet4Address) {
1596 family = AF_INET;
1597 } else if (checkAddress instanceof Inet6Address) {
1598 family = AF_INET6;
1599 }
1600 return family;
1601 }
1602
Nathan Harold031acb82017-03-07 13:23:36 -08001603 /**
Chiachang Wang2fea4a72020-08-12 12:23:59 +08001604 * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
Nathan Harold19b99d92017-08-23 13:46:33 -07001605 * IllegalArgumentException if they are not.
1606 */
1607 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001608 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001609 EncapSocketRecord encapSocketRecord = null;
Benedict Wong6855aee2017-11-16 15:27:22 -08001610
Nathan Harold19b99d92017-08-23 13:46:33 -07001611 switch (config.getEncapType()) {
1612 case IpSecTransform.ENCAP_NONE:
1613 break;
1614 case IpSecTransform.ENCAP_ESPINUDP:
1615 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong6855aee2017-11-16 15:27:22 -08001616 // Retrieve encap socket record; will throw IllegalArgumentException if not found
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001617 encapSocketRecord = userRecord.mEncapSocketRecords.getResourceOrThrow(
Benedict Wong6855aee2017-11-16 15:27:22 -08001618 config.getEncapSocketResourceId());
Nathan Harold19b99d92017-08-23 13:46:33 -07001619
1620 int port = config.getEncapRemotePort();
1621 if (port <= 0 || port > 0xFFFF) {
1622 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1623 }
1624 break;
1625 default:
1626 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1627 }
1628
Nathan Harold5a19b952018-01-05 19:25:13 -08001629 validateAlgorithms(config);
Nathan Harold19b99d92017-08-23 13:46:33 -07001630
Nathan Harold5a19b952018-01-05 19:25:13 -08001631 // Retrieve SPI record; will throw IllegalArgumentException if not found
1632 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1633
Benedict Wong68aac2a2017-12-13 18:26:40 -08001634 // Check to ensure that SPI has not already been used.
1635 if (s.getOwnedByTransform()) {
1636 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1637 }
1638
Nathan Harold5a19b952018-01-05 19:25:13 -08001639 // If no remote address is supplied, then use one from the SPI.
1640 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1641 config.setDestinationAddress(s.getDestinationAddress());
1642 }
1643
1644 // All remote addresses must match
1645 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1646 throw new IllegalArgumentException("Mismatched remote addresseses.");
1647 }
1648
1649 // This check is technically redundant due to the chain of custody between the SPI and
1650 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1651 // the transform, this will prevent us from messing up.
1652 checkInetAddress(config.getDestinationAddress());
1653
1654 // Require a valid source address for all transforms.
1655 checkInetAddress(config.getSourceAddress());
1656
evitayan43d93a02018-03-22 17:53:08 -07001657 // Check to ensure source and destination have the same address family.
1658 String sourceAddress = config.getSourceAddress();
1659 String destinationAddress = config.getDestinationAddress();
1660 int sourceFamily = getFamily(sourceAddress);
1661 int destinationFamily = getFamily(destinationAddress);
1662 if (sourceFamily != destinationFamily) {
1663 throw new IllegalArgumentException(
1664 "Source address ("
1665 + sourceAddress
1666 + ") and destination address ("
1667 + destinationAddress
1668 + ") have different address families.");
1669 }
1670
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001671 if (encapSocketRecord != null && encapSocketRecord.getFamily() != destinationFamily) {
evitayan43d93a02018-03-22 17:53:08 -07001672 throw new IllegalArgumentException(
Lorenzo Colittib38fef82023-02-06 15:22:01 +09001673 "UDP encapsulation socket and destination address families must match");
evitayan43d93a02018-03-22 17:53:08 -07001674 }
1675
Nathan Harold5a19b952018-01-05 19:25:13 -08001676 switch (config.getMode()) {
1677 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold025aae12018-02-02 18:34:25 -08001678 break;
Nathan Harold5a19b952018-01-05 19:25:13 -08001679 case IpSecTransform.MODE_TUNNEL:
1680 break;
1681 default:
1682 throw new IllegalArgumentException(
1683 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harold19b99d92017-08-23 13:46:33 -07001684 }
Benedict Wong683441d2018-07-25 18:46:19 -07001685
1686 config.setMarkValue(0);
1687 config.setMarkMask(0);
Nathan Harold19b99d92017-08-23 13:46:33 -07001688 }
1689
Benedict Wong2b6a14e2018-09-13 16:45:12 -07001690 private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
Nathan Harold7c250ae2018-05-15 19:18:38 -07001691
Benedict Wonge9763752018-11-08 19:45:34 -08001692 private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1693 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1694 throw new UnsupportedOperationException(
1695 "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1696 }
1697
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001698 Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Benedict Wongc85b7b02019-11-12 22:31:51 -08001699
1700 // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
1701 // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
1702 // permission or is the System Server.
1703 if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
1704 TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1705 return;
Nathan Harold65ef8432018-03-15 18:06:06 -07001706 }
Benedict Wongc85b7b02019-11-12 22:31:51 -08001707 mContext.enforceCallingOrSelfPermission(
1708 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
Nathan Harold025aae12018-02-02 18:34:25 -08001709 }
1710
Yan Yana48dcd92022-10-18 00:03:20 +00001711 private void enforceMigrateFeature() {
1712 if (!mContext.getPackageManager().hasSystemFeature(FEATURE_IPSEC_TUNNEL_MIGRATION)) {
1713 throw new UnsupportedOperationException(
1714 "IPsec Tunnel migration requires"
1715 + " PackageManager.FEATURE_IPSEC_TUNNEL_MIGRATION");
1716 }
1717 }
1718
Benedict Wong8edc5572018-01-19 17:36:02 -08001719 private void createOrUpdateTransform(
1720 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1721 throws RemoteException {
1722
1723 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1724 if (encapType != IpSecTransform.ENCAP_NONE) {
1725 encapLocalPort = socketRecord.getPort();
1726 encapRemotePort = c.getEncapRemotePort();
1727 }
1728
1729 IpSecAlgorithm auth = c.getAuthentication();
1730 IpSecAlgorithm crypt = c.getEncryption();
1731 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1732
Benedict Wong778327e2018-03-15 19:41:41 -07001733 String cryptName;
1734 if (crypt == null) {
1735 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1736 } else {
1737 cryptName = crypt.getName();
1738 }
1739
Aaron Huang2617cf52021-11-29 16:31:32 +08001740 mNetd.ipSecAddSecurityAssociation(
1741 Binder.getCallingUid(),
1742 c.getMode(),
1743 c.getSourceAddress(),
1744 c.getDestinationAddress(),
1745 (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
1746 spiRecord.getSpi(),
1747 c.getMarkValue(),
1748 c.getMarkMask(),
1749 (auth != null) ? auth.getName() : "",
1750 (auth != null) ? auth.getKey() : new byte[] {},
1751 (auth != null) ? auth.getTruncationLengthBits() : 0,
1752 cryptName,
1753 (crypt != null) ? crypt.getKey() : new byte[] {},
1754 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1755 (authCrypt != null) ? authCrypt.getName() : "",
1756 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1757 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1758 encapType,
1759 encapLocalPort,
1760 encapRemotePort,
1761 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001762 }
1763
Nathan Harold19b99d92017-08-23 13:46:33 -07001764 /**
Benedict Wong8edc5572018-01-19 17:36:02 -08001765 * Create a IPsec transform, which represents a single security association in the kernel. The
1766 * transform will be cached by the system server and must be freed when no longer needed. It is
1767 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1768 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold031acb82017-03-07 13:23:36 -08001769 */
1770 @Override
Nathan Harold65ef8432018-03-15 18:06:06 -07001771 public synchronized IpSecTransformResponse createTransform(
1772 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001773 Objects.requireNonNull(c);
Nathan Harold65ef8432018-03-15 18:06:06 -07001774 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
Benedict Wonge9763752018-11-08 19:45:34 -08001775 enforceTunnelFeatureAndPermissions(callingPackage);
Nathan Harold65ef8432018-03-15 18:06:06 -07001776 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001777 checkIpSecConfig(c);
Daulet Zhanguzina12c44d2020-03-26 12:30:39 +00001778 Objects.requireNonNull(binder, "Null Binder passed to createTransform");
Nathan Haroldfdafce22017-12-13 19:16:33 -08001779 final int resourceId = mNextResourceId++;
Benedict Wong6855aee2017-11-16 15:27:22 -08001780
1781 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wongcbd329b2017-12-13 17:16:53 -08001782 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong6855aee2017-11-16 15:27:22 -08001783
1784 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harold6e4681c2017-04-24 16:16:34 -07001785 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1786 }
Nathan Harold19b99d92017-08-23 13:46:33 -07001787
Benedict Wong6855aee2017-11-16 15:27:22 -08001788 EncapSocketRecord socketRecord = null;
Benedict Wong8edc5572018-01-19 17:36:02 -08001789 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong6855aee2017-11-16 15:27:22 -08001790 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1791 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1792 c.getEncapSocketResourceId());
1793 dependencies.add(refcountedSocketRecord);
Benedict Wong6855aee2017-11-16 15:27:22 -08001794 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold80865392017-04-04 19:37:48 -07001795 }
1796
Nathan Harold5a19b952018-01-05 19:25:13 -08001797 RefcountedResource<SpiRecord> refcountedSpiRecord =
1798 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1799 dependencies.add(refcountedSpiRecord);
1800 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong6855aee2017-11-16 15:27:22 -08001801
Nathan Haroldbeed0b62018-04-03 16:13:19 -07001802 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong8edc5572018-01-19 17:36:02 -08001803
1804 // SA was created successfully, time to construct a record and lock it away
Benedict Wong6855aee2017-11-16 15:27:22 -08001805 userRecord.mTransformRecords.put(
1806 resourceId,
1807 new RefcountedResource<TransformRecord>(
Nathan Harold5a19b952018-01-05 19:25:13 -08001808 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong6855aee2017-11-16 15:27:22 -08001809 binder,
1810 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold80865392017-04-04 19:37:48 -07001811 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001812 }
1813
1814 /**
Yan Yane114b382021-02-16 16:29:48 -08001815 * Migrate an active Tunnel Mode IPsec Transform to new source/destination addresses.
1816 *
1817 * <p>Begins the process of migrating a transform and cache the new addresses. To complete the
1818 * migration once started, callers MUST apply the same transform to the appropriate tunnel using
1819 * {@link #applyTunnelModeTransform}. Otherwise, the address update will not be committed and
1820 * the transform will still only process traffic between the current source and destination
1821 * address. One common use case is that the control plane will start the migration process and
1822 * then hand off the transform to the IPsec caller to perform the actual migration when the
1823 * tunnel is ready.
1824 *
1825 * <p>If this method is called multiple times before {@link #applyTunnelModeTransform} is
1826 * called, when the transform is applied, it will be migrated to the addresses from the last
1827 * call.
1828 *
1829 * <p>The provided source and destination addresses MUST share the same address family, but they
1830 * can have a different family from the current addresses.
1831 *
1832 * <p>Transform migration is only supported for tunnel mode transforms. Calling this method on
1833 * other types of transforms will throw an {@code UnsupportedOperationException}.
1834 */
1835 @Override
1836 public synchronized void migrateTransform(
1837 int transformId,
1838 String newSourceAddress,
1839 String newDestinationAddress,
1840 String callingPackage) {
1841 Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
1842 Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
1843
1844 enforceTunnelFeatureAndPermissions(callingPackage);
Yan Yana48dcd92022-10-18 00:03:20 +00001845 enforceMigrateFeature();
Yan Yane114b382021-02-16 16:29:48 -08001846
1847 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1848 TransformRecord transformInfo =
1849 userRecord.mTransformRecords.getResourceOrThrow(transformId);
1850 transformInfo.startMigration(newSourceAddress, newDestinationAddress);
1851 }
1852
1853 /**
Nathan Harold031acb82017-03-07 13:23:36 -08001854 * Delete a transport mode transform that was previously allocated by + registered with the
1855 * system server. If this is called on an inactive (or non-existent) transform, it will not
1856 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1857 * other reasons.
1858 */
1859 @Override
Benedict Wong0fff56e2018-01-18 14:38:16 -08001860 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong6855aee2017-11-16 15:27:22 -08001861 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1862 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001863 }
1864
1865 /**
1866 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1867 * association as a correspondent policy to the provided socket
1868 */
1869 @Override
Nathan Harold80865392017-04-04 19:37:48 -07001870 public synchronized void applyTransportModeTransform(
Nathan Harold5a19b952018-01-05 19:25:13 -08001871 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001872 int callingUid = Binder.getCallingUid();
1873 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harold5a19b952018-01-05 19:25:13 -08001874 checkDirection(direction);
Benedict Wong6855aee2017-11-16 15:27:22 -08001875 // Get transform record; if no transform is found, will throw IllegalArgumentException
1876 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold031acb82017-03-07 13:23:36 -08001877
Nathan Harold80865392017-04-04 19:37:48 -07001878 // TODO: make this a function.
Aaron Huangfbae3082021-12-06 15:18:42 +08001879 if (info.mPid != getCallingPid() || info.mUid != callingUid) {
Nathan Harold80865392017-04-04 19:37:48 -07001880 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1881 }
1882
Benedict Wong8bc90732018-01-18 18:31:45 -08001883 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold80865392017-04-04 19:37:48 -07001884 IpSecConfig c = info.getConfig();
Benedict Wong8bc90732018-01-18 18:31:45 -08001885 Preconditions.checkArgument(
1886 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1887 "Transform mode was not Transport mode; cannot be applied to a socket");
1888
Aaron Huang2617cf52021-11-29 16:31:32 +08001889 mNetd.ipSecApplyTransportModeTransform(
1890 socket,
1891 callingUid,
1892 direction,
1893 c.getSourceAddress(),
1894 c.getDestinationAddress(),
1895 info.getSpiRecord().getSpi());
Nathan Harold031acb82017-03-07 13:23:36 -08001896 }
Nathan Harold80865392017-04-04 19:37:48 -07001897
Nathan Harold031acb82017-03-07 13:23:36 -08001898 /**
Nathan Harold5a19b952018-01-05 19:25:13 -08001899 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1900 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1901 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1902 * reserved for future improved input validation.
Nathan Harold031acb82017-03-07 13:23:36 -08001903 */
1904 @Override
Nathan Harold0d483b72018-01-17 01:00:20 -08001905 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1906 throws RemoteException {
Aaron Huang2617cf52021-11-29 16:31:32 +08001907 mNetd.ipSecRemoveTransportModeTransform(socket);
Nathan Harold031acb82017-03-07 13:23:36 -08001908 }
1909
Benedict Wong8bc90732018-01-18 18:31:45 -08001910 /**
1911 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
Yan Yane114b382021-02-16 16:29:48 -08001912 * security association as a correspondent policy to the provided interface.
1913 *
1914 * <p>If the transform is migrating, migrate the IPsec security association to new
1915 * source/destination addresses, and mark the migration as finished.
Benedict Wong8bc90732018-01-18 18:31:45 -08001916 */
1917 @Override
1918 public synchronized void applyTunnelModeTransform(
Yan Yane114b382021-02-16 16:29:48 -08001919 int tunnelResourceId, int direction, int transformResourceId, String callingPackage)
1920 throws RemoteException {
Benedict Wonge9763752018-11-08 19:45:34 -08001921 enforceTunnelFeatureAndPermissions(callingPackage);
Benedict Wong8bc90732018-01-18 18:31:45 -08001922 checkDirection(direction);
1923
Benedict Wong6d0cd0b2018-07-25 13:06:29 -07001924 int callingUid = Binder.getCallingUid();
1925 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8bc90732018-01-18 18:31:45 -08001926
1927 // Get transform record; if no transform is found, will throw IllegalArgumentException
1928 TransformRecord transformInfo =
1929 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1930
1931 // Get tunnelInterface record; if no such interface is found, will throw
1932 // IllegalArgumentException
1933 TunnelInterfaceRecord tunnelInterfaceInfo =
1934 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1935
1936 // Get config and check that to-be-applied transform has the correct mode
1937 IpSecConfig c = transformInfo.getConfig();
1938 Preconditions.checkArgument(
1939 c.getMode() == IpSecTransform.MODE_TUNNEL,
1940 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1941
Benedict Wong8edc5572018-01-19 17:36:02 -08001942 EncapSocketRecord socketRecord = null;
1943 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1944 socketRecord =
1945 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1946 }
Benedict Wongec2e2e22019-10-03 11:09:00 -07001947 SpiRecord spiRecord = transformInfo.getSpiRecord();
Benedict Wong8edc5572018-01-19 17:36:02 -08001948
Benedict Wong8bc90732018-01-18 18:31:45 -08001949 int mark =
Benedict Wong38e52972018-05-07 20:06:44 -07001950 (direction == IpSecManager.DIRECTION_OUT)
1951 ? tunnelInterfaceInfo.getOkey()
Benedict Wong908d34e2021-04-15 11:59:16 -07001952 : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
Benedict Wong8bc90732018-01-18 18:31:45 -08001953
Benedict Wong8edc5572018-01-19 17:36:02 -08001954 try {
Benedict Wong5d749842018-09-06 11:31:25 -07001955 // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
1956 // SPI matching as part of the template resolution.
1957 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1958 c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
1959
Benedict Wong683441d2018-07-25 18:46:19 -07001960 // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
1961 // (and backporting) would allow us to narrow the mark space, and ensure that the SA
1962 // and SPs have matching marks (as VTI are meant to be built).
1963 // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
1964 // config matches the actual allocated resources in the kernel.
Benedict Wongc6fcedd2018-11-21 21:24:55 -08001965 // All SAs will have zero marks (from creation time), and any policy that matches the
1966 // same src/dst could match these SAs. Non-IpSecService governed processes that
1967 // establish floating policies with the same src/dst may result in undefined
1968 // behavior. This is generally limited to vendor code due to the permissions
1969 // (CAP_NET_ADMIN) required.
Benedict Wong683441d2018-07-25 18:46:19 -07001970 //
1971 // c.setMarkValue(mark);
1972 // c.setMarkMask(0xffffffff);
Benedict Wong8edc5572018-01-19 17:36:02 -08001973
1974 if (direction == IpSecManager.DIRECTION_OUT) {
1975 // Set output mark via underlying network (output only)
1976 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1977
Benedict Wong5d749842018-09-06 11:31:25 -07001978 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
1979 // but want to guarantee outbound packets are sent over the new SA.
Benedict Wongec2e2e22019-10-03 11:09:00 -07001980 spi = spiRecord.getSpi();
Benedict Wong5d749842018-09-06 11:31:25 -07001981 }
1982
1983 // Always update the policy with the relevant XFRM_IF_ID
1984 for (int selAddrFamily : ADDRESS_FAMILIES) {
Aaron Huang2617cf52021-11-29 16:31:32 +08001985 mNetd.ipSecUpdateSecurityPolicy(
1986 callingUid,
1987 selAddrFamily,
1988 direction,
1989 transformInfo.getConfig().getSourceAddress(),
1990 transformInfo.getConfig().getDestinationAddress(),
1991 spi, // If outbound, also add SPI to the policy.
1992 mark, // Must always set policy mark; ikey/okey for VTIs
1993 0xffffffff,
1994 c.getXfrmInterfaceId());
Benedict Wong8edc5572018-01-19 17:36:02 -08001995 }
1996
1997 // Update SA with tunnel mark (ikey or okey based on direction)
1998 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
Yan Yane114b382021-02-16 16:29:48 -08001999
2000 if (transformInfo.isMigrating()) {
Yan Yana48dcd92022-10-18 00:03:20 +00002001 if (!mContext.getPackageManager()
2002 .hasSystemFeature(FEATURE_IPSEC_TUNNEL_MIGRATION)) {
2003 Log.wtf(
2004 TAG,
2005 "Attempted to migrate a transform without"
2006 + " FEATURE_IPSEC_TUNNEL_MIGRATION");
2007 }
2008
Yan Yane114b382021-02-16 16:29:48 -08002009 for (int selAddrFamily : ADDRESS_FAMILIES) {
2010 final IpSecMigrateInfoParcel migrateInfo =
2011 new IpSecMigrateInfoParcel(
2012 Binder.getCallingUid(),
2013 selAddrFamily,
2014 direction,
2015 c.getSourceAddress(),
2016 c.getDestinationAddress(),
2017 transformInfo.getNewSourceAddress(),
2018 transformInfo.getNewDestinationAddress(),
2019 c.getXfrmInterfaceId());
2020
2021 mNetd.ipSecMigrate(migrateInfo);
2022 }
2023 transformInfo.finishMigration();
2024 }
Benedict Wong8edc5572018-01-19 17:36:02 -08002025 } catch (ServiceSpecificException e) {
2026 if (e.errorCode == EINVAL) {
2027 throw new IllegalArgumentException(e.toString());
2028 } else {
2029 throw e;
2030 }
2031 }
Benedict Wong8bc90732018-01-18 18:31:45 -08002032 }
2033
Nathan Harold031acb82017-03-07 13:23:36 -08002034 @Override
ludi89194d62017-05-22 10:52:23 -07002035 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08002036 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludi89194d62017-05-22 10:52:23 -07002037
2038 pw.println("IpSecService dump:");
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08002039 pw.println();
ludi89194d62017-05-22 10:52:23 -07002040
Benedict Wong6855aee2017-11-16 15:27:22 -08002041 pw.println("mUserResourceTracker:");
2042 pw.println(mUserResourceTracker);
Nathan Haroldd2a1dad2017-03-01 18:55:06 -08002043 }
2044}