blob: d09a161d1ef4788abb8eca80992d1fb08481549d [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 Wongda4b0c62018-03-01 18:53:07 -080039import android.net.LinkAddress;
Benedict Wong8149f6e2018-01-18 18:31:45 -080040import android.net.Network;
Nathan Harolda10003d2017-08-23 13:46:33 -070041import android.net.NetworkUtils;
Benedict Wongbabe5d72017-12-03 19:42:36 -080042import android.net.TrafficStats;
Nathan Harold1afbef42017-03-01 18:55:06 -080043import android.net.util.NetdService;
Nathan Harold93962f32017-03-07 13:23:36 -080044import android.os.Binder;
Nathan Harold93962f32017-03-07 13:23:36 -080045import android.os.IBinder;
46import android.os.ParcelFileDescriptor;
Nathan Harold1afbef42017-03-01 18:55:06 -080047import android.os.RemoteException;
Nathan Harold93962f32017-03-07 13:23:36 -080048import android.os.ServiceSpecificException;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070049import android.system.ErrnoException;
50import android.system.Os;
51import android.system.OsConstants;
Nathan Harolda10003d2017-08-23 13:46:33 -070052import android.text.TextUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080053import android.util.Log;
54import android.util.Slog;
Nathan Harold93962f32017-03-07 13:23:36 -080055import android.util.SparseArray;
Benedict Wong8149f6e2018-01-18 18:31:45 -080056import android.util.SparseBooleanArray;
Nathan Harolda10003d2017-08-23 13:46:33 -070057
Nathan Harold93962f32017-03-07 13:23:36 -080058import com.android.internal.annotations.GuardedBy;
ludi1a06aa72017-05-12 09:15:00 -070059import com.android.internal.annotations.VisibleForTesting;
Benedict Wong4f255702017-11-06 20:49:10 -080060import com.android.internal.util.Preconditions;
Nathan Harolda10003d2017-08-23 13:46:33 -070061
Nathan Harold1afbef42017-03-01 18:55:06 -080062import java.io.FileDescriptor;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070063import java.io.IOException;
Nathan Harold1afbef42017-03-01 18:55:06 -080064import java.io.PrintWriter;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070065import java.net.InetAddress;
66import java.net.InetSocketAddress;
67import java.net.UnknownHostException;
Benedict Wong409c8ca2017-10-26 19:41:43 -070068import java.util.ArrayList;
69import 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};
Benedict Wongb8ef5412018-01-24 15:31:39 -080091 private static final String[] WILDCARD_ADDRESSES = new String[]{"0.0.0.0", "::"};
Nathan Harold1afbef42017-03-01 18:55:06 -080092
ludi1a06aa72017-05-12 09:15:00 -070093 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold8dc1fd02017-04-04 19:37:48 -070094 private static final int MAX_PORT_BIND_ATTEMPTS = 10;
95 private static final InetAddress INADDR_ANY;
96
97 static {
98 try {
99 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
100 } catch (UnknownHostException e) {
101 throw new RuntimeException(e);
102 }
103 }
104
105 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
106 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
Benedict Wong8149f6e2018-01-18 18:31:45 -0800107 static final String TUNNEL_INTERFACE_PREFIX = "ipsec";
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700108
109 /* Binder context for this service */
Nathan Harold1afbef42017-03-01 18:55:06 -0800110 private final Context mContext;
111
Nathan Haroldd8c74292017-12-13 19:16:33 -0800112 /**
Nathan Harolda2523312018-01-05 19:25:13 -0800113 * The next non-repeating global ID for tracking resources between users, this service, and
114 * kernel data structures. Accessing this variable is not thread safe, so it is only read or
115 * modified within blocks synchronized on IpSecService.this. We want to avoid -1
116 * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
Nathan Haroldd8c74292017-12-13 19:16:33 -0800117 */
118 @GuardedBy("IpSecService.this")
119 private int mNextResourceId = 1;
Nathan Harold1afbef42017-03-01 18:55:06 -0800120
ludi1a06aa72017-05-12 09:15:00 -0700121 interface IpSecServiceConfiguration {
122 INetd getNetdInstance() throws RemoteException;
123
124 static IpSecServiceConfiguration GETSRVINSTANCE =
125 new IpSecServiceConfiguration() {
126 @Override
127 public INetd getNetdInstance() throws RemoteException {
128 final INetd netd = NetdService.getInstance();
129 if (netd == null) {
130 throw new RemoteException("Failed to Get Netd Instance");
131 }
132 return netd;
133 }
134 };
135 }
136
137 private final IpSecServiceConfiguration mSrvConfig;
Benedict Wongbabe5d72017-12-03 19:42:36 -0800138 final UidFdTagger mUidFdTagger;
ludi1a06aa72017-05-12 09:15:00 -0700139
Benedict Wong409c8ca2017-10-26 19:41:43 -0700140 /**
141 * Interface for user-reference and kernel-resource cleanup.
142 *
143 * <p>This interface must be implemented for a resource to be reference counted.
144 */
145 @VisibleForTesting
146 public interface IResource {
147 /**
148 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
149 * objects dependent on it.
150 *
151 * <p>Implementations of this method are expected to remove references to the IResource
152 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
153 * the resource is considered invalid for user access or allocation or use in other
154 * resources.
155 *
156 * <p>References to the IResource object may be held by other RefcountedResource objects,
Benedict Wong4f9fb812017-12-13 17:16:53 -0800157 * and as such, the underlying resources and quota may not be cleaned up.
Benedict Wong409c8ca2017-10-26 19:41:43 -0700158 */
159 void invalidate() throws RemoteException;
160
161 /**
162 * Releases underlying resources and related quotas.
163 *
164 * <p>Implementations of this method are expected to remove all system resources that are
165 * tracked by the IResource object. Due to other RefcountedResource objects potentially
Benedict Wong344bd622017-11-16 15:27:22 -0800166 * having references to the IResource object, freeUnderlyingResources may not always be
Benedict Wong409c8ca2017-10-26 19:41:43 -0700167 * called from releaseIfUnreferencedRecursively().
168 */
169 void freeUnderlyingResources() throws RemoteException;
170 }
171
172 /**
173 * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
174 *
175 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
176 * RefcountedResource object creates an explicit reference that must be freed by calling
177 * userRelease(). Additionally, adding this object as a child of another RefcountedResource
178 * object will add an implicit reference.
179 *
180 * <p>Resources are cleaned up when all references, both implicit and explicit, are released
181 * (ie, when userRelease() is called and when all parents have called releaseReference() on this
182 * object.)
183 */
184 @VisibleForTesting
185 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
186 private final T mResource;
187 private final List<RefcountedResource> mChildren;
188 int mRefCount = 1; // starts at 1 for user's reference.
189 IBinder mBinder;
190
191 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
192 synchronized (IpSecService.this) {
193 this.mResource = resource;
194 this.mChildren = new ArrayList<>(children.length);
195 this.mBinder = binder;
196
197 for (RefcountedResource child : children) {
198 mChildren.add(child);
199 child.mRefCount++;
200 }
201
202 try {
203 mBinder.linkToDeath(this, 0);
204 } catch (RemoteException e) {
205 binderDied();
206 }
207 }
208 }
209
210 /**
211 * If the Binder object dies, this function is called to free the system resources that are
Benedict Wong344bd622017-11-16 15:27:22 -0800212 * being tracked by this record and to subsequently release this record for garbage
Benedict Wong409c8ca2017-10-26 19:41:43 -0700213 * collection
214 */
215 @Override
216 public void binderDied() {
217 synchronized (IpSecService.this) {
218 try {
219 userRelease();
220 } catch (Exception e) {
221 Log.e(TAG, "Failed to release resource: " + e);
222 }
223 }
224 }
225
226 public T getResource() {
227 return mResource;
228 }
229
230 /**
231 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
232 * arrays)
233 *
234 * <p>If this method has been previously called, the RefcountedResource's binder field will
235 * be null, and the method will return without performing the cleanup a second time.
236 *
237 * <p>Note that calling this function does not imply that kernel resources will be freed at
238 * this time, or that the related quota will be returned. Such actions will only be
239 * performed upon the reference count reaching zero.
240 */
241 @GuardedBy("IpSecService.this")
242 public void userRelease() throws RemoteException {
243 // Prevent users from putting reference counts into a bad state by calling
244 // userRelease() multiple times.
245 if (mBinder == null) {
246 return;
247 }
248
249 mBinder.unlinkToDeath(this, 0);
250 mBinder = null;
251
252 mResource.invalidate();
253
254 releaseReference();
255 }
256
257 /**
258 * Removes a reference to this resource. If the resultant reference count is zero, the
259 * underlying resources are freed, and references to all child resources are also dropped
260 * recursively (resulting in them freeing their resources and children, etcetera)
261 *
262 * <p>This method also sets the reference count to an invalid value (-1) to signify that it
263 * has been fully released. Any subsequent calls to this method will result in an
264 * IllegalStateException being thrown due to resource already having been previously
265 * released
266 */
267 @VisibleForTesting
268 @GuardedBy("IpSecService.this")
269 public void releaseReference() throws RemoteException {
270 mRefCount--;
271
272 if (mRefCount > 0) {
273 return;
274 } else if (mRefCount < 0) {
275 throw new IllegalStateException(
276 "Invalid operation - resource has already been released.");
277 }
278
279 // Cleanup own resources
280 mResource.freeUnderlyingResources();
281
282 // Cleanup child resources as needed
283 for (RefcountedResource<? extends IResource> child : mChildren) {
284 child.releaseReference();
285 }
286
287 // Enforce that resource cleanup can only be called once
288 // By decrementing the refcount (from 0 to -1), the next call will throw an
289 // IllegalStateException - it has already been released fully.
290 mRefCount--;
291 }
292
293 @Override
294 public String toString() {
295 return new StringBuilder()
296 .append("{mResource=")
297 .append(mResource)
298 .append(", mRefCount=")
299 .append(mRefCount)
300 .append(", mChildren=")
301 .append(mChildren)
302 .append("}")
303 .toString();
304 }
305 }
306
Benedict Wong4f9fb812017-12-13 17:16:53 -0800307 /**
308 * Very simple counting class that looks much like a counting semaphore
309 *
310 * <p>This class is not thread-safe, and expects that that users of this class will ensure
311 * synchronization and thread safety by holding the IpSecService.this instance lock.
312 */
Benedict Wong344bd622017-11-16 15:27:22 -0800313 @VisibleForTesting
314 static class ResourceTracker {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700315 private final int mMax;
316 int mCurrent;
317
318 ResourceTracker(int max) {
319 mMax = max;
320 mCurrent = 0;
321 }
322
Benedict Wong344bd622017-11-16 15:27:22 -0800323 boolean isAvailable() {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700324 return (mCurrent < mMax);
325 }
326
Benedict Wong344bd622017-11-16 15:27:22 -0800327 void take() {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700328 if (!isAvailable()) {
329 Log.wtf(TAG, "Too many resources allocated!");
330 }
331 mCurrent++;
332 }
333
Benedict Wong344bd622017-11-16 15:27:22 -0800334 void give() {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700335 if (mCurrent <= 0) {
336 Log.wtf(TAG, "We've released this resource too many times");
337 }
338 mCurrent--;
339 }
ludi3e5ea232017-08-10 15:44:40 -0700340
341 @Override
342 public String toString() {
343 return new StringBuilder()
344 .append("{mCurrent=")
345 .append(mCurrent)
346 .append(", mMax=")
347 .append(mMax)
348 .append("}")
349 .toString();
350 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700351 }
352
Benedict Wong344bd622017-11-16 15:27:22 -0800353 @VisibleForTesting
354 static final class UserRecord {
Benedict Wong344bd622017-11-16 15:27:22 -0800355 /* Maximum number of each type of resource that a single UID may possess */
Benedict Wong8149f6e2018-01-18 18:31:45 -0800356 public static final int MAX_NUM_TUNNEL_INTERFACES = 2;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700357 public static final int MAX_NUM_ENCAP_SOCKETS = 2;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700358 public static final int MAX_NUM_TRANSFORMS = 4;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700359 public static final int MAX_NUM_SPIS = 8;
360
Benedict Wong4f9fb812017-12-13 17:16:53 -0800361 /**
362 * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
363 * and explicit (user) reference management.
364 *
365 * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
366 *
367 * <p>Resources are removed from this array when the user releases their explicit reference
368 * by calling one of the releaseResource() methods.
369 */
Benedict Wong344bd622017-11-16 15:27:22 -0800370 final RefcountedResourceArray<SpiRecord> mSpiRecords =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800371 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
Benedict Wong344bd622017-11-16 15:27:22 -0800372 final RefcountedResourceArray<TransformRecord> mTransformRecords =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800373 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
Benedict Wong344bd622017-11-16 15:27:22 -0800374 final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800375 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
Benedict Wong8149f6e2018-01-18 18:31:45 -0800376 final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
377 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
Benedict Wong4f9fb812017-12-13 17:16:53 -0800378
379 /**
380 * Trackers for quotas for each of the OwnedResource types.
381 *
382 * <p>These trackers are separate from the resource arrays, since they are incremented and
383 * decremented at different points in time. Specifically, quota is only returned upon final
384 * resource deallocation (after all explicit and implicit references are released). Note
385 * that it is possible that calls to releaseResource() will not return the used quota if
386 * there are other resources that depend on (are parents of) the resource being released.
387 */
388 final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
389 final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
Benedict Wong344bd622017-11-16 15:27:22 -0800390 final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
Benedict Wong8149f6e2018-01-18 18:31:45 -0800391 final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
Benedict Wong344bd622017-11-16 15:27:22 -0800392
393 void removeSpiRecord(int resourceId) {
394 mSpiRecords.remove(resourceId);
Nathan Harolda1afbd82017-04-24 16:16:34 -0700395 }
396
Benedict Wong344bd622017-11-16 15:27:22 -0800397 void removeTransformRecord(int resourceId) {
398 mTransformRecords.remove(resourceId);
399 }
400
Benedict Wong8149f6e2018-01-18 18:31:45 -0800401 void removeTunnelInterfaceRecord(int resourceId) {
402 mTunnelInterfaceRecords.remove(resourceId);
403 }
404
Benedict Wong344bd622017-11-16 15:27:22 -0800405 void removeEncapSocketRecord(int resourceId) {
406 mEncapSocketRecords.remove(resourceId);
407 }
408
409 @Override
410 public String toString() {
411 return new StringBuilder()
412 .append("{mSpiQuotaTracker=")
413 .append(mSpiQuotaTracker)
414 .append(", mTransformQuotaTracker=")
415 .append(mTransformQuotaTracker)
416 .append(", mSocketQuotaTracker=")
417 .append(mSocketQuotaTracker)
Benedict Wongb8ef5412018-01-24 15:31:39 -0800418 .append(", mTunnelQuotaTracker=")
419 .append(mTunnelQuotaTracker)
Benedict Wong344bd622017-11-16 15:27:22 -0800420 .append(", mSpiRecords=")
421 .append(mSpiRecords)
422 .append(", mTransformRecords=")
423 .append(mTransformRecords)
424 .append(", mEncapSocketRecords=")
425 .append(mEncapSocketRecords)
Benedict Wongb8ef5412018-01-24 15:31:39 -0800426 .append(", mTunnelInterfaceRecords=")
427 .append(mTunnelInterfaceRecords)
Benedict Wong344bd622017-11-16 15:27:22 -0800428 .append("}")
429 .toString();
430 }
431 }
432
Benedict Wong4f9fb812017-12-13 17:16:53 -0800433 /**
434 * This class is not thread-safe, and expects that that users of this class will ensure
435 * synchronization and thread safety by holding the IpSecService.this instance lock.
436 */
Benedict Wong344bd622017-11-16 15:27:22 -0800437 @VisibleForTesting
438 static final class UserResourceTracker {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700439 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
440
Benedict Wong4f9fb812017-12-13 17:16:53 -0800441 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
Benedict Wong344bd622017-11-16 15:27:22 -0800442 public UserRecord getUserRecord(int uid) {
443 checkCallerUid(uid);
444
Nathan Harolda1afbd82017-04-24 16:16:34 -0700445 UserRecord r = mUserRecords.get(uid);
446 if (r == null) {
447 r = new UserRecord();
448 mUserRecords.put(uid, r);
449 }
450 return r;
451 }
ludi3e5ea232017-08-10 15:44:40 -0700452
Benedict Wong344bd622017-11-16 15:27:22 -0800453 /** Safety method; guards against access of other user's UserRecords */
454 private void checkCallerUid(int uid) {
455 if (uid != Binder.getCallingUid()
456 && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
457 throw new SecurityException("Attempted access of unowned resources");
458 }
459 }
460
ludi3e5ea232017-08-10 15:44:40 -0700461 @Override
462 public String toString() {
463 return mUserRecords.toString();
464 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700465 }
466
Benedict Wong344bd622017-11-16 15:27:22 -0800467 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700468
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700469 /**
Benedict Wong4f9fb812017-12-13 17:16:53 -0800470 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
Benedict Wong344bd622017-11-16 15:27:22 -0800471 * resources. It relies on a provided resourceId that should uniquely identify the kernel
472 * resource. To use this class, the user should implement the invalidate() and
473 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
Benedict Wong4f9fb812017-12-13 17:16:53 -0800474 * tracking arrays and kernel resources, respectively.
475 *
476 * <p>This class associates kernel resources with the UID that owns and controls them.
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700477 */
Benedict Wong4f9fb812017-12-13 17:16:53 -0800478 private abstract class OwnedResourceRecord implements IResource {
Nathan Harold93962f32017-03-07 13:23:36 -0800479 final int pid;
480 final int uid;
Benedict Wong344bd622017-11-16 15:27:22 -0800481 protected final int mResourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800482
Benedict Wong4f9fb812017-12-13 17:16:53 -0800483 OwnedResourceRecord(int resourceId) {
Nathan Harold93962f32017-03-07 13:23:36 -0800484 super();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700485 if (resourceId == INVALID_RESOURCE_ID) {
486 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
487 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700488 mResourceId = resourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800489 pid = Binder.getCallingPid();
490 uid = Binder.getCallingUid();
491
Nathan Harolda1afbd82017-04-24 16:16:34 -0700492 getResourceTracker().take();
Nathan Harold93962f32017-03-07 13:23:36 -0800493 }
494
Benedict Wong344bd622017-11-16 15:27:22 -0800495 @Override
496 public abstract void invalidate() throws RemoteException;
497
498 /** Convenience method; retrieves the user resource record for the stored UID. */
499 protected UserRecord getUserRecord() {
500 return mUserResourceTracker.getUserRecord(uid);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700501 }
502
Benedict Wong344bd622017-11-16 15:27:22 -0800503 @Override
504 public abstract void freeUnderlyingResources() throws RemoteException;
ludib0c95b12017-05-22 10:52:23 -0700505
Nathan Harolda1afbd82017-04-24 16:16:34 -0700506 /** Get the resource tracker for this resource */
507 protected abstract ResourceTracker getResourceTracker();
508
ludib0c95b12017-05-22 10:52:23 -0700509 @Override
510 public String toString() {
511 return new StringBuilder()
512 .append("{mResourceId=")
513 .append(mResourceId)
514 .append(", pid=")
515 .append(pid)
516 .append(", uid=")
517 .append(uid)
ludib0c95b12017-05-22 10:52:23 -0700518 .append("}")
519 .toString();
520 }
Nathan Harold93962f32017-03-07 13:23:36 -0800521 };
522
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700523 /**
Benedict Wong344bd622017-11-16 15:27:22 -0800524 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
525 *
526 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
527 * if a key is not found during a retrieval process.
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700528 */
Benedict Wong344bd622017-11-16 15:27:22 -0800529 static class RefcountedResourceArray<T extends IResource> {
530 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
531 private final String mTypeName;
Nathan Harold93962f32017-03-07 13:23:36 -0800532
Benedict Wong344bd622017-11-16 15:27:22 -0800533 public RefcountedResourceArray(String typeName) {
534 this.mTypeName = typeName;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700535 }
536
Benedict Wong344bd622017-11-16 15:27:22 -0800537 /**
538 * Accessor method to get inner resource object.
539 *
540 * @throws IllegalArgumentException if no resource with provided key is found.
541 */
542 T getResourceOrThrow(int key) {
543 return getRefcountedResourceOrThrow(key).getResource();
544 }
545
546 /**
547 * Accessor method to get reference counting wrapper.
548 *
549 * @throws IllegalArgumentException if no resource with provided key is found.
550 */
551 RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
552 RefcountedResource<T> resource = mArray.get(key);
553 if (resource == null) {
554 throw new IllegalArgumentException(
555 String.format("No such %s found for given id: %d", mTypeName, key));
556 }
557
558 return resource;
559 }
560
561 void put(int key, RefcountedResource<T> obj) {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700562 checkNotNull(obj, "Null resources cannot be added");
563 mArray.put(key, obj);
564 }
565
566 void remove(int key) {
567 mArray.remove(key);
568 }
ludib0c95b12017-05-22 10:52:23 -0700569
570 @Override
571 public String toString() {
572 return mArray.toString();
573 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700574 }
575
Benedict Wong4f9fb812017-12-13 17:16:53 -0800576 /**
577 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
578 * created, the SpiRecord that originally tracked the SAs will reliquish the
579 * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
580 */
581 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700582 private final IpSecConfig mConfig;
Nathan Harolda2523312018-01-05 19:25:13 -0800583 private final SpiRecord mSpi;
Benedict Wong344bd622017-11-16 15:27:22 -0800584 private final EncapSocketRecord mSocket;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700585
586 TransformRecord(
Nathan Harolda2523312018-01-05 19:25:13 -0800587 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong344bd622017-11-16 15:27:22 -0800588 super(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -0800589 mConfig = config;
Nathan Harolda2523312018-01-05 19:25:13 -0800590 mSpi = spi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700591 mSocket = socket;
Benedict Wonge6b42772017-12-13 18:26:40 -0800592
593 spi.setOwnedByTransform();
Nathan Harold93962f32017-03-07 13:23:36 -0800594 }
595
596 public IpSecConfig getConfig() {
597 return mConfig;
598 }
599
Nathan Harolda2523312018-01-05 19:25:13 -0800600 public SpiRecord getSpiRecord() {
601 return mSpi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700602 }
603
Benedict Wong0fe58a92018-01-19 17:36:02 -0800604 public EncapSocketRecord getSocketRecord() {
605 return mSocket;
606 }
607
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700608 /** always guarded by IpSecService#this */
Nathan Harold93962f32017-03-07 13:23:36 -0800609 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800610 public void freeUnderlyingResources() {
Nathan Harolda2523312018-01-05 19:25:13 -0800611 int spi = mSpi.getSpi();
612 try {
613 mSrvConfig
614 .getNetdInstance()
615 .ipSecDeleteSecurityAssociation(
616 mResourceId,
617 mConfig.getSourceAddress(),
618 mConfig.getDestinationAddress(),
Di Lu0b611f42018-01-11 11:35:25 -0800619 spi,
620 mConfig.getMarkValue(),
621 mConfig.getMarkMask());
Benedict Wongda4b0c62018-03-01 18:53:07 -0800622 } catch (RemoteException | ServiceSpecificException e) {
623 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
Nathan Harold93962f32017-03-07 13:23:36 -0800624 }
Nathan Harold93962f32017-03-07 13:23:36 -0800625
Benedict Wong344bd622017-11-16 15:27:22 -0800626 getResourceTracker().give();
Nathan Harold93962f32017-03-07 13:23:36 -0800627 }
ludib0c95b12017-05-22 10:52:23 -0700628
Benedict Wong344bd622017-11-16 15:27:22 -0800629 @Override
630 public void invalidate() throws RemoteException {
631 getUserRecord().removeTransformRecord(mResourceId);
632 }
633
634 @Override
Nathan Harolda1afbd82017-04-24 16:16:34 -0700635 protected ResourceTracker getResourceTracker() {
Benedict Wong344bd622017-11-16 15:27:22 -0800636 return getUserRecord().mTransformQuotaTracker;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700637 }
638
ludib0c95b12017-05-22 10:52:23 -0700639 @Override
640 public String toString() {
641 StringBuilder strBuilder = new StringBuilder();
642 strBuilder
643 .append("{super=")
644 .append(super.toString())
645 .append(", mSocket=")
646 .append(mSocket)
Nathan Harolda2523312018-01-05 19:25:13 -0800647 .append(", mSpi.mResourceId=")
648 .append(mSpi.mResourceId)
ludib0c95b12017-05-22 10:52:23 -0700649 .append(", mConfig=")
650 .append(mConfig)
651 .append("}");
652 return strBuilder.toString();
653 }
Nathan Harold93962f32017-03-07 13:23:36 -0800654 }
655
Benedict Wong4f9fb812017-12-13 17:16:53 -0800656 /**
657 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
658 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
659 * object
660 */
661 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harolda2523312018-01-05 19:25:13 -0800662 private final String mSourceAddress;
663 private final String mDestinationAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800664 private int mSpi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700665
666 private boolean mOwnedByTransform = false;
Nathan Harold93962f32017-03-07 13:23:36 -0800667
Nathan Harolda2523312018-01-05 19:25:13 -0800668 SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
Benedict Wong344bd622017-11-16 15:27:22 -0800669 super(resourceId);
Nathan Harolda2523312018-01-05 19:25:13 -0800670 mSourceAddress = sourceAddress;
671 mDestinationAddress = destinationAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800672 mSpi = spi;
Nathan Harold93962f32017-03-07 13:23:36 -0800673 }
674
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700675 /** always guarded by IpSecService#this */
676 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800677 public void freeUnderlyingResources() {
Nathan Harold93962f32017-03-07 13:23:36 -0800678 try {
Nathan Harold82485a22018-02-27 19:19:40 -0800679 if (!mOwnedByTransform) {
680 mSrvConfig
681 .getNetdInstance()
682 .ipSecDeleteSecurityAssociation(
683 mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
684 }
Benedict Wongda4b0c62018-03-01 18:53:07 -0800685 } catch (ServiceSpecificException | RemoteException e) {
686 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
Nathan Harold93962f32017-03-07 13:23:36 -0800687 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700688
689 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -0800690
Benedict Wong344bd622017-11-16 15:27:22 -0800691 getResourceTracker().give();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700692 }
693
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700694 public int getSpi() {
695 return mSpi;
696 }
697
Nathan Harolda2523312018-01-05 19:25:13 -0800698 public String getDestinationAddress() {
699 return mDestinationAddress;
700 }
701
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700702 public void setOwnedByTransform() {
703 if (mOwnedByTransform) {
704 // Programming error
Andreas Gamped6d8e452017-07-11 10:25:09 -0700705 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700706 }
707
708 mOwnedByTransform = true;
Nathan Harold93962f32017-03-07 13:23:36 -0800709 }
ludib0c95b12017-05-22 10:52:23 -0700710
Benedict Wonge6b42772017-12-13 18:26:40 -0800711 public boolean getOwnedByTransform() {
712 return mOwnedByTransform;
713 }
714
ludib0c95b12017-05-22 10:52:23 -0700715 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800716 public void invalidate() throws RemoteException {
717 getUserRecord().removeSpiRecord(mResourceId);
718 }
719
720 @Override
721 protected ResourceTracker getResourceTracker() {
722 return getUserRecord().mSpiQuotaTracker;
723 }
724
725 @Override
ludib0c95b12017-05-22 10:52:23 -0700726 public String toString() {
727 StringBuilder strBuilder = new StringBuilder();
728 strBuilder
729 .append("{super=")
730 .append(super.toString())
731 .append(", mSpi=")
732 .append(mSpi)
Nathan Harolda2523312018-01-05 19:25:13 -0800733 .append(", mSourceAddress=")
734 .append(mSourceAddress)
735 .append(", mDestinationAddress=")
736 .append(mDestinationAddress)
ludib0c95b12017-05-22 10:52:23 -0700737 .append(", mOwnedByTransform=")
738 .append(mOwnedByTransform)
739 .append("}");
740 return strBuilder.toString();
741 }
Nathan Harold93962f32017-03-07 13:23:36 -0800742 }
743
Benedict Wong8149f6e2018-01-18 18:31:45 -0800744 // These values have been reserved in ConnectivityService
745 @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
746
747 @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400;
748
749 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
750 private int mNextTunnelNetIdIndex = 0;
751
752 /**
753 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
754 *
755 * <p>This method should only be called from Binder threads. Do not call this from within the
756 * system server as it will crash the system on failure.
757 *
758 * @return an integer key within the netId range, if successful
759 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
760 */
761 @VisibleForTesting
762 int reserveNetId() {
763 synchronized (mTunnelNetIds) {
764 for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
765 int index = mNextTunnelNetIdIndex;
766 int netId = index + TUN_INTF_NETID_START;
767 if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
768 if (!mTunnelNetIds.get(netId)) {
769 mTunnelNetIds.put(netId, true);
770 return netId;
771 }
772 }
773 }
774 throw new IllegalStateException("No free netIds to allocate");
775 }
776
777 @VisibleForTesting
778 void releaseNetId(int netId) {
779 synchronized (mTunnelNetIds) {
780 mTunnelNetIds.delete(netId);
781 }
782 }
783
784 private final class TunnelInterfaceRecord extends OwnedResourceRecord {
785 private final String mInterfaceName;
786 private final Network mUnderlyingNetwork;
787
788 // outer addresses
789 private final String mLocalAddress;
790 private final String mRemoteAddress;
791
792 private final int mIkey;
793 private final int mOkey;
794
795 TunnelInterfaceRecord(
796 int resourceId,
797 String interfaceName,
798 Network underlyingNetwork,
799 String localAddr,
800 String remoteAddr,
801 int ikey,
802 int okey) {
803 super(resourceId);
804
805 mInterfaceName = interfaceName;
806 mUnderlyingNetwork = underlyingNetwork;
807 mLocalAddress = localAddr;
808 mRemoteAddress = remoteAddr;
809 mIkey = ikey;
810 mOkey = okey;
811 }
812
813 /** always guarded by IpSecService#this */
814 @Override
815 public void freeUnderlyingResources() {
Benedict Wong0fe58a92018-01-19 17:36:02 -0800816 // Calls to netd
Benedict Wong8149f6e2018-01-18 18:31:45 -0800817 // Teardown VTI
818 // Delete global policies
Benedict Wong0fe58a92018-01-19 17:36:02 -0800819 try {
820 mSrvConfig.getNetdInstance().removeVirtualTunnelInterface(mInterfaceName);
821
Benedict Wongb8ef5412018-01-24 15:31:39 -0800822 for(String wildcardAddr : WILDCARD_ADDRESSES) {
823 for (int direction : DIRECTIONS) {
824 int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey;
825 mSrvConfig
826 .getNetdInstance()
827 .ipSecDeleteSecurityPolicy(
828 0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff);
829 }
Benedict Wong0fe58a92018-01-19 17:36:02 -0800830 }
Benedict Wongda4b0c62018-03-01 18:53:07 -0800831 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong0fe58a92018-01-19 17:36:02 -0800832 Log.e(
833 TAG,
834 "Failed to delete VTI with interface name: "
835 + mInterfaceName
836 + " and id: "
Benedict Wongda4b0c62018-03-01 18:53:07 -0800837 + mResourceId, e);
Benedict Wong0fe58a92018-01-19 17:36:02 -0800838 }
Benedict Wong8149f6e2018-01-18 18:31:45 -0800839
840 getResourceTracker().give();
841 releaseNetId(mIkey);
842 releaseNetId(mOkey);
843 }
844
845 public String getInterfaceName() {
846 return mInterfaceName;
847 }
848
849 public Network getUnderlyingNetwork() {
850 return mUnderlyingNetwork;
851 }
852
853 /** Returns the local, outer address for the tunnelInterface */
854 public String getLocalAddress() {
855 return mLocalAddress;
856 }
857
858 /** Returns the remote, outer address for the tunnelInterface */
859 public String getRemoteAddress() {
860 return mRemoteAddress;
861 }
862
863 public int getIkey() {
864 return mIkey;
865 }
866
867 public int getOkey() {
868 return mOkey;
869 }
870
871 @Override
872 protected ResourceTracker getResourceTracker() {
873 return getUserRecord().mTunnelQuotaTracker;
874 }
875
876 @Override
877 public void invalidate() {
878 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
879 }
880
881 @Override
882 public String toString() {
883 return new StringBuilder()
884 .append("{super=")
885 .append(super.toString())
886 .append(", mInterfaceName=")
887 .append(mInterfaceName)
888 .append(", mUnderlyingNetwork=")
889 .append(mUnderlyingNetwork)
890 .append(", mLocalAddress=")
891 .append(mLocalAddress)
892 .append(", mRemoteAddress=")
893 .append(mRemoteAddress)
894 .append(", mIkey=")
895 .append(mIkey)
896 .append(", mOkey=")
897 .append(mOkey)
898 .append("}")
899 .toString();
900 }
901 }
902
Benedict Wong4f9fb812017-12-13 17:16:53 -0800903 /**
904 * Tracks a UDP encap socket, and manages cleanup paths
905 *
906 * <p>While this class does not manage non-kernel resources, race conditions around socket
907 * binding require that the service creates the encap socket, binds it and applies the socket
908 * policy before handing it to a user.
909 */
910 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700911 private FileDescriptor mSocket;
912 private final int mPort;
Nathan Harold93962f32017-03-07 13:23:36 -0800913
Benedict Wong344bd622017-11-16 15:27:22 -0800914 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
915 super(resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700916 mSocket = socket;
917 mPort = port;
918 }
919
920 /** always guarded by IpSecService#this */
921 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800922 public void freeUnderlyingResources() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700923 Log.d(TAG, "Closing port " + mPort);
924 IoUtils.closeQuietly(mSocket);
925 mSocket = null;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700926
Benedict Wong344bd622017-11-16 15:27:22 -0800927 getResourceTracker().give();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700928 }
929
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700930 public int getPort() {
931 return mPort;
932 }
933
934 public FileDescriptor getSocket() {
935 return mSocket;
936 }
ludib0c95b12017-05-22 10:52:23 -0700937
938 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800939 protected ResourceTracker getResourceTracker() {
940 return getUserRecord().mSocketQuotaTracker;
941 }
942
943 @Override
944 public void invalidate() {
945 getUserRecord().removeEncapSocketRecord(mResourceId);
946 }
947
948 @Override
ludib0c95b12017-05-22 10:52:23 -0700949 public String toString() {
950 return new StringBuilder()
951 .append("{super=")
952 .append(super.toString())
953 .append(", mSocket=")
954 .append(mSocket)
955 .append(", mPort=")
956 .append(mPort)
957 .append("}")
958 .toString();
959 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700960 }
Nathan Harold93962f32017-03-07 13:23:36 -0800961
Nathan Harold1afbef42017-03-01 18:55:06 -0800962 /**
963 * Constructs a new IpSecService instance
964 *
965 * @param context Binder context for this service
966 */
967 private IpSecService(Context context) {
ludi1a06aa72017-05-12 09:15:00 -0700968 this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
Nathan Harold1afbef42017-03-01 18:55:06 -0800969 }
970
971 static IpSecService create(Context context) throws InterruptedException {
972 final IpSecService service = new IpSecService(context);
973 service.connectNativeNetdService();
974 return service;
975 }
976
ludi1a06aa72017-05-12 09:15:00 -0700977 /** @hide */
978 @VisibleForTesting
979 public IpSecService(Context context, IpSecServiceConfiguration config) {
Nathan Harolda2523312018-01-05 19:25:13 -0800980 this(
981 context,
982 config,
983 (fd, uid) -> {
984 try {
985 TrafficStats.setThreadStatsUid(uid);
986 TrafficStats.tagFileDescriptor(fd);
987 } finally {
988 TrafficStats.clearThreadStatsUid();
989 }
990 });
Benedict Wongbabe5d72017-12-03 19:42:36 -0800991 }
992
993 /** @hide */
994 @VisibleForTesting
995 public IpSecService(
996 Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
ludi1a06aa72017-05-12 09:15:00 -0700997 mContext = context;
998 mSrvConfig = config;
Benedict Wongbabe5d72017-12-03 19:42:36 -0800999 mUidFdTagger = uidFdTagger;
ludi1a06aa72017-05-12 09:15:00 -07001000 }
1001
Nathan Harold1afbef42017-03-01 18:55:06 -08001002 public void systemReady() {
1003 if (isNetdAlive()) {
1004 Slog.d(TAG, "IpSecService is ready");
1005 } else {
1006 Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
1007 }
1008 }
1009
1010 private void connectNativeNetdService() {
1011 // Avoid blocking the system server to do this
Nathan Haroldb0e05082017-07-17 14:01:53 -07001012 new Thread() {
1013 @Override
1014 public void run() {
1015 synchronized (IpSecService.this) {
ludi1a06aa72017-05-12 09:15:00 -07001016 NetdService.get(NETD_FETCH_TIMEOUT_MS);
Nathan Haroldb0e05082017-07-17 14:01:53 -07001017 }
1018 }
1019 }.start();
Nathan Harold1afbef42017-03-01 18:55:06 -08001020 }
1021
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001022 synchronized boolean isNetdAlive() {
1023 try {
ludi1a06aa72017-05-12 09:15:00 -07001024 final INetd netd = mSrvConfig.getNetdInstance();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001025 if (netd == null) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001026 return false;
1027 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001028 return netd.isAlive();
1029 } catch (RemoteException re) {
1030 return false;
Nathan Harold1afbef42017-03-01 18:55:06 -08001031 }
1032 }
1033
Nathan Harolda10003d2017-08-23 13:46:33 -07001034 /**
1035 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1036 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1037 */
1038 private static void checkInetAddress(String inetAddress) {
1039 if (TextUtils.isEmpty(inetAddress)) {
1040 throw new IllegalArgumentException("Unspecified address");
1041 }
1042
1043 InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
1044
1045 if (checkAddr.isAnyLocalAddress()) {
1046 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1047 }
1048 }
1049
1050 /**
1051 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1052 * DIRECTION_IN or DIRECTION_OUT
1053 */
1054 private static void checkDirection(int direction) {
1055 switch (direction) {
Nathan Harolda2523312018-01-05 19:25:13 -08001056 case IpSecManager.DIRECTION_OUT:
1057 case IpSecManager.DIRECTION_IN:
Nathan Harolda10003d2017-08-23 13:46:33 -07001058 return;
1059 }
1060 throw new IllegalArgumentException("Invalid Direction: " + direction);
1061 }
1062
Nathan Harold93962f32017-03-07 13:23:36 -08001063 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001064 @Override
1065 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harolda2523312018-01-05 19:25:13 -08001066 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1067 checkInetAddress(destinationAddress);
Nathan Harolda10003d2017-08-23 13:46:33 -07001068 /* requestedSpi can be anything in the int range, so no check is needed. */
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001069 checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harolda10003d2017-08-23 13:46:33 -07001070
Benedict Wong344bd622017-11-16 15:27:22 -08001071 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Nathan Haroldd8c74292017-12-13 19:16:33 -08001072 final int resourceId = mNextResourceId++;
Nathan Harold93962f32017-03-07 13:23:36 -08001073
1074 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -08001075 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001076 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001077 return new IpSecSpiResponse(
Nathan Harolda10003d2017-08-23 13:46:33 -07001078 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harolda1afbd82017-04-24 16:16:34 -07001079 }
Nathan Harolda2523312018-01-05 19:25:13 -08001080
Nathan Harold93962f32017-03-07 13:23:36 -08001081 spi =
ludi1a06aa72017-05-12 09:15:00 -07001082 mSrvConfig
1083 .getNetdInstance()
Nathan Harolda2523312018-01-05 19:25:13 -08001084 .ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi);
Nathan Harold93962f32017-03-07 13:23:36 -08001085 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong344bd622017-11-16 15:27:22 -08001086 userRecord.mSpiRecords.put(
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001087 resourceId,
Benedict Wong344bd622017-11-16 15:27:22 -08001088 new RefcountedResource<SpiRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001089 new SpiRecord(resourceId, "", destinationAddress, spi), binder));
Nathan Harold93962f32017-03-07 13:23:36 -08001090 } catch (ServiceSpecificException e) {
1091 // TODO: Add appropriate checks when other ServiceSpecificException types are supported
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001092 return new IpSecSpiResponse(
Nathan Harolda1afbd82017-04-24 16:16:34 -07001093 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold93962f32017-03-07 13:23:36 -08001094 } catch (RemoteException e) {
1095 throw e.rethrowFromSystemServer();
1096 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001097 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1098 }
1099
1100 /* This method should only be called from Binder threads. Do not call this from
1101 * within the system server as it will crash the system on failure.
1102 */
Benedict Wong344bd622017-11-16 15:27:22 -08001103 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001104 throws RemoteException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001105
Benedict Wong344bd622017-11-16 15:27:22 -08001106 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold93962f32017-03-07 13:23:36 -08001107 }
1108
1109 /** Release a previously allocated SPI that has been registered with the system server */
1110 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001111 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1112 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1113 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001114 }
1115
1116 /**
1117 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1118 * be unbound.
1119 *
1120 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1121 * a random open port and then bind by number, this function creates a temp socket, binds to a
1122 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1123 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1124 * FileHandle.
1125 *
1126 * <p>The loop in this function handles the inherent race window between un-binding to a port
1127 * and re-binding, during which the system could *technically* hand that port out to someone
1128 * else.
1129 */
Benedict Wongf186d672017-10-10 20:44:28 -07001130 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001131 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1132 try {
1133 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1134 Os.bind(probeSocket, INADDR_ANY, 0);
1135 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1136 Os.close(probeSocket);
1137 Log.v(TAG, "Binding to port " + port);
1138 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongf186d672017-10-10 20:44:28 -07001139 return port;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001140 } catch (ErrnoException e) {
1141 // Someone miraculously claimed the port just after we closed probeSocket.
1142 if (e.errno == OsConstants.EADDRINUSE) {
1143 continue;
1144 }
1145 throw e.rethrowAsIOException();
1146 }
1147 }
1148 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1149 }
Nathan Harold93962f32017-03-07 13:23:36 -08001150
1151 /**
Benedict Wongbabe5d72017-12-03 19:42:36 -08001152 * Functional interface to do traffic tagging of given sockets to UIDs.
1153 *
1154 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1155 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1156 *
1157 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1158 * methods that cannot be easily mocked/tested.
1159 */
1160 @VisibleForTesting
1161 public interface UidFdTagger {
1162 /**
1163 * Sets socket tag to assign all traffic to the provided UID.
1164 *
1165 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1166 * should be accounted to the UID of the unprivileged application.
1167 */
1168 public void tag(FileDescriptor fd, int uid) throws IOException;
1169 }
1170
1171 /**
Nathan Harold93962f32017-03-07 13:23:36 -08001172 * Open a socket via the system server and bind it to the specified port (random if port=0).
1173 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1174 * cache the socket and a record of its owner so that it can and must be freed when no longer
1175 * needed.
1176 */
1177 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001178 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1179 throws RemoteException {
1180 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1181 throw new IllegalArgumentException(
1182 "Specified port number must be a valid non-reserved UDP port");
1183 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001184 checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
1185
Benedict Wongbabe5d72017-12-03 19:42:36 -08001186 int callingUid = Binder.getCallingUid();
1187 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldd8c74292017-12-13 19:16:33 -08001188 final int resourceId = mNextResourceId++;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001189 FileDescriptor sockFd = null;
1190 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001191 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001192 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1193 }
1194
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001195 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Benedict Wongbabe5d72017-12-03 19:42:36 -08001196 mUidFdTagger.tag(sockFd, callingUid);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001197
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001198 // This code is common to both the unspecified and specified port cases
1199 Os.setsockoptInt(
1200 sockFd,
1201 OsConstants.IPPROTO_UDP,
1202 OsConstants.UDP_ENCAP,
1203 OsConstants.UDP_ENCAP_ESPINUDP);
1204
Benedict Wongba8d3132017-12-06 21:56:35 -08001205 mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
1206 if (port != 0) {
1207 Log.v(TAG, "Binding to port " + port);
1208 Os.bind(sockFd, INADDR_ANY, port);
1209 } else {
1210 port = bindToRandomPort(sockFd);
1211 }
1212
Benedict Wong344bd622017-11-16 15:27:22 -08001213 userRecord.mEncapSocketRecords.put(
1214 resourceId,
1215 new RefcountedResource<EncapSocketRecord>(
1216 new EncapSocketRecord(resourceId, sockFd, port), binder));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001217 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
1218 } catch (IOException | ErrnoException e) {
1219 IoUtils.closeQuietly(sockFd);
1220 }
1221 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1222 // The only reasonable condition that would cause that is resource unavailable.
1223 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001224 }
1225
1226 /** close a socket that has been been allocated by and registered with the system server */
1227 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001228 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1229 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1230 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001231 }
Nathan Harold93962f32017-03-07 13:23:36 -08001232
Benedict Wong8149f6e2018-01-18 18:31:45 -08001233 /**
1234 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1235 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1236 * needed.
1237 */
1238 @Override
1239 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
1240 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder) {
1241 checkNotNull(binder, "Null Binder passed to createTunnelInterface");
1242 checkNotNull(underlyingNetwork, "No underlying network was specified");
1243 checkInetAddress(localAddr);
1244 checkInetAddress(remoteAddr);
1245
1246 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1247 // network (b/72316676).
1248
1249 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1250 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1251 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1252 }
1253
1254 final int resourceId = mNextResourceId++;
1255 final int ikey = reserveNetId();
1256 final int okey = reserveNetId();
1257 String intfName = String.format("%s%d", TUNNEL_INTERFACE_PREFIX, resourceId);
1258
Benedict Wong0fe58a92018-01-19 17:36:02 -08001259 try {
1260 // Calls to netd:
1261 // Create VTI
1262 // Add inbound/outbound global policies
1263 // (use reqid = 0)
1264 mSrvConfig
1265 .getNetdInstance()
1266 .addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001267
Benedict Wongb8ef5412018-01-24 15:31:39 -08001268 for(String wildcardAddr : WILDCARD_ADDRESSES) {
1269 for (int direction : DIRECTIONS) {
1270 int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey;
Benedict Wong0fe58a92018-01-19 17:36:02 -08001271
Benedict Wongb8ef5412018-01-24 15:31:39 -08001272 mSrvConfig
1273 .getNetdInstance()
1274 .ipSecAddSecurityPolicy(
Benedict Wong0fe58a92018-01-19 17:36:02 -08001275 0, // Use 0 for reqId
1276 direction,
Benedict Wongb8ef5412018-01-24 15:31:39 -08001277 wildcardAddr,
1278 wildcardAddr,
Benedict Wong0fe58a92018-01-19 17:36:02 -08001279 0,
1280 mark,
1281 0xffffffff);
Benedict Wongb8ef5412018-01-24 15:31:39 -08001282 }
Benedict Wong0fe58a92018-01-19 17:36:02 -08001283 }
1284
1285 userRecord.mTunnelInterfaceRecords.put(
1286 resourceId,
1287 new RefcountedResource<TunnelInterfaceRecord>(
1288 new TunnelInterfaceRecord(
1289 resourceId,
1290 intfName,
1291 underlyingNetwork,
1292 localAddr,
1293 remoteAddr,
1294 ikey,
1295 okey),
1296 binder));
1297 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1298 } catch (RemoteException e) {
1299 // Release keys if we got an error.
1300 releaseNetId(ikey);
1301 releaseNetId(okey);
1302 throw e.rethrowFromSystemServer();
1303 } catch (ServiceSpecificException e) {
1304 // FIXME: get the error code and throw is at an IOException from Errno Exception
1305 }
1306
1307 // If we make it to here, then something has gone wrong and we couldn't create a VTI.
1308 // Release the keys that we reserved, and return an error status.
1309 releaseNetId(ikey);
1310 releaseNetId(okey);
1311 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001312 }
1313
1314 /**
1315 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1316 * from multiple local IP addresses over the same tunnel.
1317 */
1318 @Override
Benedict Wongda4b0c62018-03-01 18:53:07 -08001319 public synchronized void addAddressToTunnelInterface(
1320 int tunnelResourceId, LinkAddress localAddr) {
1321 enforceNetworkStackPermission();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001322 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1323
1324 // Get tunnelInterface record; if no such interface is found, will throw
1325 // IllegalArgumentException
1326 TunnelInterfaceRecord tunnelInterfaceInfo =
1327 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1328
Benedict Wongda4b0c62018-03-01 18:53:07 -08001329 try {
1330 // We can assume general validity of the IP address, since we get them as a
1331 // LinkAddress, which does some validation.
1332 mSrvConfig
1333 .getNetdInstance()
1334 .interfaceAddAddress(
1335 tunnelInterfaceInfo.mInterfaceName,
1336 localAddr.getAddress().getHostAddress(),
1337 localAddr.getPrefixLength());
1338 } catch (RemoteException e) {
1339 throw e.rethrowFromSystemServer();
1340 } catch (ServiceSpecificException e) {
1341 // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
1342 throw new IllegalArgumentException(e);
1343 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001344 }
1345
1346 /**
1347 * Remove a new local address from the tunnel interface. After removal, the address will no
1348 * longer be available to send from, or receive on.
1349 */
1350 @Override
1351 public synchronized void removeAddressFromTunnelInterface(
Benedict Wongda4b0c62018-03-01 18:53:07 -08001352 int tunnelResourceId, LinkAddress localAddr) {
1353 enforceNetworkStackPermission();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001354 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1355
1356 // Get tunnelInterface record; if no such interface is found, will throw
1357 // IllegalArgumentException
1358 TunnelInterfaceRecord tunnelInterfaceInfo =
1359 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1360
Benedict Wongda4b0c62018-03-01 18:53:07 -08001361 try {
1362 // We can assume general validity of the IP address, since we get them as a
1363 // LinkAddress, which does some validation.
1364 mSrvConfig
1365 .getNetdInstance()
1366 .interfaceDelAddress(
1367 tunnelInterfaceInfo.mInterfaceName,
1368 localAddr.getAddress().getHostAddress(),
1369 localAddr.getPrefixLength());
1370 } catch (RemoteException e) {
1371 throw e.rethrowFromSystemServer();
1372 } catch (ServiceSpecificException e) {
1373 // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
1374 throw new IllegalArgumentException(e);
1375 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001376 }
1377
1378 /**
1379 * Delete a TunnelInterface that has been been allocated by and registered with the system
1380 * server
1381 */
1382 @Override
1383 public synchronized void deleteTunnelInterface(int resourceId) throws RemoteException {
1384 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1385 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1386 }
1387
Benedict Wong4f255702017-11-06 20:49:10 -08001388 @VisibleForTesting
Nathan Harolda2523312018-01-05 19:25:13 -08001389 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1390 IpSecAlgorithm auth = config.getAuthentication();
1391 IpSecAlgorithm crypt = config.getEncryption();
1392 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong4f255702017-11-06 20:49:10 -08001393
Nathan Harolda2523312018-01-05 19:25:13 -08001394 // Validate the algorithm set
1395 Preconditions.checkArgument(
1396 aead != null || crypt != null || auth != null,
1397 "No Encryption or Authentication algorithms specified");
1398 Preconditions.checkArgument(
1399 auth == null || auth.isAuthentication(),
1400 "Unsupported algorithm for Authentication");
1401 Preconditions.checkArgument(
Benedict Wong4f255702017-11-06 20:49:10 -08001402 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harolda2523312018-01-05 19:25:13 -08001403 Preconditions.checkArgument(
1404 aead == null || aead.isAead(),
1405 "Unsupported algorithm for Authenticated Encryption");
1406 Preconditions.checkArgument(
1407 aead == null || (auth == null && crypt == null),
1408 "Authenticated Encryption is mutually exclusive with other Authentication "
1409 + "or Encryption algorithms");
Benedict Wong4f255702017-11-06 20:49:10 -08001410 }
1411
Nathan Harold93962f32017-03-07 13:23:36 -08001412 /**
Nathan Harolda10003d2017-08-23 13:46:33 -07001413 * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
1414 * IllegalArgumentException if they are not.
1415 */
1416 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong344bd622017-11-16 15:27:22 -08001417 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1418
Nathan Harolda10003d2017-08-23 13:46:33 -07001419 switch (config.getEncapType()) {
1420 case IpSecTransform.ENCAP_NONE:
1421 break;
1422 case IpSecTransform.ENCAP_ESPINUDP:
1423 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong344bd622017-11-16 15:27:22 -08001424 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1425 userRecord.mEncapSocketRecords.getResourceOrThrow(
1426 config.getEncapSocketResourceId());
Nathan Harolda10003d2017-08-23 13:46:33 -07001427
1428 int port = config.getEncapRemotePort();
1429 if (port <= 0 || port > 0xFFFF) {
1430 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1431 }
1432 break;
1433 default:
1434 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1435 }
1436
Nathan Harolda2523312018-01-05 19:25:13 -08001437 validateAlgorithms(config);
Nathan Harolda10003d2017-08-23 13:46:33 -07001438
Nathan Harolda2523312018-01-05 19:25:13 -08001439 // Retrieve SPI record; will throw IllegalArgumentException if not found
1440 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1441
Benedict Wonge6b42772017-12-13 18:26:40 -08001442 // Check to ensure that SPI has not already been used.
1443 if (s.getOwnedByTransform()) {
1444 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1445 }
1446
Nathan Harolda2523312018-01-05 19:25:13 -08001447 // If no remote address is supplied, then use one from the SPI.
1448 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1449 config.setDestinationAddress(s.getDestinationAddress());
1450 }
1451
1452 // All remote addresses must match
1453 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1454 throw new IllegalArgumentException("Mismatched remote addresseses.");
1455 }
1456
1457 // This check is technically redundant due to the chain of custody between the SPI and
1458 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1459 // the transform, this will prevent us from messing up.
1460 checkInetAddress(config.getDestinationAddress());
1461
1462 // Require a valid source address for all transforms.
1463 checkInetAddress(config.getSourceAddress());
1464
1465 switch (config.getMode()) {
1466 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold5a920ca2018-02-02 18:34:25 -08001467 break;
Nathan Harolda2523312018-01-05 19:25:13 -08001468 case IpSecTransform.MODE_TUNNEL:
Nathan Harold5a920ca2018-02-02 18:34:25 -08001469 enforceNetworkStackPermission();
Nathan Harolda2523312018-01-05 19:25:13 -08001470 break;
1471 default:
1472 throw new IllegalArgumentException(
1473 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harolda10003d2017-08-23 13:46:33 -07001474 }
1475 }
1476
Nathan Harold5a920ca2018-02-02 18:34:25 -08001477 private void enforceNetworkStackPermission() {
1478 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK,
1479 "IpSecService");
1480 }
1481
Benedict Wong0fe58a92018-01-19 17:36:02 -08001482 private void createOrUpdateTransform(
1483 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1484 throws RemoteException {
1485
1486 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1487 if (encapType != IpSecTransform.ENCAP_NONE) {
1488 encapLocalPort = socketRecord.getPort();
1489 encapRemotePort = c.getEncapRemotePort();
1490 }
1491
1492 IpSecAlgorithm auth = c.getAuthentication();
1493 IpSecAlgorithm crypt = c.getEncryption();
1494 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1495
Benedict Wong9be845c2018-03-15 19:41:41 -07001496 String cryptName;
1497 if (crypt == null) {
1498 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1499 } else {
1500 cryptName = crypt.getName();
1501 }
1502
Benedict Wong0fe58a92018-01-19 17:36:02 -08001503 mSrvConfig
1504 .getNetdInstance()
1505 .ipSecAddSecurityAssociation(
1506 resourceId,
1507 c.getMode(),
1508 c.getSourceAddress(),
1509 c.getDestinationAddress(),
1510 (c.getNetwork() != null) ? c.getNetwork().netId : 0,
1511 spiRecord.getSpi(),
1512 c.getMarkValue(),
1513 c.getMarkMask(),
1514 (auth != null) ? auth.getName() : "",
1515 (auth != null) ? auth.getKey() : new byte[] {},
1516 (auth != null) ? auth.getTruncationLengthBits() : 0,
Benedict Wong9be845c2018-03-15 19:41:41 -07001517 cryptName,
Benedict Wong0fe58a92018-01-19 17:36:02 -08001518 (crypt != null) ? crypt.getKey() : new byte[] {},
1519 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1520 (authCrypt != null) ? authCrypt.getName() : "",
1521 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1522 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1523 encapType,
1524 encapLocalPort,
1525 encapRemotePort);
1526 }
1527
Nathan Harolda10003d2017-08-23 13:46:33 -07001528 /**
Benedict Wong0fe58a92018-01-19 17:36:02 -08001529 * Create a IPsec transform, which represents a single security association in the kernel. The
1530 * transform will be cached by the system server and must be freed when no longer needed. It is
1531 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1532 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold93962f32017-03-07 13:23:36 -08001533 */
1534 @Override
Benedict Wongf33f03132018-01-18 14:38:16 -08001535 public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder)
1536 throws RemoteException {
Nathan Harolda10003d2017-08-23 13:46:33 -07001537 checkIpSecConfig(c);
Benedict Wongf33f03132018-01-18 14:38:16 -08001538 checkNotNull(binder, "Null Binder passed to createTransform");
Nathan Haroldd8c74292017-12-13 19:16:33 -08001539 final int resourceId = mNextResourceId++;
Benedict Wong344bd622017-11-16 15:27:22 -08001540
1541 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong4f9fb812017-12-13 17:16:53 -08001542 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong344bd622017-11-16 15:27:22 -08001543
1544 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001545 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1546 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001547
Benedict Wong344bd622017-11-16 15:27:22 -08001548 EncapSocketRecord socketRecord = null;
Benedict Wong0fe58a92018-01-19 17:36:02 -08001549 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong344bd622017-11-16 15:27:22 -08001550 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1551 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1552 c.getEncapSocketResourceId());
1553 dependencies.add(refcountedSocketRecord);
Benedict Wong344bd622017-11-16 15:27:22 -08001554 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001555 }
1556
Nathan Harolda2523312018-01-05 19:25:13 -08001557 RefcountedResource<SpiRecord> refcountedSpiRecord =
1558 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1559 dependencies.add(refcountedSpiRecord);
1560 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong344bd622017-11-16 15:27:22 -08001561
Nathan Harolda2523312018-01-05 19:25:13 -08001562 try {
Benedict Wong0fe58a92018-01-19 17:36:02 -08001563 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Nathan Harolda2523312018-01-05 19:25:13 -08001564 } catch (ServiceSpecificException e) {
1565 // FIXME: get the error code and throw is at an IOException from Errno Exception
1566 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001567 }
Benedict Wong0fe58a92018-01-19 17:36:02 -08001568
1569 // SA was created successfully, time to construct a record and lock it away
Benedict Wong344bd622017-11-16 15:27:22 -08001570 userRecord.mTransformRecords.put(
1571 resourceId,
1572 new RefcountedResource<TransformRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001573 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong344bd622017-11-16 15:27:22 -08001574 binder,
1575 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001576 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001577 }
1578
1579 /**
1580 * Delete a transport mode transform that was previously allocated by + registered with the
1581 * system server. If this is called on an inactive (or non-existent) transform, it will not
1582 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1583 * other reasons.
1584 */
1585 @Override
Benedict Wongf33f03132018-01-18 14:38:16 -08001586 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001587 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1588 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001589 }
1590
1591 /**
1592 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1593 * association as a correspondent policy to the provided socket
1594 */
1595 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001596 public synchronized void applyTransportModeTransform(
Nathan Harolda2523312018-01-05 19:25:13 -08001597 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001598 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Nathan Harolda2523312018-01-05 19:25:13 -08001599 checkDirection(direction);
Benedict Wong344bd622017-11-16 15:27:22 -08001600 // Get transform record; if no transform is found, will throw IllegalArgumentException
1601 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001602
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001603 // TODO: make this a function.
1604 if (info.pid != getCallingPid() || info.uid != getCallingUid()) {
1605 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1606 }
1607
Benedict Wong8149f6e2018-01-18 18:31:45 -08001608 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001609 IpSecConfig c = info.getConfig();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001610 Preconditions.checkArgument(
1611 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1612 "Transform mode was not Transport mode; cannot be applied to a socket");
1613
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001614 try {
Nathan Harolda2523312018-01-05 19:25:13 -08001615 mSrvConfig
1616 .getNetdInstance()
1617 .ipSecApplyTransportModeTransform(
1618 socket.getFileDescriptor(),
1619 resourceId,
1620 direction,
1621 c.getSourceAddress(),
1622 c.getDestinationAddress(),
1623 info.getSpiRecord().getSpi());
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001624 } catch (ServiceSpecificException e) {
manojboopathic4be79d2017-11-30 17:11:49 -08001625 if (e.errorCode == EINVAL) {
1626 throw new IllegalArgumentException(e.toString());
1627 } else {
1628 throw e;
1629 }
Nathan Harold93962f32017-03-07 13:23:36 -08001630 }
1631 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001632
Nathan Harold93962f32017-03-07 13:23:36 -08001633 /**
Nathan Harolda2523312018-01-05 19:25:13 -08001634 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1635 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1636 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1637 * reserved for future improved input validation.
Nathan Harold93962f32017-03-07 13:23:36 -08001638 */
1639 @Override
Nathan Haroldf73d2522018-01-17 01:00:20 -08001640 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1641 throws RemoteException {
Nathan Harold93962f32017-03-07 13:23:36 -08001642 try {
ludi1a06aa72017-05-12 09:15:00 -07001643 mSrvConfig
1644 .getNetdInstance()
1645 .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
Nathan Harold93962f32017-03-07 13:23:36 -08001646 } catch (ServiceSpecificException e) {
1647 // FIXME: get the error code and throw is at an IOException from Errno Exception
1648 }
1649 }
1650
Benedict Wong8149f6e2018-01-18 18:31:45 -08001651 /**
1652 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1653 * security association as a correspondent policy to the provided interface
1654 */
1655 @Override
1656 public synchronized void applyTunnelModeTransform(
1657 int tunnelResourceId, int direction, int transformResourceId) throws RemoteException {
Nathan Harold5a920ca2018-02-02 18:34:25 -08001658 enforceNetworkStackPermission();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001659 checkDirection(direction);
1660
1661 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1662
1663 // Get transform record; if no transform is found, will throw IllegalArgumentException
1664 TransformRecord transformInfo =
1665 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1666
1667 // Get tunnelInterface record; if no such interface is found, will throw
1668 // IllegalArgumentException
1669 TunnelInterfaceRecord tunnelInterfaceInfo =
1670 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1671
1672 // Get config and check that to-be-applied transform has the correct mode
1673 IpSecConfig c = transformInfo.getConfig();
1674 Preconditions.checkArgument(
1675 c.getMode() == IpSecTransform.MODE_TUNNEL,
1676 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1677
Benedict Wong0fe58a92018-01-19 17:36:02 -08001678 EncapSocketRecord socketRecord = null;
1679 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1680 socketRecord =
1681 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1682 }
1683 SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId());
1684
Benedict Wong8149f6e2018-01-18 18:31:45 -08001685 int mark =
1686 (direction == IpSecManager.DIRECTION_IN)
1687 ? tunnelInterfaceInfo.getIkey()
1688 : tunnelInterfaceInfo.getOkey();
1689
Benedict Wong0fe58a92018-01-19 17:36:02 -08001690 try {
1691 c.setMarkValue(mark);
1692 c.setMarkMask(0xffffffff);
1693
1694 if (direction == IpSecManager.DIRECTION_OUT) {
1695 // Set output mark via underlying network (output only)
1696 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1697
1698 // If outbound, also add SPI to the policy.
Benedict Wongb8ef5412018-01-24 15:31:39 -08001699 for(String wildcardAddr : WILDCARD_ADDRESSES) {
1700 mSrvConfig
1701 .getNetdInstance()
1702 .ipSecUpdateSecurityPolicy(
1703 0, // Use 0 for reqId
1704 direction,
1705 wildcardAddr,
1706 wildcardAddr,
1707 transformInfo.getSpiRecord().getSpi(),
1708 mark,
1709 0xffffffff);
1710 }
Benedict Wong0fe58a92018-01-19 17:36:02 -08001711 }
1712
1713 // Update SA with tunnel mark (ikey or okey based on direction)
1714 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1715 } catch (ServiceSpecificException e) {
1716 if (e.errorCode == EINVAL) {
1717 throw new IllegalArgumentException(e.toString());
1718 } else {
1719 throw e;
1720 }
1721 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001722 }
1723
Nathan Harold93962f32017-03-07 13:23:36 -08001724 @Override
ludib0c95b12017-05-22 10:52:23 -07001725 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001726 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludib0c95b12017-05-22 10:52:23 -07001727
1728 pw.println("IpSecService dump:");
Nathan Harold1afbef42017-03-01 18:55:06 -08001729 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1730 pw.println();
ludib0c95b12017-05-22 10:52:23 -07001731
Benedict Wong344bd622017-11-16 15:27:22 -08001732 pw.println("mUserResourceTracker:");
1733 pw.println(mUserResourceTracker);
Nathan Harold1afbef42017-03-01 18:55:06 -08001734 }
1735}