blob: cd90e3f9b076b2ff8ae66e5c6928750c7c34bf90 [file] [log] [blame]
Nathan Harold1afbef42017-03-01 18:55:06 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static android.Manifest.permission.DUMP;
Nathan Harold93962f32017-03-07 13:23:36 -080020import static android.net.IpSecManager.INVALID_RESOURCE_ID;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070021import static android.system.OsConstants.AF_INET;
manojboopathic4be79d2017-11-30 17:11:49 -080022import static android.system.OsConstants.EINVAL;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070023import static android.system.OsConstants.IPPROTO_UDP;
24import static android.system.OsConstants.SOCK_DGRAM;
25import static com.android.internal.util.Preconditions.checkNotNull;
Nathan Harold1afbef42017-03-01 18:55:06 -080026
Nathan Harold592dadb2018-03-15 18:06:06 -070027import android.annotation.NonNull;
28import android.app.AppOpsManager;
Nathan Harold1afbef42017-03-01 18:55:06 -080029import android.content.Context;
Nathan Harolda2523312018-01-05 19:25:13 -080030import android.net.ConnectivityManager;
Nathan Harold1afbef42017-03-01 18:55:06 -080031import android.net.IIpSecService;
32import android.net.INetd;
Nathan Harold93962f32017-03-07 13:23:36 -080033import android.net.IpSecAlgorithm;
34import android.net.IpSecConfig;
35import android.net.IpSecManager;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070036import android.net.IpSecSpiResponse;
Nathan Harold93962f32017-03-07 13:23:36 -080037import android.net.IpSecTransform;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070038import android.net.IpSecTransformResponse;
Benedict Wong8149f6e2018-01-18 18:31:45 -080039import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070040import android.net.IpSecUdpEncapResponse;
Benedict Wongda4b0c62018-03-01 18:53:07 -080041import android.net.LinkAddress;
Benedict Wong8149f6e2018-01-18 18:31:45 -080042import android.net.Network;
Nathan Harolda10003d2017-08-23 13:46:33 -070043import android.net.NetworkUtils;
Benedict Wongbabe5d72017-12-03 19:42:36 -080044import android.net.TrafficStats;
Nathan Harold1afbef42017-03-01 18:55:06 -080045import android.net.util.NetdService;
Nathan Harold93962f32017-03-07 13:23:36 -080046import android.os.Binder;
Nathan Harold592dadb2018-03-15 18:06:06 -070047import android.os.DeadSystemException;
Nathan Harold93962f32017-03-07 13:23:36 -080048import android.os.IBinder;
49import android.os.ParcelFileDescriptor;
Nathan Harold1afbef42017-03-01 18:55:06 -080050import android.os.RemoteException;
Nathan Harold93962f32017-03-07 13:23:36 -080051import android.os.ServiceSpecificException;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070052import android.system.ErrnoException;
53import android.system.Os;
54import android.system.OsConstants;
Nathan Harolda10003d2017-08-23 13:46:33 -070055import android.text.TextUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080056import android.util.Log;
57import android.util.Slog;
Nathan Harold93962f32017-03-07 13:23:36 -080058import android.util.SparseArray;
Benedict Wong8149f6e2018-01-18 18:31:45 -080059import android.util.SparseBooleanArray;
Nathan Harolda10003d2017-08-23 13:46:33 -070060
Nathan Harold93962f32017-03-07 13:23:36 -080061import com.android.internal.annotations.GuardedBy;
ludi1a06aa72017-05-12 09:15:00 -070062import com.android.internal.annotations.VisibleForTesting;
Benedict Wong4f255702017-11-06 20:49:10 -080063import com.android.internal.util.Preconditions;
Nathan Harolda10003d2017-08-23 13:46:33 -070064
Nathan Harold1afbef42017-03-01 18:55:06 -080065import java.io.FileDescriptor;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070066import java.io.IOException;
Nathan Harold1afbef42017-03-01 18:55:06 -080067import java.io.PrintWriter;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070068import java.net.InetAddress;
69import java.net.InetSocketAddress;
70import java.net.UnknownHostException;
Benedict Wong409c8ca2017-10-26 19:41:43 -070071import java.util.ArrayList;
72import java.util.List;
Nathan Harolda10003d2017-08-23 13:46:33 -070073
Nathan Harold8dc1fd02017-04-04 19:37:48 -070074import libcore.io.IoUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080075
Benedict Wong409c8ca2017-10-26 19:41:43 -070076/**
77 * A service to manage multiple clients that want to access the IpSec API. The service is
78 * responsible for maintaining a list of clients and managing the resources (and related quotas)
79 * that each of them own.
80 *
81 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
82 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
83 * thread is ever running at a time.
84 *
85 * @hide
86 */
Nathan Harold1afbef42017-03-01 18:55:06 -080087public class IpSecService extends IIpSecService.Stub {
88 private static final String TAG = "IpSecService";
89 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Nathan Harold8dc1fd02017-04-04 19:37:48 -070090
Nathan Harold1afbef42017-03-01 18:55:06 -080091 private static final String NETD_SERVICE_NAME = "netd";
Nathan Harold93962f32017-03-07 13:23:36 -080092 private static final int[] DIRECTIONS =
Nathan Harolda2523312018-01-05 19:25:13 -080093 new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN};
Benedict Wongb8ef5412018-01-24 15:31:39 -080094 private static final String[] WILDCARD_ADDRESSES = new String[]{"0.0.0.0", "::"};
Nathan Harold1afbef42017-03-01 18:55:06 -080095
ludi1a06aa72017-05-12 09:15:00 -070096 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold8dc1fd02017-04-04 19:37:48 -070097 private static final int MAX_PORT_BIND_ATTEMPTS = 10;
98 private static final InetAddress INADDR_ANY;
99
100 static {
101 try {
102 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
103 } catch (UnknownHostException e) {
104 throw new RuntimeException(e);
105 }
106 }
107
108 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
109 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
Benedict Wong8149f6e2018-01-18 18:31:45 -0800110 static final String TUNNEL_INTERFACE_PREFIX = "ipsec";
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700111
112 /* Binder context for this service */
Nathan Harold1afbef42017-03-01 18:55:06 -0800113 private final Context mContext;
114
Nathan Haroldd8c74292017-12-13 19:16:33 -0800115 /**
Nathan Harolda2523312018-01-05 19:25:13 -0800116 * The next non-repeating global ID for tracking resources between users, this service, and
117 * kernel data structures. Accessing this variable is not thread safe, so it is only read or
118 * modified within blocks synchronized on IpSecService.this. We want to avoid -1
119 * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
Nathan Haroldd8c74292017-12-13 19:16:33 -0800120 */
121 @GuardedBy("IpSecService.this")
122 private int mNextResourceId = 1;
Nathan Harold1afbef42017-03-01 18:55:06 -0800123
ludi1a06aa72017-05-12 09:15:00 -0700124 interface IpSecServiceConfiguration {
125 INetd getNetdInstance() throws RemoteException;
126
127 static IpSecServiceConfiguration GETSRVINSTANCE =
128 new IpSecServiceConfiguration() {
129 @Override
130 public INetd getNetdInstance() throws RemoteException {
131 final INetd netd = NetdService.getInstance();
132 if (netd == null) {
133 throw new RemoteException("Failed to Get Netd Instance");
134 }
135 return netd;
136 }
137 };
138 }
139
140 private final IpSecServiceConfiguration mSrvConfig;
Benedict Wongbabe5d72017-12-03 19:42:36 -0800141 final UidFdTagger mUidFdTagger;
ludi1a06aa72017-05-12 09:15:00 -0700142
Benedict Wong409c8ca2017-10-26 19:41:43 -0700143 /**
144 * Interface for user-reference and kernel-resource cleanup.
145 *
146 * <p>This interface must be implemented for a resource to be reference counted.
147 */
148 @VisibleForTesting
149 public interface IResource {
150 /**
151 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
152 * objects dependent on it.
153 *
154 * <p>Implementations of this method are expected to remove references to the IResource
155 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
156 * the resource is considered invalid for user access or allocation or use in other
157 * resources.
158 *
159 * <p>References to the IResource object may be held by other RefcountedResource objects,
Benedict Wong4f9fb812017-12-13 17:16:53 -0800160 * and as such, the underlying resources and quota may not be cleaned up.
Benedict Wong409c8ca2017-10-26 19:41:43 -0700161 */
162 void invalidate() throws RemoteException;
163
164 /**
165 * Releases underlying resources and related quotas.
166 *
167 * <p>Implementations of this method are expected to remove all system resources that are
168 * tracked by the IResource object. Due to other RefcountedResource objects potentially
Benedict Wong344bd622017-11-16 15:27:22 -0800169 * having references to the IResource object, freeUnderlyingResources may not always be
Benedict Wong409c8ca2017-10-26 19:41:43 -0700170 * called from releaseIfUnreferencedRecursively().
171 */
172 void freeUnderlyingResources() throws RemoteException;
173 }
174
175 /**
176 * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
177 *
178 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
179 * RefcountedResource object creates an explicit reference that must be freed by calling
180 * userRelease(). Additionally, adding this object as a child of another RefcountedResource
181 * object will add an implicit reference.
182 *
183 * <p>Resources are cleaned up when all references, both implicit and explicit, are released
184 * (ie, when userRelease() is called and when all parents have called releaseReference() on this
185 * object.)
186 */
187 @VisibleForTesting
188 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
189 private final T mResource;
190 private final List<RefcountedResource> mChildren;
191 int mRefCount = 1; // starts at 1 for user's reference.
192 IBinder mBinder;
193
194 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
195 synchronized (IpSecService.this) {
196 this.mResource = resource;
197 this.mChildren = new ArrayList<>(children.length);
198 this.mBinder = binder;
199
200 for (RefcountedResource child : children) {
201 mChildren.add(child);
202 child.mRefCount++;
203 }
204
205 try {
206 mBinder.linkToDeath(this, 0);
207 } catch (RemoteException e) {
208 binderDied();
209 }
210 }
211 }
212
213 /**
214 * If the Binder object dies, this function is called to free the system resources that are
Benedict Wong344bd622017-11-16 15:27:22 -0800215 * being tracked by this record and to subsequently release this record for garbage
Benedict Wong409c8ca2017-10-26 19:41:43 -0700216 * collection
217 */
218 @Override
219 public void binderDied() {
220 synchronized (IpSecService.this) {
221 try {
222 userRelease();
223 } catch (Exception e) {
224 Log.e(TAG, "Failed to release resource: " + e);
225 }
226 }
227 }
228
229 public T getResource() {
230 return mResource;
231 }
232
233 /**
234 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
235 * arrays)
236 *
237 * <p>If this method has been previously called, the RefcountedResource's binder field will
238 * be null, and the method will return without performing the cleanup a second time.
239 *
240 * <p>Note that calling this function does not imply that kernel resources will be freed at
241 * this time, or that the related quota will be returned. Such actions will only be
242 * performed upon the reference count reaching zero.
243 */
244 @GuardedBy("IpSecService.this")
245 public void userRelease() throws RemoteException {
246 // Prevent users from putting reference counts into a bad state by calling
247 // userRelease() multiple times.
248 if (mBinder == null) {
249 return;
250 }
251
252 mBinder.unlinkToDeath(this, 0);
253 mBinder = null;
254
255 mResource.invalidate();
256
257 releaseReference();
258 }
259
260 /**
261 * Removes a reference to this resource. If the resultant reference count is zero, the
262 * underlying resources are freed, and references to all child resources are also dropped
263 * recursively (resulting in them freeing their resources and children, etcetera)
264 *
265 * <p>This method also sets the reference count to an invalid value (-1) to signify that it
266 * has been fully released. Any subsequent calls to this method will result in an
267 * IllegalStateException being thrown due to resource already having been previously
268 * released
269 */
270 @VisibleForTesting
271 @GuardedBy("IpSecService.this")
272 public void releaseReference() throws RemoteException {
273 mRefCount--;
274
275 if (mRefCount > 0) {
276 return;
277 } else if (mRefCount < 0) {
278 throw new IllegalStateException(
279 "Invalid operation - resource has already been released.");
280 }
281
282 // Cleanup own resources
283 mResource.freeUnderlyingResources();
284
285 // Cleanup child resources as needed
286 for (RefcountedResource<? extends IResource> child : mChildren) {
287 child.releaseReference();
288 }
289
290 // Enforce that resource cleanup can only be called once
291 // By decrementing the refcount (from 0 to -1), the next call will throw an
292 // IllegalStateException - it has already been released fully.
293 mRefCount--;
294 }
295
296 @Override
297 public String toString() {
298 return new StringBuilder()
299 .append("{mResource=")
300 .append(mResource)
301 .append(", mRefCount=")
302 .append(mRefCount)
303 .append(", mChildren=")
304 .append(mChildren)
305 .append("}")
306 .toString();
307 }
308 }
309
Benedict Wong4f9fb812017-12-13 17:16:53 -0800310 /**
311 * Very simple counting class that looks much like a counting semaphore
312 *
313 * <p>This class is not thread-safe, and expects that that users of this class will ensure
314 * synchronization and thread safety by holding the IpSecService.this instance lock.
315 */
Benedict Wong344bd622017-11-16 15:27:22 -0800316 @VisibleForTesting
317 static class ResourceTracker {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700318 private final int mMax;
319 int mCurrent;
320
321 ResourceTracker(int max) {
322 mMax = max;
323 mCurrent = 0;
324 }
325
Benedict Wong344bd622017-11-16 15:27:22 -0800326 boolean isAvailable() {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700327 return (mCurrent < mMax);
328 }
329
Benedict Wong344bd622017-11-16 15:27:22 -0800330 void take() {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700331 if (!isAvailable()) {
332 Log.wtf(TAG, "Too many resources allocated!");
333 }
334 mCurrent++;
335 }
336
Benedict Wong344bd622017-11-16 15:27:22 -0800337 void give() {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700338 if (mCurrent <= 0) {
339 Log.wtf(TAG, "We've released this resource too many times");
340 }
341 mCurrent--;
342 }
ludi3e5ea232017-08-10 15:44:40 -0700343
344 @Override
345 public String toString() {
346 return new StringBuilder()
347 .append("{mCurrent=")
348 .append(mCurrent)
349 .append(", mMax=")
350 .append(mMax)
351 .append("}")
352 .toString();
353 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700354 }
355
Benedict Wong344bd622017-11-16 15:27:22 -0800356 @VisibleForTesting
357 static final class UserRecord {
Benedict Wong344bd622017-11-16 15:27:22 -0800358 /* Maximum number of each type of resource that a single UID may possess */
Benedict Wong8149f6e2018-01-18 18:31:45 -0800359 public static final int MAX_NUM_TUNNEL_INTERFACES = 2;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700360 public static final int MAX_NUM_ENCAP_SOCKETS = 2;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700361 public static final int MAX_NUM_TRANSFORMS = 4;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700362 public static final int MAX_NUM_SPIS = 8;
363
Benedict Wong4f9fb812017-12-13 17:16:53 -0800364 /**
365 * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
366 * and explicit (user) reference management.
367 *
368 * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
369 *
370 * <p>Resources are removed from this array when the user releases their explicit reference
371 * by calling one of the releaseResource() methods.
372 */
Benedict Wong344bd622017-11-16 15:27:22 -0800373 final RefcountedResourceArray<SpiRecord> mSpiRecords =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800374 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
Benedict Wong344bd622017-11-16 15:27:22 -0800375 final RefcountedResourceArray<TransformRecord> mTransformRecords =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800376 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
Benedict Wong344bd622017-11-16 15:27:22 -0800377 final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800378 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
Benedict Wong8149f6e2018-01-18 18:31:45 -0800379 final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
380 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
Benedict Wong4f9fb812017-12-13 17:16:53 -0800381
382 /**
383 * Trackers for quotas for each of the OwnedResource types.
384 *
385 * <p>These trackers are separate from the resource arrays, since they are incremented and
386 * decremented at different points in time. Specifically, quota is only returned upon final
387 * resource deallocation (after all explicit and implicit references are released). Note
388 * that it is possible that calls to releaseResource() will not return the used quota if
389 * there are other resources that depend on (are parents of) the resource being released.
390 */
391 final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
392 final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
Benedict Wong344bd622017-11-16 15:27:22 -0800393 final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
Benedict Wong8149f6e2018-01-18 18:31:45 -0800394 final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
Benedict Wong344bd622017-11-16 15:27:22 -0800395
396 void removeSpiRecord(int resourceId) {
397 mSpiRecords.remove(resourceId);
Nathan Harolda1afbd82017-04-24 16:16:34 -0700398 }
399
Benedict Wong344bd622017-11-16 15:27:22 -0800400 void removeTransformRecord(int resourceId) {
401 mTransformRecords.remove(resourceId);
402 }
403
Benedict Wong8149f6e2018-01-18 18:31:45 -0800404 void removeTunnelInterfaceRecord(int resourceId) {
405 mTunnelInterfaceRecords.remove(resourceId);
406 }
407
Benedict Wong344bd622017-11-16 15:27:22 -0800408 void removeEncapSocketRecord(int resourceId) {
409 mEncapSocketRecords.remove(resourceId);
410 }
411
412 @Override
413 public String toString() {
414 return new StringBuilder()
415 .append("{mSpiQuotaTracker=")
416 .append(mSpiQuotaTracker)
417 .append(", mTransformQuotaTracker=")
418 .append(mTransformQuotaTracker)
419 .append(", mSocketQuotaTracker=")
420 .append(mSocketQuotaTracker)
Benedict Wongb8ef5412018-01-24 15:31:39 -0800421 .append(", mTunnelQuotaTracker=")
422 .append(mTunnelQuotaTracker)
Benedict Wong344bd622017-11-16 15:27:22 -0800423 .append(", mSpiRecords=")
424 .append(mSpiRecords)
425 .append(", mTransformRecords=")
426 .append(mTransformRecords)
427 .append(", mEncapSocketRecords=")
428 .append(mEncapSocketRecords)
Benedict Wongb8ef5412018-01-24 15:31:39 -0800429 .append(", mTunnelInterfaceRecords=")
430 .append(mTunnelInterfaceRecords)
Benedict Wong344bd622017-11-16 15:27:22 -0800431 .append("}")
432 .toString();
433 }
434 }
435
Benedict Wong4f9fb812017-12-13 17:16:53 -0800436 /**
437 * This class is not thread-safe, and expects that that users of this class will ensure
438 * synchronization and thread safety by holding the IpSecService.this instance lock.
439 */
Benedict Wong344bd622017-11-16 15:27:22 -0800440 @VisibleForTesting
441 static final class UserResourceTracker {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700442 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
443
Benedict Wong4f9fb812017-12-13 17:16:53 -0800444 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
Benedict Wong344bd622017-11-16 15:27:22 -0800445 public UserRecord getUserRecord(int uid) {
446 checkCallerUid(uid);
447
Nathan Harolda1afbd82017-04-24 16:16:34 -0700448 UserRecord r = mUserRecords.get(uid);
449 if (r == null) {
450 r = new UserRecord();
451 mUserRecords.put(uid, r);
452 }
453 return r;
454 }
ludi3e5ea232017-08-10 15:44:40 -0700455
Benedict Wong344bd622017-11-16 15:27:22 -0800456 /** Safety method; guards against access of other user's UserRecords */
457 private void checkCallerUid(int uid) {
458 if (uid != Binder.getCallingUid()
459 && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
460 throw new SecurityException("Attempted access of unowned resources");
461 }
462 }
463
ludi3e5ea232017-08-10 15:44:40 -0700464 @Override
465 public String toString() {
466 return mUserRecords.toString();
467 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700468 }
469
Benedict Wong344bd622017-11-16 15:27:22 -0800470 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700471
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700472 /**
Benedict Wong4f9fb812017-12-13 17:16:53 -0800473 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
Benedict Wong344bd622017-11-16 15:27:22 -0800474 * resources. It relies on a provided resourceId that should uniquely identify the kernel
475 * resource. To use this class, the user should implement the invalidate() and
476 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
Benedict Wong4f9fb812017-12-13 17:16:53 -0800477 * tracking arrays and kernel resources, respectively.
478 *
479 * <p>This class associates kernel resources with the UID that owns and controls them.
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700480 */
Benedict Wong4f9fb812017-12-13 17:16:53 -0800481 private abstract class OwnedResourceRecord implements IResource {
Nathan Harold93962f32017-03-07 13:23:36 -0800482 final int pid;
483 final int uid;
Benedict Wong344bd622017-11-16 15:27:22 -0800484 protected final int mResourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800485
Benedict Wong4f9fb812017-12-13 17:16:53 -0800486 OwnedResourceRecord(int resourceId) {
Nathan Harold93962f32017-03-07 13:23:36 -0800487 super();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700488 if (resourceId == INVALID_RESOURCE_ID) {
489 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
490 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700491 mResourceId = resourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800492 pid = Binder.getCallingPid();
493 uid = Binder.getCallingUid();
494
Nathan Harolda1afbd82017-04-24 16:16:34 -0700495 getResourceTracker().take();
Nathan Harold93962f32017-03-07 13:23:36 -0800496 }
497
Benedict Wong344bd622017-11-16 15:27:22 -0800498 @Override
499 public abstract void invalidate() throws RemoteException;
500
501 /** Convenience method; retrieves the user resource record for the stored UID. */
502 protected UserRecord getUserRecord() {
503 return mUserResourceTracker.getUserRecord(uid);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700504 }
505
Benedict Wong344bd622017-11-16 15:27:22 -0800506 @Override
507 public abstract void freeUnderlyingResources() throws RemoteException;
ludib0c95b12017-05-22 10:52:23 -0700508
Nathan Harolda1afbd82017-04-24 16:16:34 -0700509 /** Get the resource tracker for this resource */
510 protected abstract ResourceTracker getResourceTracker();
511
ludib0c95b12017-05-22 10:52:23 -0700512 @Override
513 public String toString() {
514 return new StringBuilder()
515 .append("{mResourceId=")
516 .append(mResourceId)
517 .append(", pid=")
518 .append(pid)
519 .append(", uid=")
520 .append(uid)
ludib0c95b12017-05-22 10:52:23 -0700521 .append("}")
522 .toString();
523 }
Nathan Harold93962f32017-03-07 13:23:36 -0800524 };
525
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700526 /**
Benedict Wong344bd622017-11-16 15:27:22 -0800527 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
528 *
529 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
530 * if a key is not found during a retrieval process.
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700531 */
Benedict Wong344bd622017-11-16 15:27:22 -0800532 static class RefcountedResourceArray<T extends IResource> {
533 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
534 private final String mTypeName;
Nathan Harold93962f32017-03-07 13:23:36 -0800535
Benedict Wong344bd622017-11-16 15:27:22 -0800536 public RefcountedResourceArray(String typeName) {
537 this.mTypeName = typeName;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700538 }
539
Benedict Wong344bd622017-11-16 15:27:22 -0800540 /**
541 * Accessor method to get inner resource object.
542 *
543 * @throws IllegalArgumentException if no resource with provided key is found.
544 */
545 T getResourceOrThrow(int key) {
546 return getRefcountedResourceOrThrow(key).getResource();
547 }
548
549 /**
550 * Accessor method to get reference counting wrapper.
551 *
552 * @throws IllegalArgumentException if no resource with provided key is found.
553 */
554 RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
555 RefcountedResource<T> resource = mArray.get(key);
556 if (resource == null) {
557 throw new IllegalArgumentException(
558 String.format("No such %s found for given id: %d", mTypeName, key));
559 }
560
561 return resource;
562 }
563
564 void put(int key, RefcountedResource<T> obj) {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700565 checkNotNull(obj, "Null resources cannot be added");
566 mArray.put(key, obj);
567 }
568
569 void remove(int key) {
570 mArray.remove(key);
571 }
ludib0c95b12017-05-22 10:52:23 -0700572
573 @Override
574 public String toString() {
575 return mArray.toString();
576 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700577 }
578
Benedict Wong4f9fb812017-12-13 17:16:53 -0800579 /**
580 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
581 * created, the SpiRecord that originally tracked the SAs will reliquish the
582 * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
583 */
584 private final class TransformRecord extends OwnedResourceRecord {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700585 private final IpSecConfig mConfig;
Nathan Harolda2523312018-01-05 19:25:13 -0800586 private final SpiRecord mSpi;
Benedict Wong344bd622017-11-16 15:27:22 -0800587 private final EncapSocketRecord mSocket;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700588
589 TransformRecord(
Nathan Harolda2523312018-01-05 19:25:13 -0800590 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
Benedict Wong344bd622017-11-16 15:27:22 -0800591 super(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -0800592 mConfig = config;
Nathan Harolda2523312018-01-05 19:25:13 -0800593 mSpi = spi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700594 mSocket = socket;
Benedict Wonge6b42772017-12-13 18:26:40 -0800595
596 spi.setOwnedByTransform();
Nathan Harold93962f32017-03-07 13:23:36 -0800597 }
598
599 public IpSecConfig getConfig() {
600 return mConfig;
601 }
602
Nathan Harolda2523312018-01-05 19:25:13 -0800603 public SpiRecord getSpiRecord() {
604 return mSpi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700605 }
606
Benedict Wong0fe58a92018-01-19 17:36:02 -0800607 public EncapSocketRecord getSocketRecord() {
608 return mSocket;
609 }
610
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700611 /** always guarded by IpSecService#this */
Nathan Harold93962f32017-03-07 13:23:36 -0800612 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800613 public void freeUnderlyingResources() {
Nathan Harolda2523312018-01-05 19:25:13 -0800614 int spi = mSpi.getSpi();
615 try {
616 mSrvConfig
617 .getNetdInstance()
618 .ipSecDeleteSecurityAssociation(
619 mResourceId,
620 mConfig.getSourceAddress(),
621 mConfig.getDestinationAddress(),
Di Lu0b611f42018-01-11 11:35:25 -0800622 spi,
623 mConfig.getMarkValue(),
624 mConfig.getMarkMask());
Benedict Wongda4b0c62018-03-01 18:53:07 -0800625 } catch (RemoteException | ServiceSpecificException e) {
626 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
Nathan Harold93962f32017-03-07 13:23:36 -0800627 }
Nathan Harold93962f32017-03-07 13:23:36 -0800628
Benedict Wong344bd622017-11-16 15:27:22 -0800629 getResourceTracker().give();
Nathan Harold93962f32017-03-07 13:23:36 -0800630 }
ludib0c95b12017-05-22 10:52:23 -0700631
Benedict Wong344bd622017-11-16 15:27:22 -0800632 @Override
633 public void invalidate() throws RemoteException {
634 getUserRecord().removeTransformRecord(mResourceId);
635 }
636
637 @Override
Nathan Harolda1afbd82017-04-24 16:16:34 -0700638 protected ResourceTracker getResourceTracker() {
Benedict Wong344bd622017-11-16 15:27:22 -0800639 return getUserRecord().mTransformQuotaTracker;
Nathan Harolda1afbd82017-04-24 16:16:34 -0700640 }
641
ludib0c95b12017-05-22 10:52:23 -0700642 @Override
643 public String toString() {
644 StringBuilder strBuilder = new StringBuilder();
645 strBuilder
646 .append("{super=")
647 .append(super.toString())
648 .append(", mSocket=")
649 .append(mSocket)
Nathan Harolda2523312018-01-05 19:25:13 -0800650 .append(", mSpi.mResourceId=")
651 .append(mSpi.mResourceId)
ludib0c95b12017-05-22 10:52:23 -0700652 .append(", mConfig=")
653 .append(mConfig)
654 .append("}");
655 return strBuilder.toString();
656 }
Nathan Harold93962f32017-03-07 13:23:36 -0800657 }
658
Benedict Wong4f9fb812017-12-13 17:16:53 -0800659 /**
660 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
661 * responsibility for cleaning up underlying resources will be passed to the TransformRecord
662 * object
663 */
664 private final class SpiRecord extends OwnedResourceRecord {
Nathan Harolda2523312018-01-05 19:25:13 -0800665 private final String mSourceAddress;
666 private final String mDestinationAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800667 private int mSpi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700668
669 private boolean mOwnedByTransform = false;
Nathan Harold93962f32017-03-07 13:23:36 -0800670
Nathan Harolda2523312018-01-05 19:25:13 -0800671 SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
Benedict Wong344bd622017-11-16 15:27:22 -0800672 super(resourceId);
Nathan Harolda2523312018-01-05 19:25:13 -0800673 mSourceAddress = sourceAddress;
674 mDestinationAddress = destinationAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800675 mSpi = spi;
Nathan Harold93962f32017-03-07 13:23:36 -0800676 }
677
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700678 /** always guarded by IpSecService#this */
679 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800680 public void freeUnderlyingResources() {
Nathan Harold93962f32017-03-07 13:23:36 -0800681 try {
Nathan Harold82485a22018-02-27 19:19:40 -0800682 if (!mOwnedByTransform) {
683 mSrvConfig
684 .getNetdInstance()
685 .ipSecDeleteSecurityAssociation(
686 mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
687 }
Benedict Wongda4b0c62018-03-01 18:53:07 -0800688 } catch (ServiceSpecificException | RemoteException e) {
689 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
Nathan Harold93962f32017-03-07 13:23:36 -0800690 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700691
692 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -0800693
Benedict Wong344bd622017-11-16 15:27:22 -0800694 getResourceTracker().give();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700695 }
696
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700697 public int getSpi() {
698 return mSpi;
699 }
700
Nathan Harolda2523312018-01-05 19:25:13 -0800701 public String getDestinationAddress() {
702 return mDestinationAddress;
703 }
704
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700705 public void setOwnedByTransform() {
706 if (mOwnedByTransform) {
707 // Programming error
Andreas Gamped6d8e452017-07-11 10:25:09 -0700708 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700709 }
710
711 mOwnedByTransform = true;
Nathan Harold93962f32017-03-07 13:23:36 -0800712 }
ludib0c95b12017-05-22 10:52:23 -0700713
Benedict Wonge6b42772017-12-13 18:26:40 -0800714 public boolean getOwnedByTransform() {
715 return mOwnedByTransform;
716 }
717
ludib0c95b12017-05-22 10:52:23 -0700718 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800719 public void invalidate() throws RemoteException {
720 getUserRecord().removeSpiRecord(mResourceId);
721 }
722
723 @Override
724 protected ResourceTracker getResourceTracker() {
725 return getUserRecord().mSpiQuotaTracker;
726 }
727
728 @Override
ludib0c95b12017-05-22 10:52:23 -0700729 public String toString() {
730 StringBuilder strBuilder = new StringBuilder();
731 strBuilder
732 .append("{super=")
733 .append(super.toString())
734 .append(", mSpi=")
735 .append(mSpi)
Nathan Harolda2523312018-01-05 19:25:13 -0800736 .append(", mSourceAddress=")
737 .append(mSourceAddress)
738 .append(", mDestinationAddress=")
739 .append(mDestinationAddress)
ludib0c95b12017-05-22 10:52:23 -0700740 .append(", mOwnedByTransform=")
741 .append(mOwnedByTransform)
742 .append("}");
743 return strBuilder.toString();
744 }
Nathan Harold93962f32017-03-07 13:23:36 -0800745 }
746
Benedict Wong8149f6e2018-01-18 18:31:45 -0800747 // These values have been reserved in ConnectivityService
748 @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
749
750 @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400;
751
752 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
753 private int mNextTunnelNetIdIndex = 0;
754
755 /**
756 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
757 *
758 * <p>This method should only be called from Binder threads. Do not call this from within the
759 * system server as it will crash the system on failure.
760 *
761 * @return an integer key within the netId range, if successful
762 * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
763 */
764 @VisibleForTesting
765 int reserveNetId() {
766 synchronized (mTunnelNetIds) {
767 for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
768 int index = mNextTunnelNetIdIndex;
769 int netId = index + TUN_INTF_NETID_START;
770 if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
771 if (!mTunnelNetIds.get(netId)) {
772 mTunnelNetIds.put(netId, true);
773 return netId;
774 }
775 }
776 }
777 throw new IllegalStateException("No free netIds to allocate");
778 }
779
780 @VisibleForTesting
781 void releaseNetId(int netId) {
782 synchronized (mTunnelNetIds) {
783 mTunnelNetIds.delete(netId);
784 }
785 }
786
787 private final class TunnelInterfaceRecord extends OwnedResourceRecord {
788 private final String mInterfaceName;
789 private final Network mUnderlyingNetwork;
790
791 // outer addresses
792 private final String mLocalAddress;
793 private final String mRemoteAddress;
794
795 private final int mIkey;
796 private final int mOkey;
797
798 TunnelInterfaceRecord(
799 int resourceId,
800 String interfaceName,
801 Network underlyingNetwork,
802 String localAddr,
803 String remoteAddr,
804 int ikey,
805 int okey) {
806 super(resourceId);
807
808 mInterfaceName = interfaceName;
809 mUnderlyingNetwork = underlyingNetwork;
810 mLocalAddress = localAddr;
811 mRemoteAddress = remoteAddr;
812 mIkey = ikey;
813 mOkey = okey;
814 }
815
816 /** always guarded by IpSecService#this */
817 @Override
818 public void freeUnderlyingResources() {
Benedict Wong0fe58a92018-01-19 17:36:02 -0800819 // Calls to netd
Benedict Wong8149f6e2018-01-18 18:31:45 -0800820 // Teardown VTI
821 // Delete global policies
Benedict Wong0fe58a92018-01-19 17:36:02 -0800822 try {
823 mSrvConfig.getNetdInstance().removeVirtualTunnelInterface(mInterfaceName);
824
Benedict Wongb8ef5412018-01-24 15:31:39 -0800825 for(String wildcardAddr : WILDCARD_ADDRESSES) {
826 for (int direction : DIRECTIONS) {
827 int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey;
828 mSrvConfig
829 .getNetdInstance()
830 .ipSecDeleteSecurityPolicy(
831 0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff);
832 }
Benedict Wong0fe58a92018-01-19 17:36:02 -0800833 }
Benedict Wongda4b0c62018-03-01 18:53:07 -0800834 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong0fe58a92018-01-19 17:36:02 -0800835 Log.e(
836 TAG,
837 "Failed to delete VTI with interface name: "
838 + mInterfaceName
839 + " and id: "
Benedict Wongda4b0c62018-03-01 18:53:07 -0800840 + mResourceId, e);
Benedict Wong0fe58a92018-01-19 17:36:02 -0800841 }
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
Benedict Wong6ea93c42018-03-27 16:55:48 -0700937 public FileDescriptor getFileDescriptor() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700938 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
Nathan Harold592dadb2018-03-15 18:06:06 -0700980 @NonNull
981 private AppOpsManager getAppOpsManager() {
982 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
983 if(appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
984 return appOps;
985 }
986
ludi1a06aa72017-05-12 09:15:00 -0700987 /** @hide */
988 @VisibleForTesting
989 public IpSecService(Context context, IpSecServiceConfiguration config) {
Nathan Harolda2523312018-01-05 19:25:13 -0800990 this(
991 context,
992 config,
993 (fd, uid) -> {
994 try {
995 TrafficStats.setThreadStatsUid(uid);
996 TrafficStats.tagFileDescriptor(fd);
997 } finally {
998 TrafficStats.clearThreadStatsUid();
999 }
1000 });
Benedict Wongbabe5d72017-12-03 19:42:36 -08001001 }
1002
1003 /** @hide */
1004 @VisibleForTesting
1005 public IpSecService(
1006 Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
ludi1a06aa72017-05-12 09:15:00 -07001007 mContext = context;
1008 mSrvConfig = config;
Benedict Wongbabe5d72017-12-03 19:42:36 -08001009 mUidFdTagger = uidFdTagger;
ludi1a06aa72017-05-12 09:15:00 -07001010 }
1011
Nathan Harold1afbef42017-03-01 18:55:06 -08001012 public void systemReady() {
1013 if (isNetdAlive()) {
1014 Slog.d(TAG, "IpSecService is ready");
1015 } else {
1016 Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
1017 }
1018 }
1019
1020 private void connectNativeNetdService() {
1021 // Avoid blocking the system server to do this
Nathan Haroldb0e05082017-07-17 14:01:53 -07001022 new Thread() {
1023 @Override
1024 public void run() {
1025 synchronized (IpSecService.this) {
ludi1a06aa72017-05-12 09:15:00 -07001026 NetdService.get(NETD_FETCH_TIMEOUT_MS);
Nathan Haroldb0e05082017-07-17 14:01:53 -07001027 }
1028 }
1029 }.start();
Nathan Harold1afbef42017-03-01 18:55:06 -08001030 }
1031
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001032 synchronized boolean isNetdAlive() {
1033 try {
ludi1a06aa72017-05-12 09:15:00 -07001034 final INetd netd = mSrvConfig.getNetdInstance();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001035 if (netd == null) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001036 return false;
1037 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001038 return netd.isAlive();
1039 } catch (RemoteException re) {
1040 return false;
Nathan Harold1afbef42017-03-01 18:55:06 -08001041 }
1042 }
1043
Nathan Harolda10003d2017-08-23 13:46:33 -07001044 /**
1045 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1046 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1047 */
1048 private static void checkInetAddress(String inetAddress) {
1049 if (TextUtils.isEmpty(inetAddress)) {
1050 throw new IllegalArgumentException("Unspecified address");
1051 }
1052
1053 InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
1054
1055 if (checkAddr.isAnyLocalAddress()) {
1056 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1057 }
1058 }
1059
1060 /**
1061 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1062 * DIRECTION_IN or DIRECTION_OUT
1063 */
1064 private static void checkDirection(int direction) {
1065 switch (direction) {
Nathan Harolda2523312018-01-05 19:25:13 -08001066 case IpSecManager.DIRECTION_OUT:
1067 case IpSecManager.DIRECTION_IN:
Nathan Harolda10003d2017-08-23 13:46:33 -07001068 return;
1069 }
1070 throw new IllegalArgumentException("Invalid Direction: " + direction);
1071 }
1072
Nathan Harold93962f32017-03-07 13:23:36 -08001073 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001074 @Override
1075 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harolda2523312018-01-05 19:25:13 -08001076 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1077 checkInetAddress(destinationAddress);
Nathan Harold0f8c8bb02018-03-28 08:52:51 -07001078 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1079 if (requestedSpi > 0 && requestedSpi < 256) {
1080 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1081 }
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001082 checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harolda10003d2017-08-23 13:46:33 -07001083
Benedict Wong344bd622017-11-16 15:27:22 -08001084 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Nathan Haroldd8c74292017-12-13 19:16:33 -08001085 final int resourceId = mNextResourceId++;
Nathan Harold93962f32017-03-07 13:23:36 -08001086
1087 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -08001088 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001089 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001090 return new IpSecSpiResponse(
Nathan Harolda10003d2017-08-23 13:46:33 -07001091 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harolda1afbd82017-04-24 16:16:34 -07001092 }
Nathan Harolda2523312018-01-05 19:25:13 -08001093
Nathan Harold93962f32017-03-07 13:23:36 -08001094 spi =
ludi1a06aa72017-05-12 09:15:00 -07001095 mSrvConfig
1096 .getNetdInstance()
Nathan Harolda2523312018-01-05 19:25:13 -08001097 .ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi);
Nathan Harold93962f32017-03-07 13:23:36 -08001098 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong344bd622017-11-16 15:27:22 -08001099 userRecord.mSpiRecords.put(
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001100 resourceId,
Benedict Wong344bd622017-11-16 15:27:22 -08001101 new RefcountedResource<SpiRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001102 new SpiRecord(resourceId, "", destinationAddress, spi), binder));
Nathan Harold93962f32017-03-07 13:23:36 -08001103 } catch (ServiceSpecificException e) {
1104 // TODO: Add appropriate checks when other ServiceSpecificException types are supported
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001105 return new IpSecSpiResponse(
Nathan Harolda1afbd82017-04-24 16:16:34 -07001106 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold93962f32017-03-07 13:23:36 -08001107 } catch (RemoteException e) {
1108 throw e.rethrowFromSystemServer();
1109 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001110 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1111 }
1112
1113 /* This method should only be called from Binder threads. Do not call this from
1114 * within the system server as it will crash the system on failure.
1115 */
Benedict Wong344bd622017-11-16 15:27:22 -08001116 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001117 throws RemoteException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001118
Benedict Wong344bd622017-11-16 15:27:22 -08001119 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold93962f32017-03-07 13:23:36 -08001120 }
1121
1122 /** Release a previously allocated SPI that has been registered with the system server */
1123 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001124 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1125 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1126 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001127 }
1128
1129 /**
1130 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1131 * be unbound.
1132 *
1133 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1134 * a random open port and then bind by number, this function creates a temp socket, binds to a
1135 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1136 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1137 * FileHandle.
1138 *
1139 * <p>The loop in this function handles the inherent race window between un-binding to a port
1140 * and re-binding, during which the system could *technically* hand that port out to someone
1141 * else.
1142 */
Benedict Wongf186d672017-10-10 20:44:28 -07001143 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001144 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1145 try {
1146 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1147 Os.bind(probeSocket, INADDR_ANY, 0);
1148 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1149 Os.close(probeSocket);
1150 Log.v(TAG, "Binding to port " + port);
1151 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongf186d672017-10-10 20:44:28 -07001152 return port;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001153 } catch (ErrnoException e) {
1154 // Someone miraculously claimed the port just after we closed probeSocket.
1155 if (e.errno == OsConstants.EADDRINUSE) {
1156 continue;
1157 }
1158 throw e.rethrowAsIOException();
1159 }
1160 }
1161 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1162 }
Nathan Harold93962f32017-03-07 13:23:36 -08001163
1164 /**
Benedict Wongbabe5d72017-12-03 19:42:36 -08001165 * Functional interface to do traffic tagging of given sockets to UIDs.
1166 *
1167 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1168 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1169 *
1170 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1171 * methods that cannot be easily mocked/tested.
1172 */
1173 @VisibleForTesting
1174 public interface UidFdTagger {
1175 /**
1176 * Sets socket tag to assign all traffic to the provided UID.
1177 *
1178 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1179 * should be accounted to the UID of the unprivileged application.
1180 */
1181 public void tag(FileDescriptor fd, int uid) throws IOException;
1182 }
1183
1184 /**
Nathan Harold93962f32017-03-07 13:23:36 -08001185 * Open a socket via the system server and bind it to the specified port (random if port=0).
1186 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1187 * cache the socket and a record of its owner so that it can and must be freed when no longer
1188 * needed.
1189 */
1190 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001191 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1192 throws RemoteException {
1193 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1194 throw new IllegalArgumentException(
1195 "Specified port number must be a valid non-reserved UDP port");
1196 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001197 checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
1198
Benedict Wongbabe5d72017-12-03 19:42:36 -08001199 int callingUid = Binder.getCallingUid();
1200 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldd8c74292017-12-13 19:16:33 -08001201 final int resourceId = mNextResourceId++;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001202 FileDescriptor sockFd = null;
1203 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001204 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001205 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1206 }
1207
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001208 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Benedict Wongbabe5d72017-12-03 19:42:36 -08001209 mUidFdTagger.tag(sockFd, callingUid);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001210
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001211 // This code is common to both the unspecified and specified port cases
1212 Os.setsockoptInt(
1213 sockFd,
1214 OsConstants.IPPROTO_UDP,
1215 OsConstants.UDP_ENCAP,
1216 OsConstants.UDP_ENCAP_ESPINUDP);
1217
Benedict Wongba8d3132017-12-06 21:56:35 -08001218 mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
1219 if (port != 0) {
1220 Log.v(TAG, "Binding to port " + port);
1221 Os.bind(sockFd, INADDR_ANY, port);
1222 } else {
1223 port = bindToRandomPort(sockFd);
1224 }
1225
Benedict Wong344bd622017-11-16 15:27:22 -08001226 userRecord.mEncapSocketRecords.put(
1227 resourceId,
1228 new RefcountedResource<EncapSocketRecord>(
1229 new EncapSocketRecord(resourceId, sockFd, port), binder));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001230 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
1231 } catch (IOException | ErrnoException e) {
1232 IoUtils.closeQuietly(sockFd);
1233 }
1234 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1235 // The only reasonable condition that would cause that is resource unavailable.
1236 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001237 }
1238
1239 /** close a socket that has been been allocated by and registered with the system server */
1240 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001241 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1242 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1243 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001244 }
Nathan Harold93962f32017-03-07 13:23:36 -08001245
Benedict Wong8149f6e2018-01-18 18:31:45 -08001246 /**
1247 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1248 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1249 * needed.
1250 */
1251 @Override
1252 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold592dadb2018-03-15 18:06:06 -07001253 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1254 String callingPackage) {
1255 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001256 checkNotNull(binder, "Null Binder passed to createTunnelInterface");
1257 checkNotNull(underlyingNetwork, "No underlying network was specified");
1258 checkInetAddress(localAddr);
1259 checkInetAddress(remoteAddr);
1260
1261 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1262 // network (b/72316676).
1263
1264 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1265 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1266 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1267 }
1268
1269 final int resourceId = mNextResourceId++;
1270 final int ikey = reserveNetId();
1271 final int okey = reserveNetId();
1272 String intfName = String.format("%s%d", TUNNEL_INTERFACE_PREFIX, resourceId);
1273
Benedict Wong0fe58a92018-01-19 17:36:02 -08001274 try {
1275 // Calls to netd:
1276 // Create VTI
1277 // Add inbound/outbound global policies
1278 // (use reqid = 0)
1279 mSrvConfig
1280 .getNetdInstance()
1281 .addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001282
Benedict Wongb8ef5412018-01-24 15:31:39 -08001283 for(String wildcardAddr : WILDCARD_ADDRESSES) {
1284 for (int direction : DIRECTIONS) {
1285 int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey;
Benedict Wong0fe58a92018-01-19 17:36:02 -08001286
Benedict Wongb8ef5412018-01-24 15:31:39 -08001287 mSrvConfig
1288 .getNetdInstance()
1289 .ipSecAddSecurityPolicy(
Benedict Wong0fe58a92018-01-19 17:36:02 -08001290 0, // Use 0 for reqId
1291 direction,
Benedict Wongb8ef5412018-01-24 15:31:39 -08001292 wildcardAddr,
1293 wildcardAddr,
Benedict Wong0fe58a92018-01-19 17:36:02 -08001294 0,
1295 mark,
1296 0xffffffff);
Benedict Wongb8ef5412018-01-24 15:31:39 -08001297 }
Benedict Wong0fe58a92018-01-19 17:36:02 -08001298 }
1299
1300 userRecord.mTunnelInterfaceRecords.put(
1301 resourceId,
1302 new RefcountedResource<TunnelInterfaceRecord>(
1303 new TunnelInterfaceRecord(
1304 resourceId,
1305 intfName,
1306 underlyingNetwork,
1307 localAddr,
1308 remoteAddr,
1309 ikey,
1310 okey),
1311 binder));
1312 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1313 } catch (RemoteException e) {
1314 // Release keys if we got an error.
1315 releaseNetId(ikey);
1316 releaseNetId(okey);
1317 throw e.rethrowFromSystemServer();
1318 } catch (ServiceSpecificException e) {
1319 // FIXME: get the error code and throw is at an IOException from Errno Exception
1320 }
1321
1322 // If we make it to here, then something has gone wrong and we couldn't create a VTI.
1323 // Release the keys that we reserved, and return an error status.
1324 releaseNetId(ikey);
1325 releaseNetId(okey);
1326 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001327 }
1328
1329 /**
1330 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1331 * from multiple local IP addresses over the same tunnel.
1332 */
1333 @Override
Benedict Wongda4b0c62018-03-01 18:53:07 -08001334 public synchronized void addAddressToTunnelInterface(
Nathan Harold592dadb2018-03-15 18:06:06 -07001335 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
1336 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001337 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1338
1339 // Get tunnelInterface record; if no such interface is found, will throw
1340 // IllegalArgumentException
1341 TunnelInterfaceRecord tunnelInterfaceInfo =
1342 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1343
Benedict Wongda4b0c62018-03-01 18:53:07 -08001344 try {
1345 // We can assume general validity of the IP address, since we get them as a
1346 // LinkAddress, which does some validation.
1347 mSrvConfig
1348 .getNetdInstance()
1349 .interfaceAddAddress(
1350 tunnelInterfaceInfo.mInterfaceName,
1351 localAddr.getAddress().getHostAddress(),
1352 localAddr.getPrefixLength());
1353 } catch (RemoteException e) {
1354 throw e.rethrowFromSystemServer();
1355 } catch (ServiceSpecificException e) {
1356 // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
1357 throw new IllegalArgumentException(e);
1358 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001359 }
1360
1361 /**
1362 * Remove a new local address from the tunnel interface. After removal, the address will no
1363 * longer be available to send from, or receive on.
1364 */
1365 @Override
1366 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold592dadb2018-03-15 18:06:06 -07001367 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
1368 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001369
Nathan Harold592dadb2018-03-15 18:06:06 -07001370 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8149f6e2018-01-18 18:31:45 -08001371 // Get tunnelInterface record; if no such interface is found, will throw
1372 // IllegalArgumentException
1373 TunnelInterfaceRecord tunnelInterfaceInfo =
1374 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1375
Benedict Wongda4b0c62018-03-01 18:53:07 -08001376 try {
1377 // We can assume general validity of the IP address, since we get them as a
1378 // LinkAddress, which does some validation.
1379 mSrvConfig
1380 .getNetdInstance()
1381 .interfaceDelAddress(
1382 tunnelInterfaceInfo.mInterfaceName,
1383 localAddr.getAddress().getHostAddress(),
1384 localAddr.getPrefixLength());
1385 } catch (RemoteException e) {
1386 throw e.rethrowFromSystemServer();
1387 } catch (ServiceSpecificException e) {
1388 // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
1389 throw new IllegalArgumentException(e);
1390 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001391 }
1392
1393 /**
1394 * Delete a TunnelInterface that has been been allocated by and registered with the system
1395 * server
1396 */
1397 @Override
Nathan Harold592dadb2018-03-15 18:06:06 -07001398 public synchronized void deleteTunnelInterface(
1399 int resourceId, String callingPackage) throws RemoteException {
1400 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001401 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1402 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1403 }
1404
Benedict Wong4f255702017-11-06 20:49:10 -08001405 @VisibleForTesting
Nathan Harolda2523312018-01-05 19:25:13 -08001406 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1407 IpSecAlgorithm auth = config.getAuthentication();
1408 IpSecAlgorithm crypt = config.getEncryption();
1409 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong4f255702017-11-06 20:49:10 -08001410
Nathan Harolda2523312018-01-05 19:25:13 -08001411 // Validate the algorithm set
1412 Preconditions.checkArgument(
1413 aead != null || crypt != null || auth != null,
1414 "No Encryption or Authentication algorithms specified");
1415 Preconditions.checkArgument(
1416 auth == null || auth.isAuthentication(),
1417 "Unsupported algorithm for Authentication");
1418 Preconditions.checkArgument(
Benedict Wong4f255702017-11-06 20:49:10 -08001419 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harolda2523312018-01-05 19:25:13 -08001420 Preconditions.checkArgument(
1421 aead == null || aead.isAead(),
1422 "Unsupported algorithm for Authenticated Encryption");
1423 Preconditions.checkArgument(
1424 aead == null || (auth == null && crypt == null),
1425 "Authenticated Encryption is mutually exclusive with other Authentication "
1426 + "or Encryption algorithms");
Benedict Wong4f255702017-11-06 20:49:10 -08001427 }
1428
Nathan Harold93962f32017-03-07 13:23:36 -08001429 /**
Nathan Harolda10003d2017-08-23 13:46:33 -07001430 * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
1431 * IllegalArgumentException if they are not.
1432 */
1433 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong344bd622017-11-16 15:27:22 -08001434 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1435
Nathan Harolda10003d2017-08-23 13:46:33 -07001436 switch (config.getEncapType()) {
1437 case IpSecTransform.ENCAP_NONE:
1438 break;
1439 case IpSecTransform.ENCAP_ESPINUDP:
1440 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong344bd622017-11-16 15:27:22 -08001441 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1442 userRecord.mEncapSocketRecords.getResourceOrThrow(
1443 config.getEncapSocketResourceId());
Nathan Harolda10003d2017-08-23 13:46:33 -07001444
1445 int port = config.getEncapRemotePort();
1446 if (port <= 0 || port > 0xFFFF) {
1447 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1448 }
1449 break;
1450 default:
1451 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1452 }
1453
Nathan Harolda2523312018-01-05 19:25:13 -08001454 validateAlgorithms(config);
Nathan Harolda10003d2017-08-23 13:46:33 -07001455
Nathan Harolda2523312018-01-05 19:25:13 -08001456 // Retrieve SPI record; will throw IllegalArgumentException if not found
1457 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1458
Benedict Wonge6b42772017-12-13 18:26:40 -08001459 // Check to ensure that SPI has not already been used.
1460 if (s.getOwnedByTransform()) {
1461 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1462 }
1463
Nathan Harolda2523312018-01-05 19:25:13 -08001464 // If no remote address is supplied, then use one from the SPI.
1465 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1466 config.setDestinationAddress(s.getDestinationAddress());
1467 }
1468
1469 // All remote addresses must match
1470 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1471 throw new IllegalArgumentException("Mismatched remote addresseses.");
1472 }
1473
1474 // This check is technically redundant due to the chain of custody between the SPI and
1475 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1476 // the transform, this will prevent us from messing up.
1477 checkInetAddress(config.getDestinationAddress());
1478
1479 // Require a valid source address for all transforms.
1480 checkInetAddress(config.getSourceAddress());
1481
1482 switch (config.getMode()) {
1483 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold5a920ca2018-02-02 18:34:25 -08001484 break;
Nathan Harolda2523312018-01-05 19:25:13 -08001485 case IpSecTransform.MODE_TUNNEL:
1486 break;
1487 default:
1488 throw new IllegalArgumentException(
1489 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harolda10003d2017-08-23 13:46:33 -07001490 }
1491 }
1492
Nathan Harold592dadb2018-03-15 18:06:06 -07001493 private void enforceTunnelPermissions(String callingPackage) {
1494 checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
1495 switch (getAppOpsManager().noteOp(
1496 AppOpsManager.OP_MANAGE_IPSEC_TUNNELS,
1497 Binder.getCallingUid(), callingPackage)) {
1498 case AppOpsManager.MODE_DEFAULT:
1499 mContext.enforceCallingOrSelfPermission(
1500 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
1501 break;
1502 case AppOpsManager.MODE_ALLOWED:
1503 return;
1504 default:
1505 throw new SecurityException("Request to ignore AppOps for non-legacy API");
1506 }
Nathan Harold5a920ca2018-02-02 18:34:25 -08001507 }
1508
Benedict Wong0fe58a92018-01-19 17:36:02 -08001509 private void createOrUpdateTransform(
1510 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1511 throws RemoteException {
1512
1513 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1514 if (encapType != IpSecTransform.ENCAP_NONE) {
1515 encapLocalPort = socketRecord.getPort();
1516 encapRemotePort = c.getEncapRemotePort();
1517 }
1518
1519 IpSecAlgorithm auth = c.getAuthentication();
1520 IpSecAlgorithm crypt = c.getEncryption();
1521 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1522
Benedict Wong9be845c2018-03-15 19:41:41 -07001523 String cryptName;
1524 if (crypt == null) {
1525 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1526 } else {
1527 cryptName = crypt.getName();
1528 }
1529
Benedict Wong0fe58a92018-01-19 17:36:02 -08001530 mSrvConfig
1531 .getNetdInstance()
1532 .ipSecAddSecurityAssociation(
1533 resourceId,
1534 c.getMode(),
1535 c.getSourceAddress(),
1536 c.getDestinationAddress(),
1537 (c.getNetwork() != null) ? c.getNetwork().netId : 0,
1538 spiRecord.getSpi(),
1539 c.getMarkValue(),
1540 c.getMarkMask(),
1541 (auth != null) ? auth.getName() : "",
1542 (auth != null) ? auth.getKey() : new byte[] {},
1543 (auth != null) ? auth.getTruncationLengthBits() : 0,
Benedict Wong9be845c2018-03-15 19:41:41 -07001544 cryptName,
Benedict Wong0fe58a92018-01-19 17:36:02 -08001545 (crypt != null) ? crypt.getKey() : new byte[] {},
1546 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1547 (authCrypt != null) ? authCrypt.getName() : "",
1548 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1549 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1550 encapType,
1551 encapLocalPort,
1552 encapRemotePort);
1553 }
1554
Nathan Harolda10003d2017-08-23 13:46:33 -07001555 /**
Benedict Wong0fe58a92018-01-19 17:36:02 -08001556 * Create a IPsec transform, which represents a single security association in the kernel. The
1557 * transform will be cached by the system server and must be freed when no longer needed. It is
1558 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1559 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold93962f32017-03-07 13:23:36 -08001560 */
1561 @Override
Nathan Harold592dadb2018-03-15 18:06:06 -07001562 public synchronized IpSecTransformResponse createTransform(
1563 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
1564 checkNotNull(c);
1565 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
1566 enforceTunnelPermissions(callingPackage);
1567 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001568 checkIpSecConfig(c);
Benedict Wongf33f03132018-01-18 14:38:16 -08001569 checkNotNull(binder, "Null Binder passed to createTransform");
Nathan Haroldd8c74292017-12-13 19:16:33 -08001570 final int resourceId = mNextResourceId++;
Benedict Wong344bd622017-11-16 15:27:22 -08001571
1572 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong4f9fb812017-12-13 17:16:53 -08001573 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong344bd622017-11-16 15:27:22 -08001574
1575 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001576 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1577 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001578
Benedict Wong344bd622017-11-16 15:27:22 -08001579 EncapSocketRecord socketRecord = null;
Benedict Wong0fe58a92018-01-19 17:36:02 -08001580 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong344bd622017-11-16 15:27:22 -08001581 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1582 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1583 c.getEncapSocketResourceId());
1584 dependencies.add(refcountedSocketRecord);
Benedict Wong344bd622017-11-16 15:27:22 -08001585 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001586 }
1587
Nathan Harolda2523312018-01-05 19:25:13 -08001588 RefcountedResource<SpiRecord> refcountedSpiRecord =
1589 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1590 dependencies.add(refcountedSpiRecord);
1591 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong344bd622017-11-16 15:27:22 -08001592
Nathan Harolda2523312018-01-05 19:25:13 -08001593 try {
Benedict Wong0fe58a92018-01-19 17:36:02 -08001594 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Nathan Harolda2523312018-01-05 19:25:13 -08001595 } catch (ServiceSpecificException e) {
1596 // FIXME: get the error code and throw is at an IOException from Errno Exception
1597 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001598 }
Benedict Wong0fe58a92018-01-19 17:36:02 -08001599
1600 // SA was created successfully, time to construct a record and lock it away
Benedict Wong344bd622017-11-16 15:27:22 -08001601 userRecord.mTransformRecords.put(
1602 resourceId,
1603 new RefcountedResource<TransformRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001604 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong344bd622017-11-16 15:27:22 -08001605 binder,
1606 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001607 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001608 }
1609
1610 /**
1611 * Delete a transport mode transform that was previously allocated by + registered with the
1612 * system server. If this is called on an inactive (or non-existent) transform, it will not
1613 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1614 * other reasons.
1615 */
1616 @Override
Benedict Wongf33f03132018-01-18 14:38:16 -08001617 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001618 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1619 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001620 }
1621
1622 /**
1623 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1624 * association as a correspondent policy to the provided socket
1625 */
1626 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001627 public synchronized void applyTransportModeTransform(
Nathan Harolda2523312018-01-05 19:25:13 -08001628 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001629 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Nathan Harolda2523312018-01-05 19:25:13 -08001630 checkDirection(direction);
Benedict Wong344bd622017-11-16 15:27:22 -08001631 // Get transform record; if no transform is found, will throw IllegalArgumentException
1632 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001633
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001634 // TODO: make this a function.
1635 if (info.pid != getCallingPid() || info.uid != getCallingUid()) {
1636 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1637 }
1638
Benedict Wong8149f6e2018-01-18 18:31:45 -08001639 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001640 IpSecConfig c = info.getConfig();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001641 Preconditions.checkArgument(
1642 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1643 "Transform mode was not Transport mode; cannot be applied to a socket");
1644
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001645 try {
Nathan Harolda2523312018-01-05 19:25:13 -08001646 mSrvConfig
1647 .getNetdInstance()
1648 .ipSecApplyTransportModeTransform(
1649 socket.getFileDescriptor(),
1650 resourceId,
1651 direction,
1652 c.getSourceAddress(),
1653 c.getDestinationAddress(),
1654 info.getSpiRecord().getSpi());
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001655 } catch (ServiceSpecificException e) {
manojboopathic4be79d2017-11-30 17:11:49 -08001656 if (e.errorCode == EINVAL) {
1657 throw new IllegalArgumentException(e.toString());
1658 } else {
1659 throw e;
1660 }
Nathan Harold93962f32017-03-07 13:23:36 -08001661 }
1662 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001663
Nathan Harold93962f32017-03-07 13:23:36 -08001664 /**
Nathan Harolda2523312018-01-05 19:25:13 -08001665 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1666 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1667 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1668 * reserved for future improved input validation.
Nathan Harold93962f32017-03-07 13:23:36 -08001669 */
1670 @Override
Nathan Haroldf73d2522018-01-17 01:00:20 -08001671 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1672 throws RemoteException {
Nathan Harold93962f32017-03-07 13:23:36 -08001673 try {
ludi1a06aa72017-05-12 09:15:00 -07001674 mSrvConfig
1675 .getNetdInstance()
1676 .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
Nathan Harold93962f32017-03-07 13:23:36 -08001677 } catch (ServiceSpecificException e) {
1678 // FIXME: get the error code and throw is at an IOException from Errno Exception
1679 }
1680 }
1681
Benedict Wong8149f6e2018-01-18 18:31:45 -08001682 /**
1683 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1684 * security association as a correspondent policy to the provided interface
1685 */
1686 @Override
1687 public synchronized void applyTunnelModeTransform(
Nathan Harold592dadb2018-03-15 18:06:06 -07001688 int tunnelResourceId, int direction,
1689 int transformResourceId, String callingPackage) throws RemoteException {
1690 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001691 checkDirection(direction);
1692
1693 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1694
1695 // Get transform record; if no transform is found, will throw IllegalArgumentException
1696 TransformRecord transformInfo =
1697 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1698
1699 // Get tunnelInterface record; if no such interface is found, will throw
1700 // IllegalArgumentException
1701 TunnelInterfaceRecord tunnelInterfaceInfo =
1702 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1703
1704 // Get config and check that to-be-applied transform has the correct mode
1705 IpSecConfig c = transformInfo.getConfig();
1706 Preconditions.checkArgument(
1707 c.getMode() == IpSecTransform.MODE_TUNNEL,
1708 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1709
Benedict Wong0fe58a92018-01-19 17:36:02 -08001710 EncapSocketRecord socketRecord = null;
1711 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1712 socketRecord =
1713 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1714 }
1715 SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId());
1716
Benedict Wong8149f6e2018-01-18 18:31:45 -08001717 int mark =
1718 (direction == IpSecManager.DIRECTION_IN)
1719 ? tunnelInterfaceInfo.getIkey()
1720 : tunnelInterfaceInfo.getOkey();
1721
Benedict Wong0fe58a92018-01-19 17:36:02 -08001722 try {
1723 c.setMarkValue(mark);
1724 c.setMarkMask(0xffffffff);
1725
1726 if (direction == IpSecManager.DIRECTION_OUT) {
1727 // Set output mark via underlying network (output only)
1728 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1729
1730 // If outbound, also add SPI to the policy.
Benedict Wongb8ef5412018-01-24 15:31:39 -08001731 for(String wildcardAddr : WILDCARD_ADDRESSES) {
1732 mSrvConfig
1733 .getNetdInstance()
1734 .ipSecUpdateSecurityPolicy(
1735 0, // Use 0 for reqId
1736 direction,
1737 wildcardAddr,
1738 wildcardAddr,
1739 transformInfo.getSpiRecord().getSpi(),
1740 mark,
1741 0xffffffff);
1742 }
Benedict Wong0fe58a92018-01-19 17:36:02 -08001743 }
1744
1745 // Update SA with tunnel mark (ikey or okey based on direction)
1746 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1747 } catch (ServiceSpecificException e) {
1748 if (e.errorCode == EINVAL) {
1749 throw new IllegalArgumentException(e.toString());
1750 } else {
1751 throw e;
1752 }
1753 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001754 }
1755
Nathan Harold93962f32017-03-07 13:23:36 -08001756 @Override
ludib0c95b12017-05-22 10:52:23 -07001757 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001758 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludib0c95b12017-05-22 10:52:23 -07001759
1760 pw.println("IpSecService dump:");
Nathan Harold1afbef42017-03-01 18:55:06 -08001761 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1762 pw.println();
ludib0c95b12017-05-22 10:52:23 -07001763
Benedict Wong344bd622017-11-16 15:27:22 -08001764 pw.println("mUserResourceTracker:");
1765 pw.println(mUserResourceTracker);
Nathan Harold1afbef42017-03-01 18:55:06 -08001766 }
1767}