blob: 380f6a7e581e51b23a92c4732ebb1f82a3bddd13 [file] [log] [blame]
Nathan Harold1afbef42017-03-01 18:55:06 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static android.Manifest.permission.DUMP;
Nathan Harold93962f32017-03-07 13:23:36 -080020import static android.net.IpSecManager.INVALID_RESOURCE_ID;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070021import static android.system.OsConstants.AF_INET;
manojboopathic4be79d2017-11-30 17:11:49 -080022import static android.system.OsConstants.EINVAL;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070023import static android.system.OsConstants.IPPROTO_UDP;
24import static android.system.OsConstants.SOCK_DGRAM;
25import static com.android.internal.util.Preconditions.checkNotNull;
Nathan Harold1afbef42017-03-01 18:55:06 -080026
Nathan Harold21208ee2018-03-15 18:06:06 -070027import android.annotation.NonNull;
28import android.app.AppOpsManager;
Nathan Harold1afbef42017-03-01 18:55:06 -080029import android.content.Context;
30import android.net.IIpSecService;
31import android.net.INetd;
Nathan Harold93962f32017-03-07 13:23:36 -080032import android.net.IpSecAlgorithm;
33import android.net.IpSecConfig;
34import android.net.IpSecManager;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070035import android.net.IpSecSpiResponse;
Nathan Harold93962f32017-03-07 13:23:36 -080036import android.net.IpSecTransform;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070037import android.net.IpSecTransformResponse;
Benedict Wong8149f6e2018-01-18 18:31:45 -080038import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070039import android.net.IpSecUdpEncapResponse;
Benedict Wongecc9f7c2018-03-01 18:53:07 -080040import android.net.LinkAddress;
Benedict Wong8149f6e2018-01-18 18:31:45 -080041import android.net.Network;
Nathan Harolda10003d2017-08-23 13:46:33 -070042import android.net.NetworkUtils;
Benedict Wongbabe5d72017-12-03 19:42:36 -080043import android.net.TrafficStats;
Nathan Harold1afbef42017-03-01 18:55:06 -080044import android.net.util.NetdService;
Nathan Harold93962f32017-03-07 13:23:36 -080045import android.os.Binder;
Nathan Harold93962f32017-03-07 13:23:36 -080046import android.os.IBinder;
47import android.os.ParcelFileDescriptor;
Nathan Harold1afbef42017-03-01 18:55:06 -080048import android.os.RemoteException;
Nathan Harold93962f32017-03-07 13:23:36 -080049import android.os.ServiceSpecificException;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070050import android.system.ErrnoException;
51import android.system.Os;
52import android.system.OsConstants;
Nathan Harolda10003d2017-08-23 13:46:33 -070053import android.text.TextUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080054import android.util.Log;
55import android.util.Slog;
Nathan Harold93962f32017-03-07 13:23:36 -080056import android.util.SparseArray;
Benedict Wong8149f6e2018-01-18 18:31:45 -080057import android.util.SparseBooleanArray;
Nathan Harolda10003d2017-08-23 13:46:33 -070058
Nathan Harold93962f32017-03-07 13:23:36 -080059import com.android.internal.annotations.GuardedBy;
ludi1a06aa72017-05-12 09:15:00 -070060import com.android.internal.annotations.VisibleForTesting;
Benedict Wong4f255702017-11-06 20:49:10 -080061import com.android.internal.util.Preconditions;
Nathan Harolda10003d2017-08-23 13:46:33 -070062
Nathan Harold1afbef42017-03-01 18:55:06 -080063import java.io.FileDescriptor;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070064import java.io.IOException;
Nathan Harold1afbef42017-03-01 18:55:06 -080065import java.io.PrintWriter;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070066import java.net.InetAddress;
67import java.net.InetSocketAddress;
68import java.net.UnknownHostException;
Benedict Wong409c8ca2017-10-26 19:41:43 -070069import java.util.ArrayList;
70import java.util.List;
Nathan Harolda10003d2017-08-23 13:46:33 -070071
Nathan Harold8dc1fd02017-04-04 19:37:48 -070072import libcore.io.IoUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080073
Benedict Wong409c8ca2017-10-26 19:41:43 -070074/**
75 * A service to manage multiple clients that want to access the IpSec API. The service is
76 * responsible for maintaining a list of clients and managing the resources (and related quotas)
77 * that each of them own.
78 *
79 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
80 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
81 * thread is ever running at a time.
82 *
83 * @hide
84 */
Nathan Harold1afbef42017-03-01 18:55:06 -080085public class IpSecService extends IIpSecService.Stub {
86 private static final String TAG = "IpSecService";
87 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Nathan Harold8dc1fd02017-04-04 19:37:48 -070088
Nathan Harold1afbef42017-03-01 18:55:06 -080089 private static final String NETD_SERVICE_NAME = "netd";
Benedict Wong564e2aa2018-05-07 20:06:44 -070090 private static final int[] ADDRESS_FAMILIES =
91 new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
Nathan Harold1afbef42017-03-01 18:55:06 -080092
ludi1a06aa72017-05-12 09:15:00 -070093 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold8dc1fd02017-04-04 19:37:48 -070094 private static final int MAX_PORT_BIND_ATTEMPTS = 10;
95 private static final InetAddress INADDR_ANY;
96
97 static {
98 try {
99 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
100 } catch (UnknownHostException e) {
101 throw new RuntimeException(e);
102 }
103 }
104
105 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
106 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
107
108 /* Binder context for this service */
Nathan Harold1afbef42017-03-01 18:55:06 -0800109 private final Context mContext;
110
Nathan Haroldd8c74292017-12-13 19:16:33 -0800111 /**
Nathan Harolda2523312018-01-05 19:25:13 -0800112 * The next non-repeating global ID for tracking resources between users, this service, and
113 * kernel data structures. Accessing this variable is not thread safe, so it is only read or
114 * modified within blocks synchronized on IpSecService.this. We want to avoid -1
115 * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
Nathan Haroldd8c74292017-12-13 19:16:33 -0800116 */
117 @GuardedBy("IpSecService.this")
118 private int mNextResourceId = 1;
Nathan Harold1afbef42017-03-01 18:55:06 -0800119
ludi1a06aa72017-05-12 09:15:00 -0700120 interface IpSecServiceConfiguration {
121 INetd getNetdInstance() throws RemoteException;
122
123 static IpSecServiceConfiguration GETSRVINSTANCE =
124 new IpSecServiceConfiguration() {
125 @Override
126 public INetd getNetdInstance() throws RemoteException {
127 final INetd netd = NetdService.getInstance();
128 if (netd == null) {
129 throw new RemoteException("Failed to Get Netd Instance");
130 }
131 return netd;
132 }
133 };
134 }
135
136 private final IpSecServiceConfiguration mSrvConfig;
Benedict Wongbabe5d72017-12-03 19:42:36 -0800137 final UidFdTagger mUidFdTagger;
ludi1a06aa72017-05-12 09:15:00 -0700138
Benedict Wong409c8ca2017-10-26 19:41:43 -0700139 /**
140 * Interface for user-reference and kernel-resource cleanup.
141 *
142 * <p>This interface must be implemented for a resource to be reference counted.
143 */
144 @VisibleForTesting
145 public interface IResource {
146 /**
147 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
148 * objects dependent on it.
149 *
150 * <p>Implementations of this method are expected to remove references to the IResource
151 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
152 * the resource is considered invalid for user access or allocation or use in other
153 * resources.
154 *
155 * <p>References to the IResource object may be held by other RefcountedResource objects,
Benedict Wong4f9fb812017-12-13 17:16:53 -0800156 * and as such, the underlying resources and quota may not be cleaned up.
Benedict Wong409c8ca2017-10-26 19:41:43 -0700157 */
158 void invalidate() throws RemoteException;
159
160 /**
161 * Releases underlying resources and related quotas.
162 *
163 * <p>Implementations of this method are expected to remove all system resources that are
164 * tracked by the IResource object. Due to other RefcountedResource objects potentially
Benedict Wong344bd622017-11-16 15:27:22 -0800165 * having references to the IResource object, freeUnderlyingResources may not always be
Benedict Wong409c8ca2017-10-26 19:41:43 -0700166 * called from releaseIfUnreferencedRecursively().
167 */
168 void freeUnderlyingResources() throws RemoteException;
169 }
170
171 /**
172 * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
173 *
174 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
175 * RefcountedResource object creates an explicit reference that must be freed by calling
176 * userRelease(). Additionally, adding this object as a child of another RefcountedResource
177 * object will add an implicit reference.
178 *
179 * <p>Resources are cleaned up when all references, both implicit and explicit, are released
180 * (ie, when userRelease() is called and when all parents have called releaseReference() on this
181 * object.)
182 */
183 @VisibleForTesting
184 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
185 private final T mResource;
186 private final List<RefcountedResource> mChildren;
187 int mRefCount = 1; // starts at 1 for user's reference.
188 IBinder mBinder;
189
190 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
191 synchronized (IpSecService.this) {
192 this.mResource = resource;
193 this.mChildren = new ArrayList<>(children.length);
194 this.mBinder = binder;
195
196 for (RefcountedResource child : children) {
197 mChildren.add(child);
198 child.mRefCount++;
199 }
200
201 try {
202 mBinder.linkToDeath(this, 0);
203 } catch (RemoteException e) {
204 binderDied();
205 }
206 }
207 }
208
209 /**
210 * If the Binder object dies, this function is called to free the system resources that are
Benedict Wong344bd622017-11-16 15:27:22 -0800211 * being tracked by this record and to subsequently release this record for garbage
Benedict Wong409c8ca2017-10-26 19:41:43 -0700212 * collection
213 */
214 @Override
215 public void binderDied() {
216 synchronized (IpSecService.this) {
217 try {
218 userRelease();
219 } catch (Exception e) {
220 Log.e(TAG, "Failed to release resource: " + e);
221 }
222 }
223 }
224
225 public T getResource() {
226 return mResource;
227 }
228
229 /**
230 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
231 * arrays)
232 *
233 * <p>If this method has been previously called, the RefcountedResource's binder field will
234 * be null, and the method will return without performing the cleanup a second time.
235 *
236 * <p>Note that calling this function does not imply that kernel resources will be freed at
237 * this time, or that the related quota will be returned. Such actions will only be
238 * performed upon the reference count reaching zero.
239 */
240 @GuardedBy("IpSecService.this")
241 public void userRelease() throws RemoteException {
242 // Prevent users from putting reference counts into a bad state by calling
243 // userRelease() multiple times.
244 if (mBinder == null) {
245 return;
246 }
247
248 mBinder.unlinkToDeath(this, 0);
249 mBinder = null;
250
251 mResource.invalidate();
252
253 releaseReference();
254 }
255
256 /**
257 * Removes a reference to this resource. If the resultant reference count is zero, the
258 * underlying resources are freed, and references to all child resources are also dropped
259 * recursively (resulting in them freeing their resources and children, etcetera)
260 *
261 * <p>This method also sets the reference count to an invalid value (-1) to signify that it
262 * has been fully released. Any subsequent calls to this method will result in an
263 * IllegalStateException being thrown due to resource already having been previously
264 * released
265 */
266 @VisibleForTesting
267 @GuardedBy("IpSecService.this")
268 public void releaseReference() throws RemoteException {
269 mRefCount--;
270
271 if (mRefCount > 0) {
272 return;
273 } else if (mRefCount < 0) {
274 throw new IllegalStateException(
275 "Invalid operation - resource has already been released.");
276 }
277
278 // Cleanup own resources
279 mResource.freeUnderlyingResources();
280
281 // Cleanup child resources as needed
282 for (RefcountedResource<? extends IResource> child : mChildren) {
283 child.releaseReference();
284 }
285
286 // Enforce that resource cleanup can only be called once
287 // By decrementing the refcount (from 0 to -1), the next call will throw an
288 // IllegalStateException - it has already been released fully.
289 mRefCount--;
290 }
291
292 @Override
293 public String toString() {
294 return new StringBuilder()
295 .append("{mResource=")
296 .append(mResource)
297 .append(", mRefCount=")
298 .append(mRefCount)
299 .append(", mChildren=")
300 .append(mChildren)
301 .append("}")
302 .toString();
303 }
304 }
305
Benedict Wong4f9fb812017-12-13 17:16:53 -0800306 /**
307 * Very simple counting class that looks much like a counting semaphore
308 *
309 * <p>This class is not thread-safe, and expects that that users of this class will ensure
310 * synchronization and thread safety by holding the IpSecService.this instance lock.
311 */
Benedict Wong344bd622017-11-16 15:27:22 -0800312 @VisibleForTesting
313 static class ResourceTracker {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700314 private final int mMax;
315 int mCurrent;
316
317 ResourceTracker(int max) {
318 mMax = max;
319 mCurrent = 0;
320 }
321
Benedict Wong344bd622017-11-16 15:27:22 -0800322 boolean isAvailable() {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700323 return (mCurrent < mMax);
324 }
325
Benedict Wong344bd622017-11-16 15:27:22 -0800326 void take() {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700327 if (!isAvailable()) {
328 Log.wtf(TAG, "Too many resources allocated!");
329 }
330 mCurrent++;
331 }
332
Benedict Wong344bd622017-11-16 15:27:22 -0800333 void give() {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700334 if (mCurrent <= 0) {
335 Log.wtf(TAG, "We've released this resource too many times");
336 }
337 mCurrent--;
338 }
ludi3e5ea232017-08-10 15:44:40 -0700339
340 @Override
341 public String toString() {
342 return new StringBuilder()
343 .append("{mCurrent=")
344 .append(mCurrent)
345 .append(", mMax=")
346 .append(mMax)
347 .append("}")
348 .toString();
349 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700350 }
351
Benedict Wong344bd622017-11-16 15:27:22 -0800352 @VisibleForTesting
353 static final class UserRecord {
Benedict Wong344bd622017-11-16 15:27:22 -0800354 /* Maximum number of each type of resource that a single UID may possess */
Benedict Wong8149f6e2018-01-18 18:31:45 -0800355 public static final int MAX_NUM_TUNNEL_INTERFACES = 2;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700356 public static final int MAX_NUM_ENCAP_SOCKETS = 2;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700357 public static final int MAX_NUM_TRANSFORMS = 4;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700358 public static final int MAX_NUM_SPIS = 8;
359
Benedict Wong4f9fb812017-12-13 17:16:53 -0800360 /**
361 * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
362 * and explicit (user) reference management.
363 *
364 * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
365 *
366 * <p>Resources are removed from this array when the user releases their explicit reference
367 * by calling one of the releaseResource() methods.
368 */
Benedict Wong344bd622017-11-16 15:27:22 -0800369 final RefcountedResourceArray<SpiRecord> mSpiRecords =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800370 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
Benedict Wong344bd622017-11-16 15:27:22 -0800371 final RefcountedResourceArray<TransformRecord> mTransformRecords =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800372 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
Benedict Wong344bd622017-11-16 15:27:22 -0800373 final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800374 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
Benedict Wong8149f6e2018-01-18 18:31:45 -0800375 final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
376 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
Benedict Wong4f9fb812017-12-13 17:16:53 -0800377
378 /**
379 * Trackers for quotas for each of the OwnedResource types.
380 *
381 * <p>These trackers are separate from the resource arrays, since they are incremented and
382 * decremented at different points in time. Specifically, quota is only returned upon final
383 * resource deallocation (after all explicit and implicit references are released). Note
384 * that it is possible that calls to releaseResource() will not return the used quota if
385 * there are other resources that depend on (are parents of) the resource being released.
386 */
387 final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
388 final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
Benedict Wong344bd622017-11-16 15:27:22 -0800389 final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
Benedict Wong8149f6e2018-01-18 18:31:45 -0800390 final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
Benedict Wong344bd622017-11-16 15:27:22 -0800391
392 void removeSpiRecord(int resourceId) {
393 mSpiRecords.remove(resourceId);
Nathan Harolda1afbd82017-04-24 16:16:34 -0700394 }
395
Benedict Wong344bd622017-11-16 15:27:22 -0800396 void removeTransformRecord(int resourceId) {
397 mTransformRecords.remove(resourceId);
398 }
399
Benedict Wong8149f6e2018-01-18 18:31:45 -0800400 void removeTunnelInterfaceRecord(int resourceId) {
401 mTunnelInterfaceRecords.remove(resourceId);
402 }
403
Benedict Wong344bd622017-11-16 15:27:22 -0800404 void removeEncapSocketRecord(int resourceId) {
405 mEncapSocketRecords.remove(resourceId);
406 }
407
408 @Override
409 public String toString() {
410 return new StringBuilder()
411 .append("{mSpiQuotaTracker=")
412 .append(mSpiQuotaTracker)
413 .append(", mTransformQuotaTracker=")
414 .append(mTransformQuotaTracker)
415 .append(", mSocketQuotaTracker=")
416 .append(mSocketQuotaTracker)
Benedict Wongb8ef5412018-01-24 15:31:39 -0800417 .append(", mTunnelQuotaTracker=")
418 .append(mTunnelQuotaTracker)
Benedict Wong344bd622017-11-16 15:27:22 -0800419 .append(", mSpiRecords=")
420 .append(mSpiRecords)
421 .append(", mTransformRecords=")
422 .append(mTransformRecords)
423 .append(", mEncapSocketRecords=")
424 .append(mEncapSocketRecords)
Benedict Wongb8ef5412018-01-24 15:31:39 -0800425 .append(", mTunnelInterfaceRecords=")
426 .append(mTunnelInterfaceRecords)
Benedict Wong344bd622017-11-16 15:27:22 -0800427 .append("}")
428 .toString();
429 }
430 }
431
Benedict Wong4f9fb812017-12-13 17:16:53 -0800432 /**
433 * This class is not thread-safe, and expects that that users of this class will ensure
434 * synchronization and thread safety by holding the IpSecService.this instance lock.
435 */
Benedict Wong344bd622017-11-16 15:27:22 -0800436 @VisibleForTesting
437 static final class UserResourceTracker {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700438 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
439
Benedict Wong4f9fb812017-12-13 17:16:53 -0800440 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
Benedict Wong344bd622017-11-16 15:27:22 -0800441 public UserRecord getUserRecord(int uid) {
442 checkCallerUid(uid);
443
Nathan Harolda1afbd82017-04-24 16:16:34 -0700444 UserRecord r = mUserRecords.get(uid);
445 if (r == null) {
446 r = new UserRecord();
447 mUserRecords.put(uid, r);
448 }
449 return r;
450 }
ludi3e5ea232017-08-10 15:44:40 -0700451
Benedict Wong344bd622017-11-16 15:27:22 -0800452 /** Safety method; guards against access of other user's UserRecords */
453 private void checkCallerUid(int uid) {
454 if (uid != Binder.getCallingUid()
455 && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
456 throw new SecurityException("Attempted access of unowned resources");
457 }
458 }
459
ludi3e5ea232017-08-10 15:44:40 -0700460 @Override
461 public String toString() {
462 return mUserRecords.toString();
463 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700464 }
465
Benedict Wong344bd622017-11-16 15:27:22 -0800466 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700467
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700468 /**
Benedict Wong4f9fb812017-12-13 17:16:53 -0800469 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
Benedict Wong344bd622017-11-16 15:27:22 -0800470 * resources. It relies on a provided resourceId that should uniquely identify the kernel
471 * resource. To use this class, the user should implement the invalidate() and
472 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
Benedict Wong4f9fb812017-12-13 17:16:53 -0800473 * tracking arrays and kernel resources, respectively.
474 *
475 * <p>This class associates kernel resources with the UID that owns and controls them.
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700476 */
Benedict Wong4f9fb812017-12-13 17:16:53 -0800477 private abstract class OwnedResourceRecord implements IResource {
Nathan Harold93962f32017-03-07 13:23:36 -0800478 final int pid;
479 final int uid;
Benedict Wong344bd622017-11-16 15:27:22 -0800480 protected final int mResourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800481
Benedict Wong4f9fb812017-12-13 17:16:53 -0800482 OwnedResourceRecord(int resourceId) {
Nathan Harold93962f32017-03-07 13:23:36 -0800483 super();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700484 if (resourceId == INVALID_RESOURCE_ID) {
485 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
486 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700487 mResourceId = resourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800488 pid = Binder.getCallingPid();
489 uid = Binder.getCallingUid();
490
Nathan Harolda1afbd82017-04-24 16:16:34 -0700491 getResourceTracker().take();
Nathan Harold93962f32017-03-07 13:23:36 -0800492 }
493
Benedict Wong344bd622017-11-16 15:27:22 -0800494 @Override
495 public abstract void invalidate() throws RemoteException;
496
497 /** Convenience method; retrieves the user resource record for the stored UID. */
498 protected UserRecord getUserRecord() {
499 return mUserResourceTracker.getUserRecord(uid);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700500 }
501
Benedict Wong344bd622017-11-16 15:27:22 -0800502 @Override
503 public abstract void freeUnderlyingResources() throws RemoteException;
ludib0c95b12017-05-22 10:52:23 -0700504
Nathan Harolda1afbd82017-04-24 16:16:34 -0700505 /** Get the resource tracker for this resource */
506 protected abstract ResourceTracker getResourceTracker();
507
ludib0c95b12017-05-22 10:52:23 -0700508 @Override
509 public String toString() {
510 return new StringBuilder()
511 .append("{mResourceId=")
512 .append(mResourceId)
513 .append(", pid=")
514 .append(pid)
515 .append(", uid=")
516 .append(uid)
ludib0c95b12017-05-22 10:52:23 -0700517 .append("}")
518 .toString();
519 }
Nathan Harold93962f32017-03-07 13:23:36 -0800520 };
521
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700522 /**
Benedict Wong344bd622017-11-16 15:27:22 -0800523 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
524 *
525 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
526 * if a key is not found during a retrieval process.
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700527 */
Benedict Wong344bd622017-11-16 15:27:22 -0800528 static class RefcountedResourceArray<T extends IResource> {
529 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
530 private final String mTypeName;
Nathan Harold93962f32017-03-07 13:23:36 -0800531
Benedict Wong344bd622017-11-16 15:27:22 -0800532 public RefcountedResourceArray(String typeName) {
533 this.mTypeName = typeName;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700534 }
535
Benedict Wong344bd622017-11-16 15:27:22 -0800536 /**
537 * Accessor method to get inner resource object.
538 *
539 * @throws IllegalArgumentException if no resource with provided key is found.
540 */
541 T getResourceOrThrow(int key) {
542 return getRefcountedResourceOrThrow(key).getResource();
543 }
544
545 /**
546 * Accessor method to get reference counting wrapper.
547 *
548 * @throws IllegalArgumentException if no resource with provided key is found.
549 */
550 RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
551 RefcountedResource<T> resource = mArray.get(key);
552 if (resource == null) {
553 throw new IllegalArgumentException(
554 String.format("No such %s found for given id: %d", mTypeName, key));
555 }
556
557 return resource;
558 }
559
560 void put(int key, RefcountedResource<T> obj) {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700561 checkNotNull(obj, "Null resources cannot be added");
562 mArray.put(key, obj);
563 }
564
565 void remove(int key) {
566 mArray.remove(key);
567 }
ludib0c95b12017-05-22 10:52:23 -0700568
569 @Override
570 public String toString() {
571 return mArray.toString();
572 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700573 }
574
Benedict Wong4f9fb812017-12-13 17:16:53 -0800575 /**
576 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
577 * created, the SpiRecord that originally tracked the SAs will reliquish the
578 * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
579 */
580 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700581 private final IpSecConfig mConfig;
Nathan Harolda2523312018-01-05 19:25:13 -0800582 private final SpiRecord mSpi;
Benedict Wong344bd622017-11-16 15:27:22 -0800583 private final EncapSocketRecord mSocket;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700584
585 TransformRecord(
Nathan Harolda2523312018-01-05 19:25:13 -0800586 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong344bd622017-11-16 15:27:22 -0800587 super(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -0800588 mConfig = config;
Nathan Harolda2523312018-01-05 19:25:13 -0800589 mSpi = spi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700590 mSocket = socket;
Benedict Wonge6b42772017-12-13 18:26:40 -0800591
592 spi.setOwnedByTransform();
Nathan Harold93962f32017-03-07 13:23:36 -0800593 }
594
595 public IpSecConfig getConfig() {
596 return mConfig;
597 }
598
Nathan Harolda2523312018-01-05 19:25:13 -0800599 public SpiRecord getSpiRecord() {
600 return mSpi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700601 }
602
Benedict Wong0fe58a92018-01-19 17:36:02 -0800603 public EncapSocketRecord getSocketRecord() {
604 return mSocket;
605 }
606
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700607 /** always guarded by IpSecService#this */
Nathan Harold93962f32017-03-07 13:23:36 -0800608 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800609 public void freeUnderlyingResources() {
Nathan Harolda2523312018-01-05 19:25:13 -0800610 int spi = mSpi.getSpi();
611 try {
612 mSrvConfig
613 .getNetdInstance()
614 .ipSecDeleteSecurityAssociation(
Benedict Wonga0989622018-07-25 13:06:29 -0700615 uid,
Nathan Harolda2523312018-01-05 19:25:13 -0800616 mConfig.getSourceAddress(),
617 mConfig.getDestinationAddress(),
Di Lu0b611f42018-01-11 11:35:25 -0800618 spi,
619 mConfig.getMarkValue(),
620 mConfig.getMarkMask());
Benedict Wongecc9f7c2018-03-01 18:53:07 -0800621 } catch (RemoteException | ServiceSpecificException e) {
622 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
Nathan Harold93962f32017-03-07 13:23:36 -0800623 }
Nathan Harold93962f32017-03-07 13:23:36 -0800624
Benedict Wong344bd622017-11-16 15:27:22 -0800625 getResourceTracker().give();
Nathan Harold93962f32017-03-07 13:23:36 -0800626 }
ludib0c95b12017-05-22 10:52:23 -0700627
Benedict Wong344bd622017-11-16 15:27:22 -0800628 @Override
629 public void invalidate() throws RemoteException {
630 getUserRecord().removeTransformRecord(mResourceId);
631 }
632
633 @Override
Nathan Harolda1afbd82017-04-24 16:16:34 -0700634 protected ResourceTracker getResourceTracker() {
Benedict Wong344bd622017-11-16 15:27:22 -0800635 return getUserRecord().mTransformQuotaTracker;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700636 }
637
ludib0c95b12017-05-22 10:52:23 -0700638 @Override
639 public String toString() {
640 StringBuilder strBuilder = new StringBuilder();
641 strBuilder
642 .append("{super=")
643 .append(super.toString())
644 .append(", mSocket=")
645 .append(mSocket)
Nathan Harolda2523312018-01-05 19:25:13 -0800646 .append(", mSpi.mResourceId=")
647 .append(mSpi.mResourceId)
ludib0c95b12017-05-22 10:52:23 -0700648 .append(", mConfig=")
649 .append(mConfig)
650 .append("}");
651 return strBuilder.toString();
652 }
Nathan Harold93962f32017-03-07 13:23:36 -0800653 }
654
Benedict Wong4f9fb812017-12-13 17:16:53 -0800655 /**
656 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
657 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
658 * object
659 */
660 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harolda2523312018-01-05 19:25:13 -0800661 private final String mSourceAddress;
662 private final String mDestinationAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800663 private int mSpi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700664
665 private boolean mOwnedByTransform = false;
Nathan Harold93962f32017-03-07 13:23:36 -0800666
Nathan Harolda2523312018-01-05 19:25:13 -0800667 SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
Benedict Wong344bd622017-11-16 15:27:22 -0800668 super(resourceId);
Nathan Harolda2523312018-01-05 19:25:13 -0800669 mSourceAddress = sourceAddress;
670 mDestinationAddress = destinationAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800671 mSpi = spi;
Nathan Harold93962f32017-03-07 13:23:36 -0800672 }
673
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700674 /** always guarded by IpSecService#this */
675 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800676 public void freeUnderlyingResources() {
Nathan Harold93962f32017-03-07 13:23:36 -0800677 try {
Nathan Harold22795302018-02-27 19:19:40 -0800678 if (!mOwnedByTransform) {
679 mSrvConfig
680 .getNetdInstance()
681 .ipSecDeleteSecurityAssociation(
Benedict Wonga0989622018-07-25 13:06:29 -0700682 uid, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
Nathan Harold22795302018-02-27 19:19:40 -0800683 }
Benedict Wongecc9f7c2018-03-01 18:53:07 -0800684 } catch (ServiceSpecificException | RemoteException e) {
685 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
Nathan Harold93962f32017-03-07 13:23:36 -0800686 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700687
688 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -0800689
Benedict Wong344bd622017-11-16 15:27:22 -0800690 getResourceTracker().give();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700691 }
692
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700693 public int getSpi() {
694 return mSpi;
695 }
696
Nathan Harolda2523312018-01-05 19:25:13 -0800697 public String getDestinationAddress() {
698 return mDestinationAddress;
699 }
700
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700701 public void setOwnedByTransform() {
702 if (mOwnedByTransform) {
703 // Programming error
Andreas Gamped6d8e452017-07-11 10:25:09 -0700704 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700705 }
706
707 mOwnedByTransform = true;
Nathan Harold93962f32017-03-07 13:23:36 -0800708 }
ludib0c95b12017-05-22 10:52:23 -0700709
Benedict Wonge6b42772017-12-13 18:26:40 -0800710 public boolean getOwnedByTransform() {
711 return mOwnedByTransform;
712 }
713
ludib0c95b12017-05-22 10:52:23 -0700714 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800715 public void invalidate() throws RemoteException {
716 getUserRecord().removeSpiRecord(mResourceId);
717 }
718
719 @Override
720 protected ResourceTracker getResourceTracker() {
721 return getUserRecord().mSpiQuotaTracker;
722 }
723
724 @Override
ludib0c95b12017-05-22 10:52:23 -0700725 public String toString() {
726 StringBuilder strBuilder = new StringBuilder();
727 strBuilder
728 .append("{super=")
729 .append(super.toString())
730 .append(", mSpi=")
731 .append(mSpi)
Nathan Harolda2523312018-01-05 19:25:13 -0800732 .append(", mSourceAddress=")
733 .append(mSourceAddress)
734 .append(", mDestinationAddress=")
735 .append(mDestinationAddress)
ludib0c95b12017-05-22 10:52:23 -0700736 .append(", mOwnedByTransform=")
737 .append(mOwnedByTransform)
738 .append("}");
739 return strBuilder.toString();
740 }
Nathan Harold93962f32017-03-07 13:23:36 -0800741 }
742
Benedict Wong8149f6e2018-01-18 18:31:45 -0800743 // These values have been reserved in ConnectivityService
744 @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
745
746 @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400;
747
748 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
749 private int mNextTunnelNetIdIndex = 0;
750
751 /**
752 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
753 *
754 * <p>This method should only be called from Binder threads. Do not call this from within the
755 * system server as it will crash the system on failure.
756 *
757 * @return an integer key within the netId range, if successful
758 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
759 */
760 @VisibleForTesting
761 int reserveNetId() {
762 synchronized (mTunnelNetIds) {
763 for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
764 int index = mNextTunnelNetIdIndex;
765 int netId = index + TUN_INTF_NETID_START;
766 if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
767 if (!mTunnelNetIds.get(netId)) {
768 mTunnelNetIds.put(netId, true);
769 return netId;
770 }
771 }
772 }
773 throw new IllegalStateException("No free netIds to allocate");
774 }
775
776 @VisibleForTesting
777 void releaseNetId(int netId) {
778 synchronized (mTunnelNetIds) {
779 mTunnelNetIds.delete(netId);
780 }
781 }
782
783 private final class TunnelInterfaceRecord extends OwnedResourceRecord {
784 private final String mInterfaceName;
785 private final Network mUnderlyingNetwork;
786
787 // outer addresses
788 private final String mLocalAddress;
789 private final String mRemoteAddress;
790
791 private final int mIkey;
792 private final int mOkey;
793
794 TunnelInterfaceRecord(
795 int resourceId,
796 String interfaceName,
797 Network underlyingNetwork,
798 String localAddr,
799 String remoteAddr,
800 int ikey,
801 int okey) {
802 super(resourceId);
803
804 mInterfaceName = interfaceName;
805 mUnderlyingNetwork = underlyingNetwork;
806 mLocalAddress = localAddr;
807 mRemoteAddress = remoteAddr;
808 mIkey = ikey;
809 mOkey = okey;
810 }
811
812 /** always guarded by IpSecService#this */
813 @Override
814 public void freeUnderlyingResources() {
Benedict Wong0fe58a92018-01-19 17:36:02 -0800815 // Calls to netd
Benedict Wong8149f6e2018-01-18 18:31:45 -0800816 // Teardown VTI
817 // Delete global policies
Benedict Wong0fe58a92018-01-19 17:36:02 -0800818 try {
Benedict Wong564e2aa2018-05-07 20:06:44 -0700819 final INetd netd = mSrvConfig.getNetdInstance();
820 netd.removeVirtualTunnelInterface(mInterfaceName);
Benedict Wong0fe58a92018-01-19 17:36:02 -0800821
Benedict Wong564e2aa2018-05-07 20:06:44 -0700822 for (int selAddrFamily : ADDRESS_FAMILIES) {
823 netd.ipSecDeleteSecurityPolicy(
Benedict Wonga0989622018-07-25 13:06:29 -0700824 uid,
Benedict Wong564e2aa2018-05-07 20:06:44 -0700825 selAddrFamily,
826 IpSecManager.DIRECTION_OUT,
827 mOkey,
828 0xffffffff);
829 netd.ipSecDeleteSecurityPolicy(
Benedict Wonga0989622018-07-25 13:06:29 -0700830 uid,
Benedict Wong564e2aa2018-05-07 20:06:44 -0700831 selAddrFamily,
832 IpSecManager.DIRECTION_IN,
833 mIkey,
834 0xffffffff);
Benedict Wong0fe58a92018-01-19 17:36:02 -0800835 }
Benedict Wongecc9f7c2018-03-01 18:53:07 -0800836 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong0fe58a92018-01-19 17:36:02 -0800837 Log.e(
838 TAG,
839 "Failed to delete VTI with interface name: "
840 + mInterfaceName
841 + " and id: "
Benedict Wongecc9f7c2018-03-01 18:53:07 -0800842 + mResourceId, e);
Benedict Wong0fe58a92018-01-19 17:36:02 -0800843 }
Benedict Wong8149f6e2018-01-18 18:31:45 -0800844
845 getResourceTracker().give();
846 releaseNetId(mIkey);
847 releaseNetId(mOkey);
848 }
849
850 public String getInterfaceName() {
851 return mInterfaceName;
852 }
853
854 public Network getUnderlyingNetwork() {
855 return mUnderlyingNetwork;
856 }
857
858 /** Returns the local, outer address for the tunnelInterface */
859 public String getLocalAddress() {
860 return mLocalAddress;
861 }
862
863 /** Returns the remote, outer address for the tunnelInterface */
864 public String getRemoteAddress() {
865 return mRemoteAddress;
866 }
867
868 public int getIkey() {
869 return mIkey;
870 }
871
872 public int getOkey() {
873 return mOkey;
874 }
875
876 @Override
877 protected ResourceTracker getResourceTracker() {
878 return getUserRecord().mTunnelQuotaTracker;
879 }
880
881 @Override
882 public void invalidate() {
883 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
884 }
885
886 @Override
887 public String toString() {
888 return new StringBuilder()
889 .append("{super=")
890 .append(super.toString())
891 .append(", mInterfaceName=")
892 .append(mInterfaceName)
893 .append(", mUnderlyingNetwork=")
894 .append(mUnderlyingNetwork)
895 .append(", mLocalAddress=")
896 .append(mLocalAddress)
897 .append(", mRemoteAddress=")
898 .append(mRemoteAddress)
899 .append(", mIkey=")
900 .append(mIkey)
901 .append(", mOkey=")
902 .append(mOkey)
903 .append("}")
904 .toString();
905 }
906 }
907
Benedict Wong4f9fb812017-12-13 17:16:53 -0800908 /**
909 * Tracks a UDP encap socket, and manages cleanup paths
910 *
911 * <p>While this class does not manage non-kernel resources, race conditions around socket
912 * binding require that the service creates the encap socket, binds it and applies the socket
913 * policy before handing it to a user.
914 */
915 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700916 private FileDescriptor mSocket;
917 private final int mPort;
Nathan Harold93962f32017-03-07 13:23:36 -0800918
Benedict Wong344bd622017-11-16 15:27:22 -0800919 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
920 super(resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700921 mSocket = socket;
922 mPort = port;
923 }
924
925 /** always guarded by IpSecService#this */
926 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800927 public void freeUnderlyingResources() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700928 Log.d(TAG, "Closing port " + mPort);
929 IoUtils.closeQuietly(mSocket);
930 mSocket = null;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700931
Benedict Wong344bd622017-11-16 15:27:22 -0800932 getResourceTracker().give();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700933 }
934
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700935 public int getPort() {
936 return mPort;
937 }
938
Benedict Wong4c987eb2018-03-27 16:55:48 -0700939 public FileDescriptor getFileDescriptor() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700940 return mSocket;
941 }
ludib0c95b12017-05-22 10:52:23 -0700942
943 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800944 protected ResourceTracker getResourceTracker() {
945 return getUserRecord().mSocketQuotaTracker;
946 }
947
948 @Override
949 public void invalidate() {
950 getUserRecord().removeEncapSocketRecord(mResourceId);
951 }
952
953 @Override
ludib0c95b12017-05-22 10:52:23 -0700954 public String toString() {
955 return new StringBuilder()
956 .append("{super=")
957 .append(super.toString())
958 .append(", mSocket=")
959 .append(mSocket)
960 .append(", mPort=")
961 .append(mPort)
962 .append("}")
963 .toString();
964 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700965 }
Nathan Harold93962f32017-03-07 13:23:36 -0800966
Nathan Harold1afbef42017-03-01 18:55:06 -0800967 /**
968 * Constructs a new IpSecService instance
969 *
970 * @param context Binder context for this service
971 */
972 private IpSecService(Context context) {
ludi1a06aa72017-05-12 09:15:00 -0700973 this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
Nathan Harold1afbef42017-03-01 18:55:06 -0800974 }
975
976 static IpSecService create(Context context) throws InterruptedException {
977 final IpSecService service = new IpSecService(context);
978 service.connectNativeNetdService();
979 return service;
980 }
981
Nathan Harold21208ee2018-03-15 18:06:06 -0700982 @NonNull
983 private AppOpsManager getAppOpsManager() {
984 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
985 if(appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
986 return appOps;
987 }
988
ludi1a06aa72017-05-12 09:15:00 -0700989 /** @hide */
990 @VisibleForTesting
991 public IpSecService(Context context, IpSecServiceConfiguration config) {
Nathan Harolda2523312018-01-05 19:25:13 -0800992 this(
993 context,
994 config,
995 (fd, uid) -> {
996 try {
997 TrafficStats.setThreadStatsUid(uid);
998 TrafficStats.tagFileDescriptor(fd);
999 } finally {
1000 TrafficStats.clearThreadStatsUid();
1001 }
1002 });
Benedict Wongbabe5d72017-12-03 19:42:36 -08001003 }
1004
1005 /** @hide */
1006 @VisibleForTesting
1007 public IpSecService(
1008 Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
ludi1a06aa72017-05-12 09:15:00 -07001009 mContext = context;
1010 mSrvConfig = config;
Benedict Wongbabe5d72017-12-03 19:42:36 -08001011 mUidFdTagger = uidFdTagger;
ludi1a06aa72017-05-12 09:15:00 -07001012 }
1013
Nathan Harold1afbef42017-03-01 18:55:06 -08001014 public void systemReady() {
1015 if (isNetdAlive()) {
1016 Slog.d(TAG, "IpSecService is ready");
1017 } else {
1018 Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
1019 }
1020 }
1021
1022 private void connectNativeNetdService() {
1023 // Avoid blocking the system server to do this
Nathan Haroldb0e05082017-07-17 14:01:53 -07001024 new Thread() {
1025 @Override
1026 public void run() {
1027 synchronized (IpSecService.this) {
ludi1a06aa72017-05-12 09:15:00 -07001028 NetdService.get(NETD_FETCH_TIMEOUT_MS);
Nathan Haroldb0e05082017-07-17 14:01:53 -07001029 }
1030 }
1031 }.start();
Nathan Harold1afbef42017-03-01 18:55:06 -08001032 }
1033
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001034 synchronized boolean isNetdAlive() {
1035 try {
ludi1a06aa72017-05-12 09:15:00 -07001036 final INetd netd = mSrvConfig.getNetdInstance();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001037 if (netd == null) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001038 return false;
1039 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001040 return netd.isAlive();
1041 } catch (RemoteException re) {
1042 return false;
Nathan Harold1afbef42017-03-01 18:55:06 -08001043 }
1044 }
1045
Nathan Harolda10003d2017-08-23 13:46:33 -07001046 /**
1047 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1048 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1049 */
1050 private static void checkInetAddress(String inetAddress) {
1051 if (TextUtils.isEmpty(inetAddress)) {
1052 throw new IllegalArgumentException("Unspecified address");
1053 }
1054
1055 InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
1056
1057 if (checkAddr.isAnyLocalAddress()) {
1058 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1059 }
1060 }
1061
1062 /**
1063 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1064 * DIRECTION_IN or DIRECTION_OUT
1065 */
1066 private static void checkDirection(int direction) {
1067 switch (direction) {
Nathan Harolda2523312018-01-05 19:25:13 -08001068 case IpSecManager.DIRECTION_OUT:
1069 case IpSecManager.DIRECTION_IN:
Nathan Harolda10003d2017-08-23 13:46:33 -07001070 return;
1071 }
1072 throw new IllegalArgumentException("Invalid Direction: " + direction);
1073 }
1074
Nathan Harold93962f32017-03-07 13:23:36 -08001075 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001076 @Override
1077 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harolda2523312018-01-05 19:25:13 -08001078 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1079 checkInetAddress(destinationAddress);
Nathan Harold7f606ee2018-03-28 08:52:51 -07001080 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1081 if (requestedSpi > 0 && requestedSpi < 256) {
1082 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1083 }
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001084 checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harolda10003d2017-08-23 13:46:33 -07001085
Benedict Wonga0989622018-07-25 13:06:29 -07001086 int callingUid = Binder.getCallingUid();
1087 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldd8c74292017-12-13 19:16:33 -08001088 final int resourceId = mNextResourceId++;
Nathan Harold93962f32017-03-07 13:23:36 -08001089
1090 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -08001091 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001092 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001093 return new IpSecSpiResponse(
Nathan Harolda10003d2017-08-23 13:46:33 -07001094 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harolda1afbd82017-04-24 16:16:34 -07001095 }
Nathan Harolda2523312018-01-05 19:25:13 -08001096
Nathan Harold93962f32017-03-07 13:23:36 -08001097 spi =
ludi1a06aa72017-05-12 09:15:00 -07001098 mSrvConfig
1099 .getNetdInstance()
Benedict Wonga0989622018-07-25 13:06:29 -07001100 .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold93962f32017-03-07 13:23:36 -08001101 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong344bd622017-11-16 15:27:22 -08001102 userRecord.mSpiRecords.put(
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001103 resourceId,
Benedict Wong344bd622017-11-16 15:27:22 -08001104 new RefcountedResource<SpiRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001105 new SpiRecord(resourceId, "", destinationAddress, spi), binder));
Nathan Harold93962f32017-03-07 13:23:36 -08001106 } catch (ServiceSpecificException e) {
Nathan Harold144ce0a2018-04-03 16:13:19 -07001107 if (e.errorCode == OsConstants.ENOENT) {
1108 return new IpSecSpiResponse(
1109 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1110 }
1111 throw e;
Nathan Harold93962f32017-03-07 13:23:36 -08001112 } catch (RemoteException e) {
1113 throw e.rethrowFromSystemServer();
1114 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001115 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1116 }
1117
1118 /* This method should only be called from Binder threads. Do not call this from
1119 * within the system server as it will crash the system on failure.
1120 */
Benedict Wong344bd622017-11-16 15:27:22 -08001121 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001122 throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001123 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold93962f32017-03-07 13:23:36 -08001124 }
1125
1126 /** Release a previously allocated SPI that has been registered with the system server */
1127 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001128 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1129 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1130 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001131 }
1132
1133 /**
1134 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1135 * be unbound.
1136 *
1137 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1138 * a random open port and then bind by number, this function creates a temp socket, binds to a
1139 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1140 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1141 * FileHandle.
1142 *
1143 * <p>The loop in this function handles the inherent race window between un-binding to a port
1144 * and re-binding, during which the system could *technically* hand that port out to someone
1145 * else.
1146 */
Benedict Wongf186d672017-10-10 20:44:28 -07001147 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001148 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1149 try {
1150 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1151 Os.bind(probeSocket, INADDR_ANY, 0);
1152 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1153 Os.close(probeSocket);
1154 Log.v(TAG, "Binding to port " + port);
1155 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongf186d672017-10-10 20:44:28 -07001156 return port;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001157 } catch (ErrnoException e) {
1158 // Someone miraculously claimed the port just after we closed probeSocket.
1159 if (e.errno == OsConstants.EADDRINUSE) {
1160 continue;
1161 }
1162 throw e.rethrowAsIOException();
1163 }
1164 }
1165 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1166 }
Nathan Harold93962f32017-03-07 13:23:36 -08001167
1168 /**
Benedict Wongbabe5d72017-12-03 19:42:36 -08001169 * Functional interface to do traffic tagging of given sockets to UIDs.
1170 *
1171 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1172 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1173 *
1174 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1175 * methods that cannot be easily mocked/tested.
1176 */
1177 @VisibleForTesting
1178 public interface UidFdTagger {
1179 /**
1180 * Sets socket tag to assign all traffic to the provided UID.
1181 *
1182 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1183 * should be accounted to the UID of the unprivileged application.
1184 */
1185 public void tag(FileDescriptor fd, int uid) throws IOException;
1186 }
1187
1188 /**
Nathan Harold93962f32017-03-07 13:23:36 -08001189 * Open a socket via the system server and bind it to the specified port (random if port=0).
1190 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1191 * cache the socket and a record of its owner so that it can and must be freed when no longer
1192 * needed.
1193 */
1194 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001195 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1196 throws RemoteException {
1197 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1198 throw new IllegalArgumentException(
1199 "Specified port number must be a valid non-reserved UDP port");
1200 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001201 checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
1202
Benedict Wongbabe5d72017-12-03 19:42:36 -08001203 int callingUid = Binder.getCallingUid();
1204 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldd8c74292017-12-13 19:16:33 -08001205 final int resourceId = mNextResourceId++;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001206 FileDescriptor sockFd = null;
1207 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001208 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001209 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1210 }
1211
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001212 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Benedict Wongbabe5d72017-12-03 19:42:36 -08001213 mUidFdTagger.tag(sockFd, callingUid);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001214
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001215 // This code is common to both the unspecified and specified port cases
1216 Os.setsockoptInt(
1217 sockFd,
1218 OsConstants.IPPROTO_UDP,
1219 OsConstants.UDP_ENCAP,
1220 OsConstants.UDP_ENCAP_ESPINUDP);
1221
Benedict Wongba8d3132017-12-06 21:56:35 -08001222 mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
1223 if (port != 0) {
1224 Log.v(TAG, "Binding to port " + port);
1225 Os.bind(sockFd, INADDR_ANY, port);
1226 } else {
1227 port = bindToRandomPort(sockFd);
1228 }
1229
Benedict Wong344bd622017-11-16 15:27:22 -08001230 userRecord.mEncapSocketRecords.put(
1231 resourceId,
1232 new RefcountedResource<EncapSocketRecord>(
1233 new EncapSocketRecord(resourceId, sockFd, port), binder));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001234 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
1235 } catch (IOException | ErrnoException e) {
1236 IoUtils.closeQuietly(sockFd);
1237 }
1238 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1239 // The only reasonable condition that would cause that is resource unavailable.
1240 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001241 }
1242
1243 /** close a socket that has been been allocated by and registered with the system server */
1244 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001245 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1246 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1247 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001248 }
Nathan Harold93962f32017-03-07 13:23:36 -08001249
Benedict Wong8149f6e2018-01-18 18:31:45 -08001250 /**
1251 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1252 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1253 * needed.
1254 */
1255 @Override
1256 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold21208ee2018-03-15 18:06:06 -07001257 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1258 String callingPackage) {
1259 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001260 checkNotNull(binder, "Null Binder passed to createTunnelInterface");
1261 checkNotNull(underlyingNetwork, "No underlying network was specified");
1262 checkInetAddress(localAddr);
1263 checkInetAddress(remoteAddr);
1264
1265 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1266 // network (b/72316676).
1267
Benedict Wonga0989622018-07-25 13:06:29 -07001268 int callerUid = Binder.getCallingUid();
1269 UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001270 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1271 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1272 }
1273
1274 final int resourceId = mNextResourceId++;
1275 final int ikey = reserveNetId();
1276 final int okey = reserveNetId();
Nathan Harold4fd7c322018-04-26 11:47:14 -07001277 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001278
Benedict Wong0fe58a92018-01-19 17:36:02 -08001279 try {
1280 // Calls to netd:
1281 // Create VTI
1282 // Add inbound/outbound global policies
1283 // (use reqid = 0)
Benedict Wong564e2aa2018-05-07 20:06:44 -07001284 final INetd netd = mSrvConfig.getNetdInstance();
1285 netd.addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001286
Benedict Wong564e2aa2018-05-07 20:06:44 -07001287 for (int selAddrFamily : ADDRESS_FAMILIES) {
1288 // Always send down correct local/remote addresses for template.
1289 netd.ipSecAddSecurityPolicy(
Benedict Wonga0989622018-07-25 13:06:29 -07001290 callerUid,
Benedict Wong564e2aa2018-05-07 20:06:44 -07001291 selAddrFamily,
1292 IpSecManager.DIRECTION_OUT,
1293 localAddr,
1294 remoteAddr,
1295 0,
1296 okey,
1297 0xffffffff);
1298 netd.ipSecAddSecurityPolicy(
Benedict Wonga0989622018-07-25 13:06:29 -07001299 callerUid,
Benedict Wong564e2aa2018-05-07 20:06:44 -07001300 selAddrFamily,
1301 IpSecManager.DIRECTION_IN,
1302 remoteAddr,
1303 localAddr,
1304 0,
1305 ikey,
1306 0xffffffff);
Benedict Wong0fe58a92018-01-19 17:36:02 -08001307 }
1308
1309 userRecord.mTunnelInterfaceRecords.put(
1310 resourceId,
1311 new RefcountedResource<TunnelInterfaceRecord>(
1312 new TunnelInterfaceRecord(
1313 resourceId,
1314 intfName,
1315 underlyingNetwork,
1316 localAddr,
1317 remoteAddr,
1318 ikey,
1319 okey),
1320 binder));
1321 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1322 } catch (RemoteException e) {
1323 // Release keys if we got an error.
1324 releaseNetId(ikey);
1325 releaseNetId(okey);
1326 throw e.rethrowFromSystemServer();
Nathan Harold144ce0a2018-04-03 16:13:19 -07001327 } catch (Throwable t) {
1328 // Release keys if we got an error.
1329 releaseNetId(ikey);
1330 releaseNetId(okey);
1331 throw t;
Benedict Wong0fe58a92018-01-19 17:36:02 -08001332 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001333 }
1334
1335 /**
1336 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1337 * from multiple local IP addresses over the same tunnel.
1338 */
1339 @Override
Benedict Wongecc9f7c2018-03-01 18:53:07 -08001340 public synchronized void addAddressToTunnelInterface(
Nathan Harold21208ee2018-03-15 18:06:06 -07001341 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
1342 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001343 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1344
1345 // Get tunnelInterface record; if no such interface is found, will throw
1346 // IllegalArgumentException
1347 TunnelInterfaceRecord tunnelInterfaceInfo =
1348 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1349
Benedict Wongecc9f7c2018-03-01 18:53:07 -08001350 try {
1351 // We can assume general validity of the IP address, since we get them as a
1352 // LinkAddress, which does some validation.
1353 mSrvConfig
1354 .getNetdInstance()
1355 .interfaceAddAddress(
1356 tunnelInterfaceInfo.mInterfaceName,
1357 localAddr.getAddress().getHostAddress(),
1358 localAddr.getPrefixLength());
1359 } catch (RemoteException e) {
1360 throw e.rethrowFromSystemServer();
Benedict Wongecc9f7c2018-03-01 18:53:07 -08001361 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001362 }
1363
1364 /**
1365 * Remove a new local address from the tunnel interface. After removal, the address will no
1366 * longer be available to send from, or receive on.
1367 */
1368 @Override
1369 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold21208ee2018-03-15 18:06:06 -07001370 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
1371 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001372
Nathan Harold21208ee2018-03-15 18:06:06 -07001373 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8149f6e2018-01-18 18:31:45 -08001374 // Get tunnelInterface record; if no such interface is found, will throw
1375 // IllegalArgumentException
1376 TunnelInterfaceRecord tunnelInterfaceInfo =
1377 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1378
Benedict Wongecc9f7c2018-03-01 18:53:07 -08001379 try {
1380 // We can assume general validity of the IP address, since we get them as a
1381 // LinkAddress, which does some validation.
1382 mSrvConfig
1383 .getNetdInstance()
1384 .interfaceDelAddress(
1385 tunnelInterfaceInfo.mInterfaceName,
1386 localAddr.getAddress().getHostAddress(),
1387 localAddr.getPrefixLength());
1388 } catch (RemoteException e) {
1389 throw e.rethrowFromSystemServer();
Benedict Wongecc9f7c2018-03-01 18:53:07 -08001390 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001391 }
1392
1393 /**
1394 * Delete a TunnelInterface that has been been allocated by and registered with the system
1395 * server
1396 */
1397 @Override
Nathan Harold21208ee2018-03-15 18:06:06 -07001398 public synchronized void deleteTunnelInterface(
1399 int resourceId, String callingPackage) throws RemoteException {
1400 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001401 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1402 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1403 }
1404
Benedict Wong4f255702017-11-06 20:49:10 -08001405 @VisibleForTesting
Nathan Harolda2523312018-01-05 19:25:13 -08001406 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1407 IpSecAlgorithm auth = config.getAuthentication();
1408 IpSecAlgorithm crypt = config.getEncryption();
1409 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong4f255702017-11-06 20:49:10 -08001410
Nathan Harolda2523312018-01-05 19:25:13 -08001411 // Validate the algorithm set
1412 Preconditions.checkArgument(
1413 aead != null || crypt != null || auth != null,
1414 "No Encryption or Authentication algorithms specified");
1415 Preconditions.checkArgument(
1416 auth == null || auth.isAuthentication(),
1417 "Unsupported algorithm for Authentication");
1418 Preconditions.checkArgument(
Benedict Wong4f255702017-11-06 20:49:10 -08001419 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harolda2523312018-01-05 19:25:13 -08001420 Preconditions.checkArgument(
1421 aead == null || aead.isAead(),
1422 "Unsupported algorithm for Authenticated Encryption");
1423 Preconditions.checkArgument(
1424 aead == null || (auth == null && crypt == null),
1425 "Authenticated Encryption is mutually exclusive with other Authentication "
1426 + "or Encryption algorithms");
Benedict Wong4f255702017-11-06 20:49:10 -08001427 }
1428
Nathan Harold93962f32017-03-07 13:23:36 -08001429 /**
Nathan Harolda10003d2017-08-23 13:46:33 -07001430 * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
1431 * IllegalArgumentException if they are not.
1432 */
1433 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong344bd622017-11-16 15:27:22 -08001434 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1435
Nathan Harolda10003d2017-08-23 13:46:33 -07001436 switch (config.getEncapType()) {
1437 case IpSecTransform.ENCAP_NONE:
1438 break;
1439 case IpSecTransform.ENCAP_ESPINUDP:
1440 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong344bd622017-11-16 15:27:22 -08001441 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1442 userRecord.mEncapSocketRecords.getResourceOrThrow(
1443 config.getEncapSocketResourceId());
Nathan Harolda10003d2017-08-23 13:46:33 -07001444
1445 int port = config.getEncapRemotePort();
1446 if (port <= 0 || port > 0xFFFF) {
1447 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1448 }
1449 break;
1450 default:
1451 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1452 }
1453
Nathan Harolda2523312018-01-05 19:25:13 -08001454 validateAlgorithms(config);
Nathan Harolda10003d2017-08-23 13:46:33 -07001455
Nathan Harolda2523312018-01-05 19:25:13 -08001456 // Retrieve SPI record; will throw IllegalArgumentException if not found
1457 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1458
Benedict Wonge6b42772017-12-13 18:26:40 -08001459 // Check to ensure that SPI has not already been used.
1460 if (s.getOwnedByTransform()) {
1461 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1462 }
1463
Nathan Harolda2523312018-01-05 19:25:13 -08001464 // If no remote address is supplied, then use one from the SPI.
1465 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1466 config.setDestinationAddress(s.getDestinationAddress());
1467 }
1468
1469 // All remote addresses must match
1470 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1471 throw new IllegalArgumentException("Mismatched remote addresseses.");
1472 }
1473
1474 // This check is technically redundant due to the chain of custody between the SPI and
1475 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1476 // the transform, this will prevent us from messing up.
1477 checkInetAddress(config.getDestinationAddress());
1478
1479 // Require a valid source address for all transforms.
1480 checkInetAddress(config.getSourceAddress());
1481
1482 switch (config.getMode()) {
1483 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold5a920ca2018-02-02 18:34:25 -08001484 break;
Nathan Harolda2523312018-01-05 19:25:13 -08001485 case IpSecTransform.MODE_TUNNEL:
1486 break;
1487 default:
1488 throw new IllegalArgumentException(
1489 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harolda10003d2017-08-23 13:46:33 -07001490 }
1491 }
1492
Nathan Harold8c69bcb2018-05-15 19:18:38 -07001493 private static final String TUNNEL_OP = "STOPSHIP"; // = AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
1494
Nathan Harold21208ee2018-03-15 18:06:06 -07001495 private void enforceTunnelPermissions(String callingPackage) {
1496 checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Nathan Harold8c69bcb2018-05-15 19:18:38 -07001497 if (false) { // STOPSHIP if this line is present
1498 switch (getAppOpsManager().noteOp(
1499 TUNNEL_OP,
1500 Binder.getCallingUid(), callingPackage)) {
1501 case AppOpsManager.MODE_DEFAULT:
1502 mContext.enforceCallingOrSelfPermission(
1503 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
1504 break;
1505 case AppOpsManager.MODE_ALLOWED:
1506 return;
1507 default:
1508 throw new SecurityException("Request to ignore AppOps for non-legacy API");
1509 }
Nathan Harold21208ee2018-03-15 18:06:06 -07001510 }
Nathan Harold5a920ca2018-02-02 18:34:25 -08001511 }
1512
Benedict Wong0fe58a92018-01-19 17:36:02 -08001513 private void createOrUpdateTransform(
1514 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1515 throws RemoteException {
1516
1517 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1518 if (encapType != IpSecTransform.ENCAP_NONE) {
1519 encapLocalPort = socketRecord.getPort();
1520 encapRemotePort = c.getEncapRemotePort();
1521 }
1522
1523 IpSecAlgorithm auth = c.getAuthentication();
1524 IpSecAlgorithm crypt = c.getEncryption();
1525 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1526
Benedict Wongbf013a32018-03-15 19:41:41 -07001527 String cryptName;
1528 if (crypt == null) {
1529 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1530 } else {
1531 cryptName = crypt.getName();
1532 }
1533
Benedict Wong0fe58a92018-01-19 17:36:02 -08001534 mSrvConfig
1535 .getNetdInstance()
1536 .ipSecAddSecurityAssociation(
Benedict Wonga0989622018-07-25 13:06:29 -07001537 Binder.getCallingUid(),
Benedict Wong0fe58a92018-01-19 17:36:02 -08001538 c.getMode(),
1539 c.getSourceAddress(),
1540 c.getDestinationAddress(),
1541 (c.getNetwork() != null) ? c.getNetwork().netId : 0,
1542 spiRecord.getSpi(),
1543 c.getMarkValue(),
1544 c.getMarkMask(),
1545 (auth != null) ? auth.getName() : "",
1546 (auth != null) ? auth.getKey() : new byte[] {},
1547 (auth != null) ? auth.getTruncationLengthBits() : 0,
Benedict Wongbf013a32018-03-15 19:41:41 -07001548 cryptName,
Benedict Wong0fe58a92018-01-19 17:36:02 -08001549 (crypt != null) ? crypt.getKey() : new byte[] {},
1550 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1551 (authCrypt != null) ? authCrypt.getName() : "",
1552 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1553 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1554 encapType,
1555 encapLocalPort,
1556 encapRemotePort);
1557 }
1558
Nathan Harolda10003d2017-08-23 13:46:33 -07001559 /**
Benedict Wong0fe58a92018-01-19 17:36:02 -08001560 * Create a IPsec transform, which represents a single security association in the kernel. The
1561 * transform will be cached by the system server and must be freed when no longer needed. It is
1562 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1563 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold93962f32017-03-07 13:23:36 -08001564 */
1565 @Override
Nathan Harold21208ee2018-03-15 18:06:06 -07001566 public synchronized IpSecTransformResponse createTransform(
1567 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
1568 checkNotNull(c);
1569 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
1570 enforceTunnelPermissions(callingPackage);
1571 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001572 checkIpSecConfig(c);
Benedict Wongf33f03132018-01-18 14:38:16 -08001573 checkNotNull(binder, "Null Binder passed to createTransform");
Nathan Haroldd8c74292017-12-13 19:16:33 -08001574 final int resourceId = mNextResourceId++;
Benedict Wong344bd622017-11-16 15:27:22 -08001575
1576 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong4f9fb812017-12-13 17:16:53 -08001577 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong344bd622017-11-16 15:27:22 -08001578
1579 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001580 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1581 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001582
Benedict Wong344bd622017-11-16 15:27:22 -08001583 EncapSocketRecord socketRecord = null;
Benedict Wong0fe58a92018-01-19 17:36:02 -08001584 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong344bd622017-11-16 15:27:22 -08001585 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1586 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1587 c.getEncapSocketResourceId());
1588 dependencies.add(refcountedSocketRecord);
Benedict Wong344bd622017-11-16 15:27:22 -08001589 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001590 }
1591
Nathan Harolda2523312018-01-05 19:25:13 -08001592 RefcountedResource<SpiRecord> refcountedSpiRecord =
1593 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1594 dependencies.add(refcountedSpiRecord);
1595 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong344bd622017-11-16 15:27:22 -08001596
Nathan Harold144ce0a2018-04-03 16:13:19 -07001597 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong0fe58a92018-01-19 17:36:02 -08001598
1599 // SA was created successfully, time to construct a record and lock it away
Benedict Wong344bd622017-11-16 15:27:22 -08001600 userRecord.mTransformRecords.put(
1601 resourceId,
1602 new RefcountedResource<TransformRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001603 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong344bd622017-11-16 15:27:22 -08001604 binder,
1605 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001606 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001607 }
1608
1609 /**
1610 * Delete a transport mode transform that was previously allocated by + registered with the
1611 * system server. If this is called on an inactive (or non-existent) transform, it will not
1612 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1613 * other reasons.
1614 */
1615 @Override
Benedict Wongf33f03132018-01-18 14:38:16 -08001616 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001617 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1618 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001619 }
1620
1621 /**
1622 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1623 * association as a correspondent policy to the provided socket
1624 */
1625 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001626 public synchronized void applyTransportModeTransform(
Nathan Harolda2523312018-01-05 19:25:13 -08001627 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wonga0989622018-07-25 13:06:29 -07001628 int callingUid = Binder.getCallingUid();
1629 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harolda2523312018-01-05 19:25:13 -08001630 checkDirection(direction);
Benedict Wong344bd622017-11-16 15:27:22 -08001631 // Get transform record; if no transform is found, will throw IllegalArgumentException
1632 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001633
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001634 // TODO: make this a function.
Benedict Wonga0989622018-07-25 13:06:29 -07001635 if (info.pid != getCallingPid() || info.uid != callingUid) {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001636 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1637 }
1638
Benedict Wong8149f6e2018-01-18 18:31:45 -08001639 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001640 IpSecConfig c = info.getConfig();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001641 Preconditions.checkArgument(
1642 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1643 "Transform mode was not Transport mode; cannot be applied to a socket");
1644
Nathan Harold144ce0a2018-04-03 16:13:19 -07001645 mSrvConfig
1646 .getNetdInstance()
1647 .ipSecApplyTransportModeTransform(
1648 socket.getFileDescriptor(),
Benedict Wonga0989622018-07-25 13:06:29 -07001649 callingUid,
Nathan Harold144ce0a2018-04-03 16:13:19 -07001650 direction,
1651 c.getSourceAddress(),
1652 c.getDestinationAddress(),
1653 info.getSpiRecord().getSpi());
Nathan Harold93962f32017-03-07 13:23:36 -08001654 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001655
Nathan Harold93962f32017-03-07 13:23:36 -08001656 /**
Nathan Harolda2523312018-01-05 19:25:13 -08001657 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1658 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1659 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1660 * reserved for future improved input validation.
Nathan Harold93962f32017-03-07 13:23:36 -08001661 */
1662 @Override
Nathan Haroldf73d2522018-01-17 01:00:20 -08001663 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1664 throws RemoteException {
Nathan Harold144ce0a2018-04-03 16:13:19 -07001665 mSrvConfig
1666 .getNetdInstance()
1667 .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
Nathan Harold93962f32017-03-07 13:23:36 -08001668 }
1669
Benedict Wong8149f6e2018-01-18 18:31:45 -08001670 /**
1671 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1672 * security association as a correspondent policy to the provided interface
1673 */
1674 @Override
1675 public synchronized void applyTunnelModeTransform(
Nathan Harold21208ee2018-03-15 18:06:06 -07001676 int tunnelResourceId, int direction,
1677 int transformResourceId, String callingPackage) throws RemoteException {
1678 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001679 checkDirection(direction);
1680
Benedict Wonga0989622018-07-25 13:06:29 -07001681 int callingUid = Binder.getCallingUid();
1682 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001683
1684 // Get transform record; if no transform is found, will throw IllegalArgumentException
1685 TransformRecord transformInfo =
1686 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1687
1688 // Get tunnelInterface record; if no such interface is found, will throw
1689 // IllegalArgumentException
1690 TunnelInterfaceRecord tunnelInterfaceInfo =
1691 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1692
1693 // Get config and check that to-be-applied transform has the correct mode
1694 IpSecConfig c = transformInfo.getConfig();
1695 Preconditions.checkArgument(
1696 c.getMode() == IpSecTransform.MODE_TUNNEL,
1697 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1698
Benedict Wong0fe58a92018-01-19 17:36:02 -08001699 EncapSocketRecord socketRecord = null;
1700 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1701 socketRecord =
1702 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1703 }
1704 SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId());
1705
Benedict Wong8149f6e2018-01-18 18:31:45 -08001706 int mark =
Benedict Wong564e2aa2018-05-07 20:06:44 -07001707 (direction == IpSecManager.DIRECTION_OUT)
1708 ? tunnelInterfaceInfo.getOkey()
1709 : tunnelInterfaceInfo.getIkey();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001710
Benedict Wong0fe58a92018-01-19 17:36:02 -08001711 try {
1712 c.setMarkValue(mark);
1713 c.setMarkMask(0xffffffff);
1714
1715 if (direction == IpSecManager.DIRECTION_OUT) {
1716 // Set output mark via underlying network (output only)
1717 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1718
1719 // If outbound, also add SPI to the policy.
Benedict Wong564e2aa2018-05-07 20:06:44 -07001720 for (int selAddrFamily : ADDRESS_FAMILIES) {
Benedict Wongb8ef5412018-01-24 15:31:39 -08001721 mSrvConfig
1722 .getNetdInstance()
1723 .ipSecUpdateSecurityPolicy(
Benedict Wonga0989622018-07-25 13:06:29 -07001724 callingUid,
Benedict Wong564e2aa2018-05-07 20:06:44 -07001725 selAddrFamily,
Benedict Wongb8ef5412018-01-24 15:31:39 -08001726 direction,
Benedict Wong564e2aa2018-05-07 20:06:44 -07001727 tunnelInterfaceInfo.getLocalAddress(),
1728 tunnelInterfaceInfo.getRemoteAddress(),
Benedict Wongb8ef5412018-01-24 15:31:39 -08001729 transformInfo.getSpiRecord().getSpi(),
1730 mark,
1731 0xffffffff);
1732 }
Benedict Wong0fe58a92018-01-19 17:36:02 -08001733 }
1734
1735 // Update SA with tunnel mark (ikey or okey based on direction)
1736 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1737 } catch (ServiceSpecificException e) {
1738 if (e.errorCode == EINVAL) {
1739 throw new IllegalArgumentException(e.toString());
1740 } else {
1741 throw e;
1742 }
1743 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001744 }
1745
Nathan Harold93962f32017-03-07 13:23:36 -08001746 @Override
ludib0c95b12017-05-22 10:52:23 -07001747 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001748 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludib0c95b12017-05-22 10:52:23 -07001749
1750 pw.println("IpSecService dump:");
Nathan Harold1afbef42017-03-01 18:55:06 -08001751 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1752 pw.println();
ludib0c95b12017-05-22 10:52:23 -07001753
Benedict Wong344bd622017-11-16 15:27:22 -08001754 pw.println("mUserResourceTracker:");
1755 pw.println(mUserResourceTracker);
Nathan Harold1afbef42017-03-01 18:55:06 -08001756 }
1757}