blob: c2adbfaeb74054eec12e36a28dab8b40063cb04f [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
27import android.content.Context;
Nathan Harolda2523312018-01-05 19:25:13 -080028import android.net.ConnectivityManager;
Nathan Harold1afbef42017-03-01 18:55:06 -080029import android.net.IIpSecService;
30import android.net.INetd;
Nathan Harold93962f32017-03-07 13:23:36 -080031import android.net.IpSecAlgorithm;
32import android.net.IpSecConfig;
33import android.net.IpSecManager;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070034import android.net.IpSecSpiResponse;
Nathan Harold93962f32017-03-07 13:23:36 -080035import android.net.IpSecTransform;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070036import android.net.IpSecTransformResponse;
Benedict Wong8149f6e2018-01-18 18:31:45 -080037import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070038import android.net.IpSecUdpEncapResponse;
Benedict Wong8149f6e2018-01-18 18:31:45 -080039import android.net.Network;
Nathan Harolda10003d2017-08-23 13:46:33 -070040import android.net.NetworkUtils;
Benedict Wongbabe5d72017-12-03 19:42:36 -080041import android.net.TrafficStats;
Nathan Harold1afbef42017-03-01 18:55:06 -080042import android.net.util.NetdService;
Nathan Harold93962f32017-03-07 13:23:36 -080043import android.os.Binder;
Nathan Harold93962f32017-03-07 13:23:36 -080044import android.os.IBinder;
45import android.os.ParcelFileDescriptor;
Nathan Harold1afbef42017-03-01 18:55:06 -080046import android.os.RemoteException;
Nathan Harold93962f32017-03-07 13:23:36 -080047import android.os.ServiceSpecificException;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070048import android.system.ErrnoException;
49import android.system.Os;
50import android.system.OsConstants;
Nathan Harolda10003d2017-08-23 13:46:33 -070051import android.text.TextUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080052import android.util.Log;
53import android.util.Slog;
Nathan Harold93962f32017-03-07 13:23:36 -080054import android.util.SparseArray;
Benedict Wong8149f6e2018-01-18 18:31:45 -080055import android.util.SparseBooleanArray;
Nathan Harolda10003d2017-08-23 13:46:33 -070056
Nathan Harold93962f32017-03-07 13:23:36 -080057import com.android.internal.annotations.GuardedBy;
ludi1a06aa72017-05-12 09:15:00 -070058import com.android.internal.annotations.VisibleForTesting;
Benedict Wong4f255702017-11-06 20:49:10 -080059import com.android.internal.util.Preconditions;
Nathan Harolda10003d2017-08-23 13:46:33 -070060
Nathan Harold1afbef42017-03-01 18:55:06 -080061import java.io.FileDescriptor;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070062import java.io.IOException;
Nathan Harold1afbef42017-03-01 18:55:06 -080063import java.io.PrintWriter;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070064import java.net.InetAddress;
65import java.net.InetSocketAddress;
66import java.net.UnknownHostException;
Benedict Wong409c8ca2017-10-26 19:41:43 -070067import java.util.ArrayList;
Benedict Wong8149f6e2018-01-18 18:31:45 -080068import java.util.BitSet;
Benedict Wong409c8ca2017-10-26 19:41:43 -070069import java.util.List;
Nathan Harolda10003d2017-08-23 13:46:33 -070070
Nathan Harold8dc1fd02017-04-04 19:37:48 -070071import libcore.io.IoUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080072
Benedict Wong409c8ca2017-10-26 19:41:43 -070073/**
74 * A service to manage multiple clients that want to access the IpSec API. The service is
75 * responsible for maintaining a list of clients and managing the resources (and related quotas)
76 * that each of them own.
77 *
78 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
79 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
80 * thread is ever running at a time.
81 *
82 * @hide
83 */
Nathan Harold1afbef42017-03-01 18:55:06 -080084public class IpSecService extends IIpSecService.Stub {
85 private static final String TAG = "IpSecService";
86 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Nathan Harold8dc1fd02017-04-04 19:37:48 -070087
Nathan Harold1afbef42017-03-01 18:55:06 -080088 private static final String NETD_SERVICE_NAME = "netd";
Nathan Harold93962f32017-03-07 13:23:36 -080089 private static final int[] DIRECTIONS =
Nathan Harolda2523312018-01-05 19:25:13 -080090 new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN};
Nathan Harold1afbef42017-03-01 18:55:06 -080091
ludi1a06aa72017-05-12 09:15:00 -070092 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold8dc1fd02017-04-04 19:37:48 -070093 private static final int MAX_PORT_BIND_ATTEMPTS = 10;
94 private static final InetAddress INADDR_ANY;
95
96 static {
97 try {
98 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
99 } catch (UnknownHostException e) {
100 throw new RuntimeException(e);
101 }
102 }
103
104 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
105 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
Benedict Wong8149f6e2018-01-18 18:31:45 -0800106 static final String TUNNEL_INTERFACE_PREFIX = "ipsec";
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700107
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)
417 .append(", mSpiRecords=")
418 .append(mSpiRecords)
419 .append(", mTransformRecords=")
420 .append(mTransformRecords)
421 .append(", mEncapSocketRecords=")
422 .append(mEncapSocketRecords)
423 .append("}")
424 .toString();
425 }
426 }
427
Benedict Wong4f9fb812017-12-13 17:16:53 -0800428 /**
429 * This class is not thread-safe, and expects that that users of this class will ensure
430 * synchronization and thread safety by holding the IpSecService.this instance lock.
431 */
Benedict Wong344bd622017-11-16 15:27:22 -0800432 @VisibleForTesting
433 static final class UserResourceTracker {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700434 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
435
Benedict Wong4f9fb812017-12-13 17:16:53 -0800436 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
Benedict Wong344bd622017-11-16 15:27:22 -0800437 public UserRecord getUserRecord(int uid) {
438 checkCallerUid(uid);
439
Nathan Harolda1afbd82017-04-24 16:16:34 -0700440 UserRecord r = mUserRecords.get(uid);
441 if (r == null) {
442 r = new UserRecord();
443 mUserRecords.put(uid, r);
444 }
445 return r;
446 }
ludi3e5ea232017-08-10 15:44:40 -0700447
Benedict Wong344bd622017-11-16 15:27:22 -0800448 /** Safety method; guards against access of other user's UserRecords */
449 private void checkCallerUid(int uid) {
450 if (uid != Binder.getCallingUid()
451 && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
452 throw new SecurityException("Attempted access of unowned resources");
453 }
454 }
455
ludi3e5ea232017-08-10 15:44:40 -0700456 @Override
457 public String toString() {
458 return mUserRecords.toString();
459 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700460 }
461
Benedict Wong344bd622017-11-16 15:27:22 -0800462 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700463
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700464 /**
Benedict Wong4f9fb812017-12-13 17:16:53 -0800465 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
Benedict Wong344bd622017-11-16 15:27:22 -0800466 * resources. It relies on a provided resourceId that should uniquely identify the kernel
467 * resource. To use this class, the user should implement the invalidate() and
468 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
Benedict Wong4f9fb812017-12-13 17:16:53 -0800469 * tracking arrays and kernel resources, respectively.
470 *
471 * <p>This class associates kernel resources with the UID that owns and controls them.
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700472 */
Benedict Wong4f9fb812017-12-13 17:16:53 -0800473 private abstract class OwnedResourceRecord implements IResource {
Nathan Harold93962f32017-03-07 13:23:36 -0800474 final int pid;
475 final int uid;
Benedict Wong344bd622017-11-16 15:27:22 -0800476 protected final int mResourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800477
Benedict Wong4f9fb812017-12-13 17:16:53 -0800478 OwnedResourceRecord(int resourceId) {
Nathan Harold93962f32017-03-07 13:23:36 -0800479 super();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700480 if (resourceId == INVALID_RESOURCE_ID) {
481 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
482 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700483 mResourceId = resourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800484 pid = Binder.getCallingPid();
485 uid = Binder.getCallingUid();
486
Nathan Harolda1afbd82017-04-24 16:16:34 -0700487 getResourceTracker().take();
Nathan Harold93962f32017-03-07 13:23:36 -0800488 }
489
Benedict Wong344bd622017-11-16 15:27:22 -0800490 @Override
491 public abstract void invalidate() throws RemoteException;
492
493 /** Convenience method; retrieves the user resource record for the stored UID. */
494 protected UserRecord getUserRecord() {
495 return mUserResourceTracker.getUserRecord(uid);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700496 }
497
Benedict Wong344bd622017-11-16 15:27:22 -0800498 @Override
499 public abstract void freeUnderlyingResources() throws RemoteException;
ludib0c95b12017-05-22 10:52:23 -0700500
Nathan Harolda1afbd82017-04-24 16:16:34 -0700501 /** Get the resource tracker for this resource */
502 protected abstract ResourceTracker getResourceTracker();
503
ludib0c95b12017-05-22 10:52:23 -0700504 @Override
505 public String toString() {
506 return new StringBuilder()
507 .append("{mResourceId=")
508 .append(mResourceId)
509 .append(", pid=")
510 .append(pid)
511 .append(", uid=")
512 .append(uid)
ludib0c95b12017-05-22 10:52:23 -0700513 .append("}")
514 .toString();
515 }
Nathan Harold93962f32017-03-07 13:23:36 -0800516 };
517
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700518 /**
Benedict Wong344bd622017-11-16 15:27:22 -0800519 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
520 *
521 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
522 * if a key is not found during a retrieval process.
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700523 */
Benedict Wong344bd622017-11-16 15:27:22 -0800524 static class RefcountedResourceArray<T extends IResource> {
525 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
526 private final String mTypeName;
Nathan Harold93962f32017-03-07 13:23:36 -0800527
Benedict Wong344bd622017-11-16 15:27:22 -0800528 public RefcountedResourceArray(String typeName) {
529 this.mTypeName = typeName;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700530 }
531
Benedict Wong344bd622017-11-16 15:27:22 -0800532 /**
533 * Accessor method to get inner resource object.
534 *
535 * @throws IllegalArgumentException if no resource with provided key is found.
536 */
537 T getResourceOrThrow(int key) {
538 return getRefcountedResourceOrThrow(key).getResource();
539 }
540
541 /**
542 * Accessor method to get reference counting wrapper.
543 *
544 * @throws IllegalArgumentException if no resource with provided key is found.
545 */
546 RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
547 RefcountedResource<T> resource = mArray.get(key);
548 if (resource == null) {
549 throw new IllegalArgumentException(
550 String.format("No such %s found for given id: %d", mTypeName, key));
551 }
552
553 return resource;
554 }
555
556 void put(int key, RefcountedResource<T> obj) {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700557 checkNotNull(obj, "Null resources cannot be added");
558 mArray.put(key, obj);
559 }
560
561 void remove(int key) {
562 mArray.remove(key);
563 }
ludib0c95b12017-05-22 10:52:23 -0700564
565 @Override
566 public String toString() {
567 return mArray.toString();
568 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700569 }
570
Benedict Wong4f9fb812017-12-13 17:16:53 -0800571 /**
572 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
573 * created, the SpiRecord that originally tracked the SAs will reliquish the
574 * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
575 */
576 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700577 private final IpSecConfig mConfig;
Nathan Harolda2523312018-01-05 19:25:13 -0800578 private final SpiRecord mSpi;
Benedict Wong344bd622017-11-16 15:27:22 -0800579 private final EncapSocketRecord mSocket;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700580
581 TransformRecord(
Nathan Harolda2523312018-01-05 19:25:13 -0800582 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong344bd622017-11-16 15:27:22 -0800583 super(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -0800584 mConfig = config;
Nathan Harolda2523312018-01-05 19:25:13 -0800585 mSpi = spi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700586 mSocket = socket;
Benedict Wonge6b42772017-12-13 18:26:40 -0800587
588 spi.setOwnedByTransform();
Nathan Harold93962f32017-03-07 13:23:36 -0800589 }
590
591 public IpSecConfig getConfig() {
592 return mConfig;
593 }
594
Nathan Harolda2523312018-01-05 19:25:13 -0800595 public SpiRecord getSpiRecord() {
596 return mSpi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700597 }
598
599 /** always guarded by IpSecService#this */
Nathan Harold93962f32017-03-07 13:23:36 -0800600 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800601 public void freeUnderlyingResources() {
Nathan Harolda2523312018-01-05 19:25:13 -0800602 int spi = mSpi.getSpi();
603 try {
604 mSrvConfig
605 .getNetdInstance()
606 .ipSecDeleteSecurityAssociation(
607 mResourceId,
608 mConfig.getSourceAddress(),
609 mConfig.getDestinationAddress(),
Di Lu0b611f42018-01-11 11:35:25 -0800610 spi,
611 mConfig.getMarkValue(),
612 mConfig.getMarkMask());
Nathan Harolda2523312018-01-05 19:25:13 -0800613 } catch (ServiceSpecificException e) {
614 // FIXME: get the error code and throw is at an IOException from Errno Exception
615 } catch (RemoteException e) {
616 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
Nathan Harold93962f32017-03-07 13:23:36 -0800617 }
Nathan Harold93962f32017-03-07 13:23:36 -0800618
Benedict Wong344bd622017-11-16 15:27:22 -0800619 getResourceTracker().give();
Nathan Harold93962f32017-03-07 13:23:36 -0800620 }
ludib0c95b12017-05-22 10:52:23 -0700621
Benedict Wong344bd622017-11-16 15:27:22 -0800622 @Override
623 public void invalidate() throws RemoteException {
624 getUserRecord().removeTransformRecord(mResourceId);
625 }
626
627 @Override
Nathan Harolda1afbd82017-04-24 16:16:34 -0700628 protected ResourceTracker getResourceTracker() {
Benedict Wong344bd622017-11-16 15:27:22 -0800629 return getUserRecord().mTransformQuotaTracker;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700630 }
631
ludib0c95b12017-05-22 10:52:23 -0700632 @Override
633 public String toString() {
634 StringBuilder strBuilder = new StringBuilder();
635 strBuilder
636 .append("{super=")
637 .append(super.toString())
638 .append(", mSocket=")
639 .append(mSocket)
Nathan Harolda2523312018-01-05 19:25:13 -0800640 .append(", mSpi.mResourceId=")
641 .append(mSpi.mResourceId)
ludib0c95b12017-05-22 10:52:23 -0700642 .append(", mConfig=")
643 .append(mConfig)
644 .append("}");
645 return strBuilder.toString();
646 }
Nathan Harold93962f32017-03-07 13:23:36 -0800647 }
648
Benedict Wong4f9fb812017-12-13 17:16:53 -0800649 /**
650 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
651 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
652 * object
653 */
654 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harolda2523312018-01-05 19:25:13 -0800655 private final String mSourceAddress;
656 private final String mDestinationAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800657 private int mSpi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700658
659 private boolean mOwnedByTransform = false;
Nathan Harold93962f32017-03-07 13:23:36 -0800660
Nathan Harolda2523312018-01-05 19:25:13 -0800661 SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
Benedict Wong344bd622017-11-16 15:27:22 -0800662 super(resourceId);
Nathan Harolda2523312018-01-05 19:25:13 -0800663 mSourceAddress = sourceAddress;
664 mDestinationAddress = destinationAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800665 mSpi = spi;
Nathan Harold93962f32017-03-07 13:23:36 -0800666 }
667
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700668 /** always guarded by IpSecService#this */
669 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800670 public void freeUnderlyingResources() {
Nathan Harold93962f32017-03-07 13:23:36 -0800671 try {
ludi1a06aa72017-05-12 09:15:00 -0700672 mSrvConfig
673 .getNetdInstance()
Nathan Harold93962f32017-03-07 13:23:36 -0800674 .ipSecDeleteSecurityAssociation(
Di Lu0b611f42018-01-11 11:35:25 -0800675 mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
Nathan Harold93962f32017-03-07 13:23:36 -0800676 } catch (ServiceSpecificException e) {
677 // FIXME: get the error code and throw is at an IOException from Errno Exception
678 } catch (RemoteException e) {
679 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId);
680 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700681
682 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -0800683
Benedict Wong344bd622017-11-16 15:27:22 -0800684 getResourceTracker().give();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700685 }
686
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700687 public int getSpi() {
688 return mSpi;
689 }
690
Nathan Harolda2523312018-01-05 19:25:13 -0800691 public String getDestinationAddress() {
692 return mDestinationAddress;
693 }
694
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700695 public void setOwnedByTransform() {
696 if (mOwnedByTransform) {
697 // Programming error
Andreas Gamped6d8e452017-07-11 10:25:09 -0700698 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700699 }
700
701 mOwnedByTransform = true;
Nathan Harold93962f32017-03-07 13:23:36 -0800702 }
ludib0c95b12017-05-22 10:52:23 -0700703
Benedict Wonge6b42772017-12-13 18:26:40 -0800704 public boolean getOwnedByTransform() {
705 return mOwnedByTransform;
706 }
707
ludib0c95b12017-05-22 10:52:23 -0700708 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800709 public void invalidate() throws RemoteException {
710 getUserRecord().removeSpiRecord(mResourceId);
711 }
712
713 @Override
714 protected ResourceTracker getResourceTracker() {
715 return getUserRecord().mSpiQuotaTracker;
716 }
717
718 @Override
ludib0c95b12017-05-22 10:52:23 -0700719 public String toString() {
720 StringBuilder strBuilder = new StringBuilder();
721 strBuilder
722 .append("{super=")
723 .append(super.toString())
724 .append(", mSpi=")
725 .append(mSpi)
Nathan Harolda2523312018-01-05 19:25:13 -0800726 .append(", mSourceAddress=")
727 .append(mSourceAddress)
728 .append(", mDestinationAddress=")
729 .append(mDestinationAddress)
ludib0c95b12017-05-22 10:52:23 -0700730 .append(", mOwnedByTransform=")
731 .append(mOwnedByTransform)
732 .append("}");
733 return strBuilder.toString();
734 }
Nathan Harold93962f32017-03-07 13:23:36 -0800735 }
736
Benedict Wong8149f6e2018-01-18 18:31:45 -0800737 // These values have been reserved in ConnectivityService
738 @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
739
740 @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400;
741
742 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
743 private int mNextTunnelNetIdIndex = 0;
744
745 /**
746 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
747 *
748 * <p>This method should only be called from Binder threads. Do not call this from within the
749 * system server as it will crash the system on failure.
750 *
751 * @return an integer key within the netId range, if successful
752 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
753 */
754 @VisibleForTesting
755 int reserveNetId() {
756 synchronized (mTunnelNetIds) {
757 for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
758 int index = mNextTunnelNetIdIndex;
759 int netId = index + TUN_INTF_NETID_START;
760 if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
761 if (!mTunnelNetIds.get(netId)) {
762 mTunnelNetIds.put(netId, true);
763 return netId;
764 }
765 }
766 }
767 throw new IllegalStateException("No free netIds to allocate");
768 }
769
770 @VisibleForTesting
771 void releaseNetId(int netId) {
772 synchronized (mTunnelNetIds) {
773 mTunnelNetIds.delete(netId);
774 }
775 }
776
777 private final class TunnelInterfaceRecord extends OwnedResourceRecord {
778 private final String mInterfaceName;
779 private final Network mUnderlyingNetwork;
780
781 // outer addresses
782 private final String mLocalAddress;
783 private final String mRemoteAddress;
784
785 private final int mIkey;
786 private final int mOkey;
787
788 TunnelInterfaceRecord(
789 int resourceId,
790 String interfaceName,
791 Network underlyingNetwork,
792 String localAddr,
793 String remoteAddr,
794 int ikey,
795 int okey) {
796 super(resourceId);
797
798 mInterfaceName = interfaceName;
799 mUnderlyingNetwork = underlyingNetwork;
800 mLocalAddress = localAddr;
801 mRemoteAddress = remoteAddr;
802 mIkey = ikey;
803 mOkey = okey;
804 }
805
806 /** always guarded by IpSecService#this */
807 @Override
808 public void freeUnderlyingResources() {
809 // TODO: Add calls to netd
810 // Teardown VTI
811 // Delete global policies
812
813 getResourceTracker().give();
814 releaseNetId(mIkey);
815 releaseNetId(mOkey);
816 }
817
818 public String getInterfaceName() {
819 return mInterfaceName;
820 }
821
822 public Network getUnderlyingNetwork() {
823 return mUnderlyingNetwork;
824 }
825
826 /** Returns the local, outer address for the tunnelInterface */
827 public String getLocalAddress() {
828 return mLocalAddress;
829 }
830
831 /** Returns the remote, outer address for the tunnelInterface */
832 public String getRemoteAddress() {
833 return mRemoteAddress;
834 }
835
836 public int getIkey() {
837 return mIkey;
838 }
839
840 public int getOkey() {
841 return mOkey;
842 }
843
844 @Override
845 protected ResourceTracker getResourceTracker() {
846 return getUserRecord().mTunnelQuotaTracker;
847 }
848
849 @Override
850 public void invalidate() {
851 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
852 }
853
854 @Override
855 public String toString() {
856 return new StringBuilder()
857 .append("{super=")
858 .append(super.toString())
859 .append(", mInterfaceName=")
860 .append(mInterfaceName)
861 .append(", mUnderlyingNetwork=")
862 .append(mUnderlyingNetwork)
863 .append(", mLocalAddress=")
864 .append(mLocalAddress)
865 .append(", mRemoteAddress=")
866 .append(mRemoteAddress)
867 .append(", mIkey=")
868 .append(mIkey)
869 .append(", mOkey=")
870 .append(mOkey)
871 .append("}")
872 .toString();
873 }
874 }
875
Benedict Wong4f9fb812017-12-13 17:16:53 -0800876 /**
877 * Tracks a UDP encap socket, and manages cleanup paths
878 *
879 * <p>While this class does not manage non-kernel resources, race conditions around socket
880 * binding require that the service creates the encap socket, binds it and applies the socket
881 * policy before handing it to a user.
882 */
883 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700884 private FileDescriptor mSocket;
885 private final int mPort;
Nathan Harold93962f32017-03-07 13:23:36 -0800886
Benedict Wong344bd622017-11-16 15:27:22 -0800887 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
888 super(resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700889 mSocket = socket;
890 mPort = port;
891 }
892
893 /** always guarded by IpSecService#this */
894 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800895 public void freeUnderlyingResources() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700896 Log.d(TAG, "Closing port " + mPort);
897 IoUtils.closeQuietly(mSocket);
898 mSocket = null;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700899
Benedict Wong344bd622017-11-16 15:27:22 -0800900 getResourceTracker().give();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700901 }
902
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700903 public int getPort() {
904 return mPort;
905 }
906
907 public FileDescriptor getSocket() {
908 return mSocket;
909 }
ludib0c95b12017-05-22 10:52:23 -0700910
911 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800912 protected ResourceTracker getResourceTracker() {
913 return getUserRecord().mSocketQuotaTracker;
914 }
915
916 @Override
917 public void invalidate() {
918 getUserRecord().removeEncapSocketRecord(mResourceId);
919 }
920
921 @Override
ludib0c95b12017-05-22 10:52:23 -0700922 public String toString() {
923 return new StringBuilder()
924 .append("{super=")
925 .append(super.toString())
926 .append(", mSocket=")
927 .append(mSocket)
928 .append(", mPort=")
929 .append(mPort)
930 .append("}")
931 .toString();
932 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700933 }
Nathan Harold93962f32017-03-07 13:23:36 -0800934
Nathan Harold1afbef42017-03-01 18:55:06 -0800935 /**
936 * Constructs a new IpSecService instance
937 *
938 * @param context Binder context for this service
939 */
940 private IpSecService(Context context) {
ludi1a06aa72017-05-12 09:15:00 -0700941 this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
Nathan Harold1afbef42017-03-01 18:55:06 -0800942 }
943
944 static IpSecService create(Context context) throws InterruptedException {
945 final IpSecService service = new IpSecService(context);
946 service.connectNativeNetdService();
947 return service;
948 }
949
ludi1a06aa72017-05-12 09:15:00 -0700950 /** @hide */
951 @VisibleForTesting
952 public IpSecService(Context context, IpSecServiceConfiguration config) {
Nathan Harolda2523312018-01-05 19:25:13 -0800953 this(
954 context,
955 config,
956 (fd, uid) -> {
957 try {
958 TrafficStats.setThreadStatsUid(uid);
959 TrafficStats.tagFileDescriptor(fd);
960 } finally {
961 TrafficStats.clearThreadStatsUid();
962 }
963 });
Benedict Wongbabe5d72017-12-03 19:42:36 -0800964 }
965
966 /** @hide */
967 @VisibleForTesting
968 public IpSecService(
969 Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
ludi1a06aa72017-05-12 09:15:00 -0700970 mContext = context;
971 mSrvConfig = config;
Benedict Wongbabe5d72017-12-03 19:42:36 -0800972 mUidFdTagger = uidFdTagger;
ludi1a06aa72017-05-12 09:15:00 -0700973 }
974
Nathan Harold1afbef42017-03-01 18:55:06 -0800975 public void systemReady() {
976 if (isNetdAlive()) {
977 Slog.d(TAG, "IpSecService is ready");
978 } else {
979 Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
980 }
981 }
982
983 private void connectNativeNetdService() {
984 // Avoid blocking the system server to do this
Nathan Haroldb0e05082017-07-17 14:01:53 -0700985 new Thread() {
986 @Override
987 public void run() {
988 synchronized (IpSecService.this) {
ludi1a06aa72017-05-12 09:15:00 -0700989 NetdService.get(NETD_FETCH_TIMEOUT_MS);
Nathan Haroldb0e05082017-07-17 14:01:53 -0700990 }
991 }
992 }.start();
Nathan Harold1afbef42017-03-01 18:55:06 -0800993 }
994
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700995 synchronized boolean isNetdAlive() {
996 try {
ludi1a06aa72017-05-12 09:15:00 -0700997 final INetd netd = mSrvConfig.getNetdInstance();
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700998 if (netd == null) {
Nathan Harold1afbef42017-03-01 18:55:06 -0800999 return false;
1000 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001001 return netd.isAlive();
1002 } catch (RemoteException re) {
1003 return false;
Nathan Harold1afbef42017-03-01 18:55:06 -08001004 }
1005 }
1006
Nathan Harolda10003d2017-08-23 13:46:33 -07001007 /**
1008 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1009 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1010 */
1011 private static void checkInetAddress(String inetAddress) {
1012 if (TextUtils.isEmpty(inetAddress)) {
1013 throw new IllegalArgumentException("Unspecified address");
1014 }
1015
1016 InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
1017
1018 if (checkAddr.isAnyLocalAddress()) {
1019 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1020 }
1021 }
1022
1023 /**
1024 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1025 * DIRECTION_IN or DIRECTION_OUT
1026 */
1027 private static void checkDirection(int direction) {
1028 switch (direction) {
Nathan Harolda2523312018-01-05 19:25:13 -08001029 case IpSecManager.DIRECTION_OUT:
1030 case IpSecManager.DIRECTION_IN:
Nathan Harolda10003d2017-08-23 13:46:33 -07001031 return;
1032 }
1033 throw new IllegalArgumentException("Invalid Direction: " + direction);
1034 }
1035
Nathan Harold93962f32017-03-07 13:23:36 -08001036 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001037 @Override
1038 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harolda2523312018-01-05 19:25:13 -08001039 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1040 checkInetAddress(destinationAddress);
Nathan Harolda10003d2017-08-23 13:46:33 -07001041 /* requestedSpi can be anything in the int range, so no check is needed. */
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001042 checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harolda10003d2017-08-23 13:46:33 -07001043
Benedict Wong344bd622017-11-16 15:27:22 -08001044 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Nathan Haroldd8c74292017-12-13 19:16:33 -08001045 final int resourceId = mNextResourceId++;
Nathan Harold93962f32017-03-07 13:23:36 -08001046
1047 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -08001048 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001049 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001050 return new IpSecSpiResponse(
Nathan Harolda10003d2017-08-23 13:46:33 -07001051 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harolda1afbd82017-04-24 16:16:34 -07001052 }
Nathan Harolda2523312018-01-05 19:25:13 -08001053
Nathan Harold93962f32017-03-07 13:23:36 -08001054 spi =
ludi1a06aa72017-05-12 09:15:00 -07001055 mSrvConfig
1056 .getNetdInstance()
Nathan Harolda2523312018-01-05 19:25:13 -08001057 .ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi);
Nathan Harold93962f32017-03-07 13:23:36 -08001058 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong344bd622017-11-16 15:27:22 -08001059 userRecord.mSpiRecords.put(
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001060 resourceId,
Benedict Wong344bd622017-11-16 15:27:22 -08001061 new RefcountedResource<SpiRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001062 new SpiRecord(resourceId, "", destinationAddress, spi), binder));
Nathan Harold93962f32017-03-07 13:23:36 -08001063 } catch (ServiceSpecificException e) {
1064 // TODO: Add appropriate checks when other ServiceSpecificException types are supported
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001065 return new IpSecSpiResponse(
Nathan Harolda1afbd82017-04-24 16:16:34 -07001066 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold93962f32017-03-07 13:23:36 -08001067 } catch (RemoteException e) {
1068 throw e.rethrowFromSystemServer();
1069 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001070 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1071 }
1072
1073 /* This method should only be called from Binder threads. Do not call this from
1074 * within the system server as it will crash the system on failure.
1075 */
Benedict Wong344bd622017-11-16 15:27:22 -08001076 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001077 throws RemoteException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001078
Benedict Wong344bd622017-11-16 15:27:22 -08001079 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold93962f32017-03-07 13:23:36 -08001080 }
1081
1082 /** Release a previously allocated SPI that has been registered with the system server */
1083 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001084 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1085 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1086 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001087 }
1088
1089 /**
1090 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1091 * be unbound.
1092 *
1093 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1094 * a random open port and then bind by number, this function creates a temp socket, binds to a
1095 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1096 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1097 * FileHandle.
1098 *
1099 * <p>The loop in this function handles the inherent race window between un-binding to a port
1100 * and re-binding, during which the system could *technically* hand that port out to someone
1101 * else.
1102 */
Benedict Wongf186d672017-10-10 20:44:28 -07001103 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001104 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1105 try {
1106 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1107 Os.bind(probeSocket, INADDR_ANY, 0);
1108 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1109 Os.close(probeSocket);
1110 Log.v(TAG, "Binding to port " + port);
1111 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongf186d672017-10-10 20:44:28 -07001112 return port;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001113 } catch (ErrnoException e) {
1114 // Someone miraculously claimed the port just after we closed probeSocket.
1115 if (e.errno == OsConstants.EADDRINUSE) {
1116 continue;
1117 }
1118 throw e.rethrowAsIOException();
1119 }
1120 }
1121 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1122 }
Nathan Harold93962f32017-03-07 13:23:36 -08001123
1124 /**
Benedict Wongbabe5d72017-12-03 19:42:36 -08001125 * Functional interface to do traffic tagging of given sockets to UIDs.
1126 *
1127 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1128 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1129 *
1130 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1131 * methods that cannot be easily mocked/tested.
1132 */
1133 @VisibleForTesting
1134 public interface UidFdTagger {
1135 /**
1136 * Sets socket tag to assign all traffic to the provided UID.
1137 *
1138 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1139 * should be accounted to the UID of the unprivileged application.
1140 */
1141 public void tag(FileDescriptor fd, int uid) throws IOException;
1142 }
1143
1144 /**
Nathan Harold93962f32017-03-07 13:23:36 -08001145 * Open a socket via the system server and bind it to the specified port (random if port=0).
1146 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1147 * cache the socket and a record of its owner so that it can and must be freed when no longer
1148 * needed.
1149 */
1150 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001151 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1152 throws RemoteException {
1153 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1154 throw new IllegalArgumentException(
1155 "Specified port number must be a valid non-reserved UDP port");
1156 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001157 checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
1158
Benedict Wongbabe5d72017-12-03 19:42:36 -08001159 int callingUid = Binder.getCallingUid();
1160 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldd8c74292017-12-13 19:16:33 -08001161 final int resourceId = mNextResourceId++;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001162 FileDescriptor sockFd = null;
1163 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001164 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001165 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1166 }
1167
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001168 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Benedict Wongbabe5d72017-12-03 19:42:36 -08001169 mUidFdTagger.tag(sockFd, callingUid);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001170
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001171 // This code is common to both the unspecified and specified port cases
1172 Os.setsockoptInt(
1173 sockFd,
1174 OsConstants.IPPROTO_UDP,
1175 OsConstants.UDP_ENCAP,
1176 OsConstants.UDP_ENCAP_ESPINUDP);
1177
Benedict Wongba8d3132017-12-06 21:56:35 -08001178 mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
1179 if (port != 0) {
1180 Log.v(TAG, "Binding to port " + port);
1181 Os.bind(sockFd, INADDR_ANY, port);
1182 } else {
1183 port = bindToRandomPort(sockFd);
1184 }
1185
Benedict Wong344bd622017-11-16 15:27:22 -08001186 userRecord.mEncapSocketRecords.put(
1187 resourceId,
1188 new RefcountedResource<EncapSocketRecord>(
1189 new EncapSocketRecord(resourceId, sockFd, port), binder));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001190 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
1191 } catch (IOException | ErrnoException e) {
1192 IoUtils.closeQuietly(sockFd);
1193 }
1194 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1195 // The only reasonable condition that would cause that is resource unavailable.
1196 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001197 }
1198
1199 /** close a socket that has been been allocated by and registered with the system server */
1200 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001201 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1202 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1203 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001204 }
Nathan Harold93962f32017-03-07 13:23:36 -08001205
Benedict Wong8149f6e2018-01-18 18:31:45 -08001206 /**
1207 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1208 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1209 * needed.
1210 */
1211 @Override
1212 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
1213 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder) {
1214 checkNotNull(binder, "Null Binder passed to createTunnelInterface");
1215 checkNotNull(underlyingNetwork, "No underlying network was specified");
1216 checkInetAddress(localAddr);
1217 checkInetAddress(remoteAddr);
1218
1219 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1220 // network (b/72316676).
1221
1222 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1223 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1224 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1225 }
1226
1227 final int resourceId = mNextResourceId++;
1228 final int ikey = reserveNetId();
1229 final int okey = reserveNetId();
1230 String intfName = String.format("%s%d", TUNNEL_INTERFACE_PREFIX, resourceId);
1231
1232 // TODO: Add calls to netd:
1233 // Create VTI
1234 // Add inbound/outbound global policies
1235 // (use reqid = 0)
1236
1237 userRecord.mTunnelInterfaceRecords.put(
1238 resourceId,
1239 new RefcountedResource<TunnelInterfaceRecord>(
1240 new TunnelInterfaceRecord(
1241 resourceId,
1242 intfName,
1243 underlyingNetwork,
1244 localAddr,
1245 remoteAddr,
1246 ikey,
1247 okey),
1248 binder));
1249 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1250 }
1251
1252 /**
1253 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1254 * from multiple local IP addresses over the same tunnel.
1255 */
1256 @Override
1257 public synchronized void addAddressToTunnelInterface(int tunnelResourceId, String localAddr) {
1258 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1259
1260 // Get tunnelInterface record; if no such interface is found, will throw
1261 // IllegalArgumentException
1262 TunnelInterfaceRecord tunnelInterfaceInfo =
1263 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1264
1265 // TODO: Add calls to netd:
1266 // Add address to TunnelInterface
1267 }
1268
1269 /**
1270 * Remove a new local address from the tunnel interface. After removal, the address will no
1271 * longer be available to send from, or receive on.
1272 */
1273 @Override
1274 public synchronized void removeAddressFromTunnelInterface(
1275 int tunnelResourceId, String localAddr) {
1276 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1277
1278 // Get tunnelInterface record; if no such interface is found, will throw
1279 // IllegalArgumentException
1280 TunnelInterfaceRecord tunnelInterfaceInfo =
1281 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1282
1283 // TODO: Add calls to netd:
1284 // Remove address from TunnelInterface
1285 }
1286
1287 /**
1288 * Delete a TunnelInterface that has been been allocated by and registered with the system
1289 * server
1290 */
1291 @Override
1292 public synchronized void deleteTunnelInterface(int resourceId) throws RemoteException {
1293 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1294 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1295 }
1296
Benedict Wong4f255702017-11-06 20:49:10 -08001297 @VisibleForTesting
Nathan Harolda2523312018-01-05 19:25:13 -08001298 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1299 IpSecAlgorithm auth = config.getAuthentication();
1300 IpSecAlgorithm crypt = config.getEncryption();
1301 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong4f255702017-11-06 20:49:10 -08001302
Nathan Harolda2523312018-01-05 19:25:13 -08001303 // Validate the algorithm set
1304 Preconditions.checkArgument(
1305 aead != null || crypt != null || auth != null,
1306 "No Encryption or Authentication algorithms specified");
1307 Preconditions.checkArgument(
1308 auth == null || auth.isAuthentication(),
1309 "Unsupported algorithm for Authentication");
1310 Preconditions.checkArgument(
Benedict Wong4f255702017-11-06 20:49:10 -08001311 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harolda2523312018-01-05 19:25:13 -08001312 Preconditions.checkArgument(
1313 aead == null || aead.isAead(),
1314 "Unsupported algorithm for Authenticated Encryption");
1315 Preconditions.checkArgument(
1316 aead == null || (auth == null && crypt == null),
1317 "Authenticated Encryption is mutually exclusive with other Authentication "
1318 + "or Encryption algorithms");
Benedict Wong4f255702017-11-06 20:49:10 -08001319 }
1320
Nathan Harold93962f32017-03-07 13:23:36 -08001321 /**
Nathan Harolda10003d2017-08-23 13:46:33 -07001322 * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
1323 * IllegalArgumentException if they are not.
1324 */
1325 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong344bd622017-11-16 15:27:22 -08001326 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1327
Nathan Harolda10003d2017-08-23 13:46:33 -07001328 switch (config.getEncapType()) {
1329 case IpSecTransform.ENCAP_NONE:
1330 break;
1331 case IpSecTransform.ENCAP_ESPINUDP:
1332 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong344bd622017-11-16 15:27:22 -08001333 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1334 userRecord.mEncapSocketRecords.getResourceOrThrow(
1335 config.getEncapSocketResourceId());
Nathan Harolda10003d2017-08-23 13:46:33 -07001336
1337 int port = config.getEncapRemotePort();
1338 if (port <= 0 || port > 0xFFFF) {
1339 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1340 }
1341 break;
1342 default:
1343 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1344 }
1345
Nathan Harolda2523312018-01-05 19:25:13 -08001346 validateAlgorithms(config);
Nathan Harolda10003d2017-08-23 13:46:33 -07001347
Nathan Harolda2523312018-01-05 19:25:13 -08001348 // Retrieve SPI record; will throw IllegalArgumentException if not found
1349 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1350
Benedict Wonge6b42772017-12-13 18:26:40 -08001351 // Check to ensure that SPI has not already been used.
1352 if (s.getOwnedByTransform()) {
1353 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1354 }
1355
Nathan Harolda2523312018-01-05 19:25:13 -08001356 // If no remote address is supplied, then use one from the SPI.
1357 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1358 config.setDestinationAddress(s.getDestinationAddress());
1359 }
1360
1361 // All remote addresses must match
1362 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1363 throw new IllegalArgumentException("Mismatched remote addresseses.");
1364 }
1365
1366 // This check is technically redundant due to the chain of custody between the SPI and
1367 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1368 // the transform, this will prevent us from messing up.
1369 checkInetAddress(config.getDestinationAddress());
1370
1371 // Require a valid source address for all transforms.
1372 checkInetAddress(config.getSourceAddress());
1373
1374 switch (config.getMode()) {
1375 case IpSecTransform.MODE_TRANSPORT:
1376 case IpSecTransform.MODE_TUNNEL:
1377 break;
1378 default:
1379 throw new IllegalArgumentException(
1380 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harolda10003d2017-08-23 13:46:33 -07001381 }
1382 }
1383
1384 /**
Nathan Harold93962f32017-03-07 13:23:36 -08001385 * Create a transport mode transform, which represent two security associations (one in each
1386 * direction) in the kernel. The transform will be cached by the system server and must be freed
1387 * when no longer needed. It is possible to free one, deleting the SA from underneath sockets
1388 * that are using it, which will result in all of those sockets becoming unable to send or
1389 * receive data.
1390 */
1391 @Override
Benedict Wongf33f03132018-01-18 14:38:16 -08001392 public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder)
1393 throws RemoteException {
Nathan Harolda10003d2017-08-23 13:46:33 -07001394 checkIpSecConfig(c);
Benedict Wongf33f03132018-01-18 14:38:16 -08001395 checkNotNull(binder, "Null Binder passed to createTransform");
Nathan Haroldd8c74292017-12-13 19:16:33 -08001396 final int resourceId = mNextResourceId++;
Benedict Wong344bd622017-11-16 15:27:22 -08001397
1398 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong4f9fb812017-12-13 17:16:53 -08001399 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong344bd622017-11-16 15:27:22 -08001400
1401 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001402 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1403 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001404
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001405 int encapType, encapLocalPort = 0, encapRemotePort = 0;
Benedict Wong344bd622017-11-16 15:27:22 -08001406 EncapSocketRecord socketRecord = null;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001407 encapType = c.getEncapType();
1408 if (encapType != IpSecTransform.ENCAP_NONE) {
Benedict Wong344bd622017-11-16 15:27:22 -08001409 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1410 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1411 c.getEncapSocketResourceId());
1412 dependencies.add(refcountedSocketRecord);
1413
1414 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001415 encapLocalPort = socketRecord.getPort();
1416 encapRemotePort = c.getEncapRemotePort();
1417 }
1418
Nathan Harolda2523312018-01-05 19:25:13 -08001419 IpSecAlgorithm auth = c.getAuthentication();
1420 IpSecAlgorithm crypt = c.getEncryption();
1421 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001422
Nathan Harolda2523312018-01-05 19:25:13 -08001423 RefcountedResource<SpiRecord> refcountedSpiRecord =
1424 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1425 dependencies.add(refcountedSpiRecord);
1426 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong344bd622017-11-16 15:27:22 -08001427
Nathan Harolda2523312018-01-05 19:25:13 -08001428 try {
1429 mSrvConfig
1430 .getNetdInstance()
1431 .ipSecAddSecurityAssociation(
1432 resourceId,
1433 c.getMode(),
1434 c.getSourceAddress(),
1435 c.getDestinationAddress(),
1436 (c.getNetwork() != null) ? c.getNetwork().netId : 0,
1437 spiRecord.getSpi(),
Di Lu0b611f42018-01-11 11:35:25 -08001438 c.getMarkValue(),
1439 c.getMarkMask(),
Nathan Harolda2523312018-01-05 19:25:13 -08001440 (auth != null) ? auth.getName() : "",
1441 (auth != null) ? auth.getKey() : new byte[] {},
1442 (auth != null) ? auth.getTruncationLengthBits() : 0,
1443 (crypt != null) ? crypt.getName() : "",
1444 (crypt != null) ? crypt.getKey() : new byte[] {},
1445 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1446 (authCrypt != null) ? authCrypt.getName() : "",
1447 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1448 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1449 encapType,
1450 encapLocalPort,
1451 encapRemotePort);
1452 } catch (ServiceSpecificException e) {
1453 // FIXME: get the error code and throw is at an IOException from Errno Exception
1454 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001455 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001456 // Both SAs were created successfully, time to construct a record and lock it away
Benedict Wong344bd622017-11-16 15:27:22 -08001457 userRecord.mTransformRecords.put(
1458 resourceId,
1459 new RefcountedResource<TransformRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001460 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong344bd622017-11-16 15:27:22 -08001461 binder,
1462 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001463 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001464 }
1465
1466 /**
1467 * Delete a transport mode transform that was previously allocated by + registered with the
1468 * system server. If this is called on an inactive (or non-existent) transform, it will not
1469 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1470 * other reasons.
1471 */
1472 @Override
Benedict Wongf33f03132018-01-18 14:38:16 -08001473 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001474 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1475 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001476 }
1477
1478 /**
1479 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1480 * association as a correspondent policy to the provided socket
1481 */
1482 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001483 public synchronized void applyTransportModeTransform(
Nathan Harolda2523312018-01-05 19:25:13 -08001484 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001485 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Nathan Harolda2523312018-01-05 19:25:13 -08001486 checkDirection(direction);
Benedict Wong344bd622017-11-16 15:27:22 -08001487 // Get transform record; if no transform is found, will throw IllegalArgumentException
1488 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001489
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001490 // TODO: make this a function.
1491 if (info.pid != getCallingPid() || info.uid != getCallingUid()) {
1492 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1493 }
1494
Benedict Wong8149f6e2018-01-18 18:31:45 -08001495 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001496 IpSecConfig c = info.getConfig();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001497 Preconditions.checkArgument(
1498 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1499 "Transform mode was not Transport mode; cannot be applied to a socket");
1500
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001501 try {
Nathan Harolda2523312018-01-05 19:25:13 -08001502 mSrvConfig
1503 .getNetdInstance()
1504 .ipSecApplyTransportModeTransform(
1505 socket.getFileDescriptor(),
1506 resourceId,
1507 direction,
1508 c.getSourceAddress(),
1509 c.getDestinationAddress(),
1510 info.getSpiRecord().getSpi());
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001511 } catch (ServiceSpecificException e) {
manojboopathic4be79d2017-11-30 17:11:49 -08001512 if (e.errorCode == EINVAL) {
1513 throw new IllegalArgumentException(e.toString());
1514 } else {
1515 throw e;
1516 }
Nathan Harold93962f32017-03-07 13:23:36 -08001517 }
1518 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001519
Nathan Harold93962f32017-03-07 13:23:36 -08001520 /**
Nathan Harolda2523312018-01-05 19:25:13 -08001521 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1522 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1523 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1524 * reserved for future improved input validation.
Nathan Harold93962f32017-03-07 13:23:36 -08001525 */
1526 @Override
Nathan Haroldf73d2522018-01-17 01:00:20 -08001527 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1528 throws RemoteException {
Nathan Harold93962f32017-03-07 13:23:36 -08001529 try {
ludi1a06aa72017-05-12 09:15:00 -07001530 mSrvConfig
1531 .getNetdInstance()
1532 .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
Nathan Harold93962f32017-03-07 13:23:36 -08001533 } catch (ServiceSpecificException e) {
1534 // FIXME: get the error code and throw is at an IOException from Errno Exception
1535 }
1536 }
1537
Benedict Wong8149f6e2018-01-18 18:31:45 -08001538 /**
1539 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1540 * security association as a correspondent policy to the provided interface
1541 */
1542 @Override
1543 public synchronized void applyTunnelModeTransform(
1544 int tunnelResourceId, int direction, int transformResourceId) throws RemoteException {
1545 checkDirection(direction);
1546
1547 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1548
1549 // Get transform record; if no transform is found, will throw IllegalArgumentException
1550 TransformRecord transformInfo =
1551 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1552
1553 // Get tunnelInterface record; if no such interface is found, will throw
1554 // IllegalArgumentException
1555 TunnelInterfaceRecord tunnelInterfaceInfo =
1556 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1557
1558 // Get config and check that to-be-applied transform has the correct mode
1559 IpSecConfig c = transformInfo.getConfig();
1560 Preconditions.checkArgument(
1561 c.getMode() == IpSecTransform.MODE_TUNNEL,
1562 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1563
1564 int mark =
1565 (direction == IpSecManager.DIRECTION_IN)
1566 ? tunnelInterfaceInfo.getIkey()
1567 : tunnelInterfaceInfo.getOkey();
1568
1569 // TODO: Add calls to netd:
1570 // Update SA with tunnel mark (ikey or okey based on direction)
1571 // If outbound, add SPI to policy
1572 }
1573
Nathan Harold93962f32017-03-07 13:23:36 -08001574 @Override
ludib0c95b12017-05-22 10:52:23 -07001575 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001576 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludib0c95b12017-05-22 10:52:23 -07001577
1578 pw.println("IpSecService dump:");
Nathan Harold1afbef42017-03-01 18:55:06 -08001579 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1580 pw.println();
ludib0c95b12017-05-22 10:52:23 -07001581
Benedict Wong344bd622017-11-16 15:27:22 -08001582 pw.println("mUserResourceTracker:");
1583 pw.println(mUserResourceTracker);
Nathan Harold1afbef42017-03-01 18:55:06 -08001584 }
1585}