blob: 45a4dfb91bbf82aa8e47d406dd54611392384bae [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;
68import java.util.List;
Nathan Harolda10003d2017-08-23 13:46:33 -070069
Nathan Harold8dc1fd02017-04-04 19:37:48 -070070import libcore.io.IoUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080071
Benedict Wong409c8ca2017-10-26 19:41:43 -070072/**
73 * A service to manage multiple clients that want to access the IpSec API. The service is
74 * responsible for maintaining a list of clients and managing the resources (and related quotas)
75 * that each of them own.
76 *
77 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
78 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
79 * thread is ever running at a time.
80 *
81 * @hide
82 */
Nathan Harold1afbef42017-03-01 18:55:06 -080083public class IpSecService extends IIpSecService.Stub {
84 private static final String TAG = "IpSecService";
85 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Nathan Harold8dc1fd02017-04-04 19:37:48 -070086
Nathan Harold1afbef42017-03-01 18:55:06 -080087 private static final String NETD_SERVICE_NAME = "netd";
Nathan Harold93962f32017-03-07 13:23:36 -080088 private static final int[] DIRECTIONS =
Nathan Harolda2523312018-01-05 19:25:13 -080089 new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN};
Benedict Wongb8ef5412018-01-24 15:31:39 -080090 private static final String[] WILDCARD_ADDRESSES = new String[]{"0.0.0.0", "::"};
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)
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(
615 mResourceId,
616 mConfig.getSourceAddress(),
617 mConfig.getDestinationAddress(),
Di Lu0b611f42018-01-11 11:35:25 -0800618 spi,
619 mConfig.getMarkValue(),
620 mConfig.getMarkMask());
Nathan Harolda2523312018-01-05 19:25:13 -0800621 } catch (ServiceSpecificException e) {
622 // FIXME: get the error code and throw is at an IOException from Errno Exception
623 } catch (RemoteException e) {
624 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
Nathan Harold93962f32017-03-07 13:23:36 -0800625 }
Nathan Harold93962f32017-03-07 13:23:36 -0800626
Benedict Wong344bd622017-11-16 15:27:22 -0800627 getResourceTracker().give();
Nathan Harold93962f32017-03-07 13:23:36 -0800628 }
ludib0c95b12017-05-22 10:52:23 -0700629
Benedict Wong344bd622017-11-16 15:27:22 -0800630 @Override
631 public void invalidate() throws RemoteException {
632 getUserRecord().removeTransformRecord(mResourceId);
633 }
634
635 @Override
Nathan Harolda1afbd82017-04-24 16:16:34 -0700636 protected ResourceTracker getResourceTracker() {
Benedict Wong344bd622017-11-16 15:27:22 -0800637 return getUserRecord().mTransformQuotaTracker;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700638 }
639
ludib0c95b12017-05-22 10:52:23 -0700640 @Override
641 public String toString() {
642 StringBuilder strBuilder = new StringBuilder();
643 strBuilder
644 .append("{super=")
645 .append(super.toString())
646 .append(", mSocket=")
647 .append(mSocket)
Nathan Harolda2523312018-01-05 19:25:13 -0800648 .append(", mSpi.mResourceId=")
649 .append(mSpi.mResourceId)
ludib0c95b12017-05-22 10:52:23 -0700650 .append(", mConfig=")
651 .append(mConfig)
652 .append("}");
653 return strBuilder.toString();
654 }
Nathan Harold93962f32017-03-07 13:23:36 -0800655 }
656
Benedict Wong4f9fb812017-12-13 17:16:53 -0800657 /**
658 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
659 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
660 * object
661 */
662 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harolda2523312018-01-05 19:25:13 -0800663 private final String mSourceAddress;
664 private final String mDestinationAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800665 private int mSpi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700666
667 private boolean mOwnedByTransform = false;
Nathan Harold93962f32017-03-07 13:23:36 -0800668
Nathan Harolda2523312018-01-05 19:25:13 -0800669 SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
Benedict Wong344bd622017-11-16 15:27:22 -0800670 super(resourceId);
Nathan Harolda2523312018-01-05 19:25:13 -0800671 mSourceAddress = sourceAddress;
672 mDestinationAddress = destinationAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800673 mSpi = spi;
Nathan Harold93962f32017-03-07 13:23:36 -0800674 }
675
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700676 /** always guarded by IpSecService#this */
677 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800678 public void freeUnderlyingResources() {
Nathan Harold93962f32017-03-07 13:23:36 -0800679 try {
ludi1a06aa72017-05-12 09:15:00 -0700680 mSrvConfig
681 .getNetdInstance()
Nathan Harold93962f32017-03-07 13:23:36 -0800682 .ipSecDeleteSecurityAssociation(
Di Lu0b611f42018-01-11 11:35:25 -0800683 mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
Nathan Harold93962f32017-03-07 13:23:36 -0800684 } catch (ServiceSpecificException e) {
685 // FIXME: get the error code and throw is at an IOException from Errno Exception
686 } catch (RemoteException e) {
687 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId);
688 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700689
690 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -0800691
Benedict Wong344bd622017-11-16 15:27:22 -0800692 getResourceTracker().give();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700693 }
694
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700695 public int getSpi() {
696 return mSpi;
697 }
698
Nathan Harolda2523312018-01-05 19:25:13 -0800699 public String getDestinationAddress() {
700 return mDestinationAddress;
701 }
702
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700703 public void setOwnedByTransform() {
704 if (mOwnedByTransform) {
705 // Programming error
Andreas Gamped6d8e452017-07-11 10:25:09 -0700706 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700707 }
708
709 mOwnedByTransform = true;
Nathan Harold93962f32017-03-07 13:23:36 -0800710 }
ludib0c95b12017-05-22 10:52:23 -0700711
Benedict Wonge6b42772017-12-13 18:26:40 -0800712 public boolean getOwnedByTransform() {
713 return mOwnedByTransform;
714 }
715
ludib0c95b12017-05-22 10:52:23 -0700716 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800717 public void invalidate() throws RemoteException {
718 getUserRecord().removeSpiRecord(mResourceId);
719 }
720
721 @Override
722 protected ResourceTracker getResourceTracker() {
723 return getUserRecord().mSpiQuotaTracker;
724 }
725
726 @Override
ludib0c95b12017-05-22 10:52:23 -0700727 public String toString() {
728 StringBuilder strBuilder = new StringBuilder();
729 strBuilder
730 .append("{super=")
731 .append(super.toString())
732 .append(", mSpi=")
733 .append(mSpi)
Nathan Harolda2523312018-01-05 19:25:13 -0800734 .append(", mSourceAddress=")
735 .append(mSourceAddress)
736 .append(", mDestinationAddress=")
737 .append(mDestinationAddress)
ludib0c95b12017-05-22 10:52:23 -0700738 .append(", mOwnedByTransform=")
739 .append(mOwnedByTransform)
740 .append("}");
741 return strBuilder.toString();
742 }
Nathan Harold93962f32017-03-07 13:23:36 -0800743 }
744
Benedict Wong8149f6e2018-01-18 18:31:45 -0800745 // These values have been reserved in ConnectivityService
746 @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
747
748 @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400;
749
750 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
751 private int mNextTunnelNetIdIndex = 0;
752
753 /**
754 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
755 *
756 * <p>This method should only be called from Binder threads. Do not call this from within the
757 * system server as it will crash the system on failure.
758 *
759 * @return an integer key within the netId range, if successful
760 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
761 */
762 @VisibleForTesting
763 int reserveNetId() {
764 synchronized (mTunnelNetIds) {
765 for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
766 int index = mNextTunnelNetIdIndex;
767 int netId = index + TUN_INTF_NETID_START;
768 if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
769 if (!mTunnelNetIds.get(netId)) {
770 mTunnelNetIds.put(netId, true);
771 return netId;
772 }
773 }
774 }
775 throw new IllegalStateException("No free netIds to allocate");
776 }
777
778 @VisibleForTesting
779 void releaseNetId(int netId) {
780 synchronized (mTunnelNetIds) {
781 mTunnelNetIds.delete(netId);
782 }
783 }
784
785 private final class TunnelInterfaceRecord extends OwnedResourceRecord {
786 private final String mInterfaceName;
787 private final Network mUnderlyingNetwork;
788
789 // outer addresses
790 private final String mLocalAddress;
791 private final String mRemoteAddress;
792
793 private final int mIkey;
794 private final int mOkey;
795
796 TunnelInterfaceRecord(
797 int resourceId,
798 String interfaceName,
799 Network underlyingNetwork,
800 String localAddr,
801 String remoteAddr,
802 int ikey,
803 int okey) {
804 super(resourceId);
805
806 mInterfaceName = interfaceName;
807 mUnderlyingNetwork = underlyingNetwork;
808 mLocalAddress = localAddr;
809 mRemoteAddress = remoteAddr;
810 mIkey = ikey;
811 mOkey = okey;
812 }
813
814 /** always guarded by IpSecService#this */
815 @Override
816 public void freeUnderlyingResources() {
Benedict Wong0fe58a92018-01-19 17:36:02 -0800817 // Calls to netd
Benedict Wong8149f6e2018-01-18 18:31:45 -0800818 // Teardown VTI
819 // Delete global policies
Benedict Wong0fe58a92018-01-19 17:36:02 -0800820 try {
821 mSrvConfig.getNetdInstance().removeVirtualTunnelInterface(mInterfaceName);
822
Benedict Wongb8ef5412018-01-24 15:31:39 -0800823 for(String wildcardAddr : WILDCARD_ADDRESSES) {
824 for (int direction : DIRECTIONS) {
825 int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey;
826 mSrvConfig
827 .getNetdInstance()
828 .ipSecDeleteSecurityPolicy(
829 0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff);
830 }
Benedict Wong0fe58a92018-01-19 17:36:02 -0800831 }
832 } catch (ServiceSpecificException e) {
833 // FIXME: get the error code and throw is at an IOException from Errno Exception
834 } catch (RemoteException e) {
835 Log.e(
836 TAG,
837 "Failed to delete VTI with interface name: "
838 + mInterfaceName
839 + " and id: "
840 + mResourceId);
841 }
Benedict Wong8149f6e2018-01-18 18:31:45 -0800842
843 getResourceTracker().give();
844 releaseNetId(mIkey);
845 releaseNetId(mOkey);
846 }
847
848 public String getInterfaceName() {
849 return mInterfaceName;
850 }
851
852 public Network getUnderlyingNetwork() {
853 return mUnderlyingNetwork;
854 }
855
856 /** Returns the local, outer address for the tunnelInterface */
857 public String getLocalAddress() {
858 return mLocalAddress;
859 }
860
861 /** Returns the remote, outer address for the tunnelInterface */
862 public String getRemoteAddress() {
863 return mRemoteAddress;
864 }
865
866 public int getIkey() {
867 return mIkey;
868 }
869
870 public int getOkey() {
871 return mOkey;
872 }
873
874 @Override
875 protected ResourceTracker getResourceTracker() {
876 return getUserRecord().mTunnelQuotaTracker;
877 }
878
879 @Override
880 public void invalidate() {
881 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
882 }
883
884 @Override
885 public String toString() {
886 return new StringBuilder()
887 .append("{super=")
888 .append(super.toString())
889 .append(", mInterfaceName=")
890 .append(mInterfaceName)
891 .append(", mUnderlyingNetwork=")
892 .append(mUnderlyingNetwork)
893 .append(", mLocalAddress=")
894 .append(mLocalAddress)
895 .append(", mRemoteAddress=")
896 .append(mRemoteAddress)
897 .append(", mIkey=")
898 .append(mIkey)
899 .append(", mOkey=")
900 .append(mOkey)
901 .append("}")
902 .toString();
903 }
904 }
905
Benedict Wong4f9fb812017-12-13 17:16:53 -0800906 /**
907 * Tracks a UDP encap socket, and manages cleanup paths
908 *
909 * <p>While this class does not manage non-kernel resources, race conditions around socket
910 * binding require that the service creates the encap socket, binds it and applies the socket
911 * policy before handing it to a user.
912 */
913 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700914 private FileDescriptor mSocket;
915 private final int mPort;
Nathan Harold93962f32017-03-07 13:23:36 -0800916
Benedict Wong344bd622017-11-16 15:27:22 -0800917 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
918 super(resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700919 mSocket = socket;
920 mPort = port;
921 }
922
923 /** always guarded by IpSecService#this */
924 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800925 public void freeUnderlyingResources() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700926 Log.d(TAG, "Closing port " + mPort);
927 IoUtils.closeQuietly(mSocket);
928 mSocket = null;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700929
Benedict Wong344bd622017-11-16 15:27:22 -0800930 getResourceTracker().give();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700931 }
932
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700933 public int getPort() {
934 return mPort;
935 }
936
937 public FileDescriptor getSocket() {
938 return mSocket;
939 }
ludib0c95b12017-05-22 10:52:23 -0700940
941 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800942 protected ResourceTracker getResourceTracker() {
943 return getUserRecord().mSocketQuotaTracker;
944 }
945
946 @Override
947 public void invalidate() {
948 getUserRecord().removeEncapSocketRecord(mResourceId);
949 }
950
951 @Override
ludib0c95b12017-05-22 10:52:23 -0700952 public String toString() {
953 return new StringBuilder()
954 .append("{super=")
955 .append(super.toString())
956 .append(", mSocket=")
957 .append(mSocket)
958 .append(", mPort=")
959 .append(mPort)
960 .append("}")
961 .toString();
962 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700963 }
Nathan Harold93962f32017-03-07 13:23:36 -0800964
Nathan Harold1afbef42017-03-01 18:55:06 -0800965 /**
966 * Constructs a new IpSecService instance
967 *
968 * @param context Binder context for this service
969 */
970 private IpSecService(Context context) {
ludi1a06aa72017-05-12 09:15:00 -0700971 this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
Nathan Harold1afbef42017-03-01 18:55:06 -0800972 }
973
974 static IpSecService create(Context context) throws InterruptedException {
975 final IpSecService service = new IpSecService(context);
976 service.connectNativeNetdService();
977 return service;
978 }
979
ludi1a06aa72017-05-12 09:15:00 -0700980 /** @hide */
981 @VisibleForTesting
982 public IpSecService(Context context, IpSecServiceConfiguration config) {
Nathan Harolda2523312018-01-05 19:25:13 -0800983 this(
984 context,
985 config,
986 (fd, uid) -> {
987 try {
988 TrafficStats.setThreadStatsUid(uid);
989 TrafficStats.tagFileDescriptor(fd);
990 } finally {
991 TrafficStats.clearThreadStatsUid();
992 }
993 });
Benedict Wongbabe5d72017-12-03 19:42:36 -0800994 }
995
996 /** @hide */
997 @VisibleForTesting
998 public IpSecService(
999 Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
ludi1a06aa72017-05-12 09:15:00 -07001000 mContext = context;
1001 mSrvConfig = config;
Benedict Wongbabe5d72017-12-03 19:42:36 -08001002 mUidFdTagger = uidFdTagger;
ludi1a06aa72017-05-12 09:15:00 -07001003 }
1004
Nathan Harold1afbef42017-03-01 18:55:06 -08001005 public void systemReady() {
1006 if (isNetdAlive()) {
1007 Slog.d(TAG, "IpSecService is ready");
1008 } else {
1009 Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
1010 }
1011 }
1012
1013 private void connectNativeNetdService() {
1014 // Avoid blocking the system server to do this
Nathan Haroldb0e05082017-07-17 14:01:53 -07001015 new Thread() {
1016 @Override
1017 public void run() {
1018 synchronized (IpSecService.this) {
ludi1a06aa72017-05-12 09:15:00 -07001019 NetdService.get(NETD_FETCH_TIMEOUT_MS);
Nathan Haroldb0e05082017-07-17 14:01:53 -07001020 }
1021 }
1022 }.start();
Nathan Harold1afbef42017-03-01 18:55:06 -08001023 }
1024
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001025 synchronized boolean isNetdAlive() {
1026 try {
ludi1a06aa72017-05-12 09:15:00 -07001027 final INetd netd = mSrvConfig.getNetdInstance();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001028 if (netd == null) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001029 return false;
1030 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001031 return netd.isAlive();
1032 } catch (RemoteException re) {
1033 return false;
Nathan Harold1afbef42017-03-01 18:55:06 -08001034 }
1035 }
1036
Nathan Harolda10003d2017-08-23 13:46:33 -07001037 /**
1038 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1039 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1040 */
1041 private static void checkInetAddress(String inetAddress) {
1042 if (TextUtils.isEmpty(inetAddress)) {
1043 throw new IllegalArgumentException("Unspecified address");
1044 }
1045
1046 InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
1047
1048 if (checkAddr.isAnyLocalAddress()) {
1049 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1050 }
1051 }
1052
1053 /**
1054 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1055 * DIRECTION_IN or DIRECTION_OUT
1056 */
1057 private static void checkDirection(int direction) {
1058 switch (direction) {
Nathan Harolda2523312018-01-05 19:25:13 -08001059 case IpSecManager.DIRECTION_OUT:
1060 case IpSecManager.DIRECTION_IN:
Nathan Harolda10003d2017-08-23 13:46:33 -07001061 return;
1062 }
1063 throw new IllegalArgumentException("Invalid Direction: " + direction);
1064 }
1065
Nathan Harold93962f32017-03-07 13:23:36 -08001066 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001067 @Override
1068 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harolda2523312018-01-05 19:25:13 -08001069 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1070 checkInetAddress(destinationAddress);
Nathan Harolda10003d2017-08-23 13:46:33 -07001071 /* requestedSpi can be anything in the int range, so no check is needed. */
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001072 checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harolda10003d2017-08-23 13:46:33 -07001073
Benedict Wong344bd622017-11-16 15:27:22 -08001074 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Nathan Haroldd8c74292017-12-13 19:16:33 -08001075 final int resourceId = mNextResourceId++;
Nathan Harold93962f32017-03-07 13:23:36 -08001076
1077 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -08001078 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001079 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001080 return new IpSecSpiResponse(
Nathan Harolda10003d2017-08-23 13:46:33 -07001081 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harolda1afbd82017-04-24 16:16:34 -07001082 }
Nathan Harolda2523312018-01-05 19:25:13 -08001083
Nathan Harold93962f32017-03-07 13:23:36 -08001084 spi =
ludi1a06aa72017-05-12 09:15:00 -07001085 mSrvConfig
1086 .getNetdInstance()
Nathan Harolda2523312018-01-05 19:25:13 -08001087 .ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi);
Nathan Harold93962f32017-03-07 13:23:36 -08001088 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong344bd622017-11-16 15:27:22 -08001089 userRecord.mSpiRecords.put(
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001090 resourceId,
Benedict Wong344bd622017-11-16 15:27:22 -08001091 new RefcountedResource<SpiRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001092 new SpiRecord(resourceId, "", destinationAddress, spi), binder));
Nathan Harold93962f32017-03-07 13:23:36 -08001093 } catch (ServiceSpecificException e) {
1094 // TODO: Add appropriate checks when other ServiceSpecificException types are supported
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001095 return new IpSecSpiResponse(
Nathan Harolda1afbd82017-04-24 16:16:34 -07001096 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold93962f32017-03-07 13:23:36 -08001097 } catch (RemoteException e) {
1098 throw e.rethrowFromSystemServer();
1099 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001100 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1101 }
1102
1103 /* This method should only be called from Binder threads. Do not call this from
1104 * within the system server as it will crash the system on failure.
1105 */
Benedict Wong344bd622017-11-16 15:27:22 -08001106 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001107 throws RemoteException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001108
Benedict Wong344bd622017-11-16 15:27:22 -08001109 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold93962f32017-03-07 13:23:36 -08001110 }
1111
1112 /** Release a previously allocated SPI that has been registered with the system server */
1113 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001114 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1115 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1116 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001117 }
1118
1119 /**
1120 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1121 * be unbound.
1122 *
1123 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1124 * a random open port and then bind by number, this function creates a temp socket, binds to a
1125 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1126 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1127 * FileHandle.
1128 *
1129 * <p>The loop in this function handles the inherent race window between un-binding to a port
1130 * and re-binding, during which the system could *technically* hand that port out to someone
1131 * else.
1132 */
Benedict Wongf186d672017-10-10 20:44:28 -07001133 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001134 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1135 try {
1136 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1137 Os.bind(probeSocket, INADDR_ANY, 0);
1138 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1139 Os.close(probeSocket);
1140 Log.v(TAG, "Binding to port " + port);
1141 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongf186d672017-10-10 20:44:28 -07001142 return port;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001143 } catch (ErrnoException e) {
1144 // Someone miraculously claimed the port just after we closed probeSocket.
1145 if (e.errno == OsConstants.EADDRINUSE) {
1146 continue;
1147 }
1148 throw e.rethrowAsIOException();
1149 }
1150 }
1151 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1152 }
Nathan Harold93962f32017-03-07 13:23:36 -08001153
1154 /**
Benedict Wongbabe5d72017-12-03 19:42:36 -08001155 * Functional interface to do traffic tagging of given sockets to UIDs.
1156 *
1157 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1158 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1159 *
1160 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1161 * methods that cannot be easily mocked/tested.
1162 */
1163 @VisibleForTesting
1164 public interface UidFdTagger {
1165 /**
1166 * Sets socket tag to assign all traffic to the provided UID.
1167 *
1168 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1169 * should be accounted to the UID of the unprivileged application.
1170 */
1171 public void tag(FileDescriptor fd, int uid) throws IOException;
1172 }
1173
1174 /**
Nathan Harold93962f32017-03-07 13:23:36 -08001175 * Open a socket via the system server and bind it to the specified port (random if port=0).
1176 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1177 * cache the socket and a record of its owner so that it can and must be freed when no longer
1178 * needed.
1179 */
1180 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001181 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1182 throws RemoteException {
1183 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1184 throw new IllegalArgumentException(
1185 "Specified port number must be a valid non-reserved UDP port");
1186 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001187 checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
1188
Benedict Wongbabe5d72017-12-03 19:42:36 -08001189 int callingUid = Binder.getCallingUid();
1190 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldd8c74292017-12-13 19:16:33 -08001191 final int resourceId = mNextResourceId++;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001192 FileDescriptor sockFd = null;
1193 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001194 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001195 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1196 }
1197
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001198 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Benedict Wongbabe5d72017-12-03 19:42:36 -08001199 mUidFdTagger.tag(sockFd, callingUid);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001200
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001201 // This code is common to both the unspecified and specified port cases
1202 Os.setsockoptInt(
1203 sockFd,
1204 OsConstants.IPPROTO_UDP,
1205 OsConstants.UDP_ENCAP,
1206 OsConstants.UDP_ENCAP_ESPINUDP);
1207
Benedict Wongba8d3132017-12-06 21:56:35 -08001208 mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
1209 if (port != 0) {
1210 Log.v(TAG, "Binding to port " + port);
1211 Os.bind(sockFd, INADDR_ANY, port);
1212 } else {
1213 port = bindToRandomPort(sockFd);
1214 }
1215
Benedict Wong344bd622017-11-16 15:27:22 -08001216 userRecord.mEncapSocketRecords.put(
1217 resourceId,
1218 new RefcountedResource<EncapSocketRecord>(
1219 new EncapSocketRecord(resourceId, sockFd, port), binder));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001220 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
1221 } catch (IOException | ErrnoException e) {
1222 IoUtils.closeQuietly(sockFd);
1223 }
1224 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1225 // The only reasonable condition that would cause that is resource unavailable.
1226 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001227 }
1228
1229 /** close a socket that has been been allocated by and registered with the system server */
1230 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001231 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1232 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1233 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001234 }
Nathan Harold93962f32017-03-07 13:23:36 -08001235
Benedict Wong8149f6e2018-01-18 18:31:45 -08001236 /**
1237 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1238 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1239 * needed.
1240 */
1241 @Override
1242 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
1243 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder) {
1244 checkNotNull(binder, "Null Binder passed to createTunnelInterface");
1245 checkNotNull(underlyingNetwork, "No underlying network was specified");
1246 checkInetAddress(localAddr);
1247 checkInetAddress(remoteAddr);
1248
1249 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1250 // network (b/72316676).
1251
1252 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1253 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1254 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1255 }
1256
1257 final int resourceId = mNextResourceId++;
1258 final int ikey = reserveNetId();
1259 final int okey = reserveNetId();
1260 String intfName = String.format("%s%d", TUNNEL_INTERFACE_PREFIX, resourceId);
1261
Benedict Wong0fe58a92018-01-19 17:36:02 -08001262 try {
1263 // Calls to netd:
1264 // Create VTI
1265 // Add inbound/outbound global policies
1266 // (use reqid = 0)
1267 mSrvConfig
1268 .getNetdInstance()
1269 .addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001270
Benedict Wongb8ef5412018-01-24 15:31:39 -08001271 for(String wildcardAddr : WILDCARD_ADDRESSES) {
1272 for (int direction : DIRECTIONS) {
1273 int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey;
Benedict Wong0fe58a92018-01-19 17:36:02 -08001274
Benedict Wongb8ef5412018-01-24 15:31:39 -08001275 mSrvConfig
1276 .getNetdInstance()
1277 .ipSecAddSecurityPolicy(
Benedict Wong0fe58a92018-01-19 17:36:02 -08001278 0, // Use 0 for reqId
1279 direction,
Benedict Wongb8ef5412018-01-24 15:31:39 -08001280 wildcardAddr,
1281 wildcardAddr,
Benedict Wong0fe58a92018-01-19 17:36:02 -08001282 0,
1283 mark,
1284 0xffffffff);
Benedict Wongb8ef5412018-01-24 15:31:39 -08001285 }
Benedict Wong0fe58a92018-01-19 17:36:02 -08001286 }
1287
1288 userRecord.mTunnelInterfaceRecords.put(
1289 resourceId,
1290 new RefcountedResource<TunnelInterfaceRecord>(
1291 new TunnelInterfaceRecord(
1292 resourceId,
1293 intfName,
1294 underlyingNetwork,
1295 localAddr,
1296 remoteAddr,
1297 ikey,
1298 okey),
1299 binder));
1300 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1301 } catch (RemoteException e) {
1302 // Release keys if we got an error.
1303 releaseNetId(ikey);
1304 releaseNetId(okey);
1305 throw e.rethrowFromSystemServer();
1306 } catch (ServiceSpecificException e) {
1307 // FIXME: get the error code and throw is at an IOException from Errno Exception
1308 }
1309
1310 // If we make it to here, then something has gone wrong and we couldn't create a VTI.
1311 // Release the keys that we reserved, and return an error status.
1312 releaseNetId(ikey);
1313 releaseNetId(okey);
1314 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001315 }
1316
1317 /**
1318 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1319 * from multiple local IP addresses over the same tunnel.
1320 */
1321 @Override
1322 public synchronized void addAddressToTunnelInterface(int tunnelResourceId, String localAddr) {
1323 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1324
1325 // Get tunnelInterface record; if no such interface is found, will throw
1326 // IllegalArgumentException
1327 TunnelInterfaceRecord tunnelInterfaceInfo =
1328 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1329
1330 // TODO: Add calls to netd:
1331 // Add address to TunnelInterface
1332 }
1333
1334 /**
1335 * Remove a new local address from the tunnel interface. After removal, the address will no
1336 * longer be available to send from, or receive on.
1337 */
1338 @Override
1339 public synchronized void removeAddressFromTunnelInterface(
1340 int tunnelResourceId, String localAddr) {
1341 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1342
1343 // Get tunnelInterface record; if no such interface is found, will throw
1344 // IllegalArgumentException
1345 TunnelInterfaceRecord tunnelInterfaceInfo =
1346 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1347
1348 // TODO: Add calls to netd:
1349 // Remove address from TunnelInterface
1350 }
1351
1352 /**
1353 * Delete a TunnelInterface that has been been allocated by and registered with the system
1354 * server
1355 */
1356 @Override
1357 public synchronized void deleteTunnelInterface(int resourceId) throws RemoteException {
1358 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1359 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1360 }
1361
Benedict Wong4f255702017-11-06 20:49:10 -08001362 @VisibleForTesting
Nathan Harolda2523312018-01-05 19:25:13 -08001363 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1364 IpSecAlgorithm auth = config.getAuthentication();
1365 IpSecAlgorithm crypt = config.getEncryption();
1366 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong4f255702017-11-06 20:49:10 -08001367
Nathan Harolda2523312018-01-05 19:25:13 -08001368 // Validate the algorithm set
1369 Preconditions.checkArgument(
1370 aead != null || crypt != null || auth != null,
1371 "No Encryption or Authentication algorithms specified");
1372 Preconditions.checkArgument(
1373 auth == null || auth.isAuthentication(),
1374 "Unsupported algorithm for Authentication");
1375 Preconditions.checkArgument(
Benedict Wong4f255702017-11-06 20:49:10 -08001376 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harolda2523312018-01-05 19:25:13 -08001377 Preconditions.checkArgument(
1378 aead == null || aead.isAead(),
1379 "Unsupported algorithm for Authenticated Encryption");
1380 Preconditions.checkArgument(
1381 aead == null || (auth == null && crypt == null),
1382 "Authenticated Encryption is mutually exclusive with other Authentication "
1383 + "or Encryption algorithms");
Benedict Wong4f255702017-11-06 20:49:10 -08001384 }
1385
Nathan Harold93962f32017-03-07 13:23:36 -08001386 /**
Nathan Harolda10003d2017-08-23 13:46:33 -07001387 * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
1388 * IllegalArgumentException if they are not.
1389 */
1390 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong344bd622017-11-16 15:27:22 -08001391 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1392
Nathan Harolda10003d2017-08-23 13:46:33 -07001393 switch (config.getEncapType()) {
1394 case IpSecTransform.ENCAP_NONE:
1395 break;
1396 case IpSecTransform.ENCAP_ESPINUDP:
1397 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong344bd622017-11-16 15:27:22 -08001398 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1399 userRecord.mEncapSocketRecords.getResourceOrThrow(
1400 config.getEncapSocketResourceId());
Nathan Harolda10003d2017-08-23 13:46:33 -07001401
1402 int port = config.getEncapRemotePort();
1403 if (port <= 0 || port > 0xFFFF) {
1404 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1405 }
1406 break;
1407 default:
1408 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1409 }
1410
Nathan Harolda2523312018-01-05 19:25:13 -08001411 validateAlgorithms(config);
Nathan Harolda10003d2017-08-23 13:46:33 -07001412
Nathan Harolda2523312018-01-05 19:25:13 -08001413 // Retrieve SPI record; will throw IllegalArgumentException if not found
1414 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1415
Benedict Wonge6b42772017-12-13 18:26:40 -08001416 // Check to ensure that SPI has not already been used.
1417 if (s.getOwnedByTransform()) {
1418 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1419 }
1420
Nathan Harolda2523312018-01-05 19:25:13 -08001421 // If no remote address is supplied, then use one from the SPI.
1422 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1423 config.setDestinationAddress(s.getDestinationAddress());
1424 }
1425
1426 // All remote addresses must match
1427 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1428 throw new IllegalArgumentException("Mismatched remote addresseses.");
1429 }
1430
1431 // This check is technically redundant due to the chain of custody between the SPI and
1432 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1433 // the transform, this will prevent us from messing up.
1434 checkInetAddress(config.getDestinationAddress());
1435
1436 // Require a valid source address for all transforms.
1437 checkInetAddress(config.getSourceAddress());
1438
1439 switch (config.getMode()) {
1440 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold5a920ca2018-02-02 18:34:25 -08001441 break;
Nathan Harolda2523312018-01-05 19:25:13 -08001442 case IpSecTransform.MODE_TUNNEL:
Nathan Harold5a920ca2018-02-02 18:34:25 -08001443 enforceNetworkStackPermission();
Nathan Harolda2523312018-01-05 19:25:13 -08001444 break;
1445 default:
1446 throw new IllegalArgumentException(
1447 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harolda10003d2017-08-23 13:46:33 -07001448 }
1449 }
1450
Nathan Harold5a920ca2018-02-02 18:34:25 -08001451 private void enforceNetworkStackPermission() {
1452 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK,
1453 "IpSecService");
1454 }
1455
Benedict Wong0fe58a92018-01-19 17:36:02 -08001456 private void createOrUpdateTransform(
1457 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1458 throws RemoteException {
1459
1460 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1461 if (encapType != IpSecTransform.ENCAP_NONE) {
1462 encapLocalPort = socketRecord.getPort();
1463 encapRemotePort = c.getEncapRemotePort();
1464 }
1465
1466 IpSecAlgorithm auth = c.getAuthentication();
1467 IpSecAlgorithm crypt = c.getEncryption();
1468 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1469
1470 mSrvConfig
1471 .getNetdInstance()
1472 .ipSecAddSecurityAssociation(
1473 resourceId,
1474 c.getMode(),
1475 c.getSourceAddress(),
1476 c.getDestinationAddress(),
1477 (c.getNetwork() != null) ? c.getNetwork().netId : 0,
1478 spiRecord.getSpi(),
1479 c.getMarkValue(),
1480 c.getMarkMask(),
1481 (auth != null) ? auth.getName() : "",
1482 (auth != null) ? auth.getKey() : new byte[] {},
1483 (auth != null) ? auth.getTruncationLengthBits() : 0,
1484 (crypt != null) ? crypt.getName() : "",
1485 (crypt != null) ? crypt.getKey() : new byte[] {},
1486 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1487 (authCrypt != null) ? authCrypt.getName() : "",
1488 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1489 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1490 encapType,
1491 encapLocalPort,
1492 encapRemotePort);
1493 }
1494
Nathan Harolda10003d2017-08-23 13:46:33 -07001495 /**
Benedict Wong0fe58a92018-01-19 17:36:02 -08001496 * Create a IPsec transform, which represents a single security association in the kernel. The
1497 * transform will be cached by the system server and must be freed when no longer needed. It is
1498 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1499 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold93962f32017-03-07 13:23:36 -08001500 */
1501 @Override
Benedict Wongf33f03132018-01-18 14:38:16 -08001502 public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder)
1503 throws RemoteException {
Nathan Harolda10003d2017-08-23 13:46:33 -07001504 checkIpSecConfig(c);
Benedict Wongf33f03132018-01-18 14:38:16 -08001505 checkNotNull(binder, "Null Binder passed to createTransform");
Nathan Haroldd8c74292017-12-13 19:16:33 -08001506 final int resourceId = mNextResourceId++;
Benedict Wong344bd622017-11-16 15:27:22 -08001507
1508 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong4f9fb812017-12-13 17:16:53 -08001509 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong344bd622017-11-16 15:27:22 -08001510
1511 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001512 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1513 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001514
Benedict Wong344bd622017-11-16 15:27:22 -08001515 EncapSocketRecord socketRecord = null;
Benedict Wong0fe58a92018-01-19 17:36:02 -08001516 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong344bd622017-11-16 15:27:22 -08001517 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1518 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1519 c.getEncapSocketResourceId());
1520 dependencies.add(refcountedSocketRecord);
Benedict Wong344bd622017-11-16 15:27:22 -08001521 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001522 }
1523
Nathan Harolda2523312018-01-05 19:25:13 -08001524 RefcountedResource<SpiRecord> refcountedSpiRecord =
1525 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1526 dependencies.add(refcountedSpiRecord);
1527 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong344bd622017-11-16 15:27:22 -08001528
Nathan Harolda2523312018-01-05 19:25:13 -08001529 try {
Benedict Wong0fe58a92018-01-19 17:36:02 -08001530 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Nathan Harolda2523312018-01-05 19:25:13 -08001531 } catch (ServiceSpecificException e) {
1532 // FIXME: get the error code and throw is at an IOException from Errno Exception
1533 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001534 }
Benedict Wong0fe58a92018-01-19 17:36:02 -08001535
1536 // SA was created successfully, time to construct a record and lock it away
Benedict Wong344bd622017-11-16 15:27:22 -08001537 userRecord.mTransformRecords.put(
1538 resourceId,
1539 new RefcountedResource<TransformRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001540 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong344bd622017-11-16 15:27:22 -08001541 binder,
1542 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001543 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001544 }
1545
1546 /**
1547 * Delete a transport mode transform that was previously allocated by + registered with the
1548 * system server. If this is called on an inactive (or non-existent) transform, it will not
1549 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1550 * other reasons.
1551 */
1552 @Override
Benedict Wongf33f03132018-01-18 14:38:16 -08001553 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001554 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1555 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001556 }
1557
1558 /**
1559 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1560 * association as a correspondent policy to the provided socket
1561 */
1562 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001563 public synchronized void applyTransportModeTransform(
Nathan Harolda2523312018-01-05 19:25:13 -08001564 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001565 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Nathan Harolda2523312018-01-05 19:25:13 -08001566 checkDirection(direction);
Benedict Wong344bd622017-11-16 15:27:22 -08001567 // Get transform record; if no transform is found, will throw IllegalArgumentException
1568 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001569
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001570 // TODO: make this a function.
1571 if (info.pid != getCallingPid() || info.uid != getCallingUid()) {
1572 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1573 }
1574
Benedict Wong8149f6e2018-01-18 18:31:45 -08001575 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001576 IpSecConfig c = info.getConfig();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001577 Preconditions.checkArgument(
1578 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1579 "Transform mode was not Transport mode; cannot be applied to a socket");
1580
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001581 try {
Nathan Harolda2523312018-01-05 19:25:13 -08001582 mSrvConfig
1583 .getNetdInstance()
1584 .ipSecApplyTransportModeTransform(
1585 socket.getFileDescriptor(),
1586 resourceId,
1587 direction,
1588 c.getSourceAddress(),
1589 c.getDestinationAddress(),
1590 info.getSpiRecord().getSpi());
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001591 } catch (ServiceSpecificException e) {
manojboopathic4be79d2017-11-30 17:11:49 -08001592 if (e.errorCode == EINVAL) {
1593 throw new IllegalArgumentException(e.toString());
1594 } else {
1595 throw e;
1596 }
Nathan Harold93962f32017-03-07 13:23:36 -08001597 }
1598 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001599
Nathan Harold93962f32017-03-07 13:23:36 -08001600 /**
Nathan Harolda2523312018-01-05 19:25:13 -08001601 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1602 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1603 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1604 * reserved for future improved input validation.
Nathan Harold93962f32017-03-07 13:23:36 -08001605 */
1606 @Override
Nathan Haroldf73d2522018-01-17 01:00:20 -08001607 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1608 throws RemoteException {
Nathan Harold93962f32017-03-07 13:23:36 -08001609 try {
ludi1a06aa72017-05-12 09:15:00 -07001610 mSrvConfig
1611 .getNetdInstance()
1612 .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
Nathan Harold93962f32017-03-07 13:23:36 -08001613 } catch (ServiceSpecificException e) {
1614 // FIXME: get the error code and throw is at an IOException from Errno Exception
1615 }
1616 }
1617
Benedict Wong8149f6e2018-01-18 18:31:45 -08001618 /**
1619 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1620 * security association as a correspondent policy to the provided interface
1621 */
1622 @Override
1623 public synchronized void applyTunnelModeTransform(
1624 int tunnelResourceId, int direction, int transformResourceId) throws RemoteException {
Nathan Harold5a920ca2018-02-02 18:34:25 -08001625 enforceNetworkStackPermission();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001626 checkDirection(direction);
1627
1628 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1629
1630 // Get transform record; if no transform is found, will throw IllegalArgumentException
1631 TransformRecord transformInfo =
1632 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1633
1634 // Get tunnelInterface record; if no such interface is found, will throw
1635 // IllegalArgumentException
1636 TunnelInterfaceRecord tunnelInterfaceInfo =
1637 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1638
1639 // Get config and check that to-be-applied transform has the correct mode
1640 IpSecConfig c = transformInfo.getConfig();
1641 Preconditions.checkArgument(
1642 c.getMode() == IpSecTransform.MODE_TUNNEL,
1643 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1644
Benedict Wong0fe58a92018-01-19 17:36:02 -08001645 EncapSocketRecord socketRecord = null;
1646 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1647 socketRecord =
1648 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1649 }
1650 SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId());
1651
Benedict Wong8149f6e2018-01-18 18:31:45 -08001652 int mark =
1653 (direction == IpSecManager.DIRECTION_IN)
1654 ? tunnelInterfaceInfo.getIkey()
1655 : tunnelInterfaceInfo.getOkey();
1656
Benedict Wong0fe58a92018-01-19 17:36:02 -08001657 try {
1658 c.setMarkValue(mark);
1659 c.setMarkMask(0xffffffff);
1660
1661 if (direction == IpSecManager.DIRECTION_OUT) {
1662 // Set output mark via underlying network (output only)
1663 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1664
1665 // If outbound, also add SPI to the policy.
Benedict Wongb8ef5412018-01-24 15:31:39 -08001666 for(String wildcardAddr : WILDCARD_ADDRESSES) {
1667 mSrvConfig
1668 .getNetdInstance()
1669 .ipSecUpdateSecurityPolicy(
1670 0, // Use 0 for reqId
1671 direction,
1672 wildcardAddr,
1673 wildcardAddr,
1674 transformInfo.getSpiRecord().getSpi(),
1675 mark,
1676 0xffffffff);
1677 }
Benedict Wong0fe58a92018-01-19 17:36:02 -08001678 }
1679
1680 // Update SA with tunnel mark (ikey or okey based on direction)
1681 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1682 } catch (ServiceSpecificException e) {
1683 if (e.errorCode == EINVAL) {
1684 throw new IllegalArgumentException(e.toString());
1685 } else {
1686 throw e;
1687 }
1688 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001689 }
1690
Nathan Harold93962f32017-03-07 13:23:36 -08001691 @Override
ludib0c95b12017-05-22 10:52:23 -07001692 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001693 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludib0c95b12017-05-22 10:52:23 -07001694
1695 pw.println("IpSecService dump:");
Nathan Harold1afbef42017-03-01 18:55:06 -08001696 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1697 pw.println();
ludib0c95b12017-05-22 10:52:23 -07001698
Benedict Wong344bd622017-11-16 15:27:22 -08001699 pw.println("mUserResourceTracker:");
1700 pw.println(mUserResourceTracker);
Nathan Harold1afbef42017-03-01 18:55:06 -08001701 }
1702}