blob: 8c25917c7436255d833261a9e3d2f10c748525a3 [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;
evitayane4259d32018-03-22 17:53:08 -070022import static android.system.OsConstants.AF_INET6;
23import static android.system.OsConstants.AF_UNSPEC;
manojboopathic4be79d2017-11-30 17:11:49 -080024import static android.system.OsConstants.EINVAL;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070025import static android.system.OsConstants.IPPROTO_UDP;
26import static android.system.OsConstants.SOCK_DGRAM;
27import static com.android.internal.util.Preconditions.checkNotNull;
Nathan Harold1afbef42017-03-01 18:55:06 -080028
Nathan Harold21208ee2018-03-15 18:06:06 -070029import android.annotation.NonNull;
30import android.app.AppOpsManager;
Nathan Harold1afbef42017-03-01 18:55:06 -080031import android.content.Context;
32import android.net.IIpSecService;
33import android.net.INetd;
Nathan Harold93962f32017-03-07 13:23:36 -080034import android.net.IpSecAlgorithm;
35import android.net.IpSecConfig;
36import android.net.IpSecManager;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070037import android.net.IpSecSpiResponse;
Nathan Harold93962f32017-03-07 13:23:36 -080038import android.net.IpSecTransform;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070039import android.net.IpSecTransformResponse;
Benedict Wong8149f6e2018-01-18 18:31:45 -080040import android.net.IpSecTunnelInterfaceResponse;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070041import android.net.IpSecUdpEncapResponse;
Benedict Wongecc9f7c2018-03-01 18:53:07 -080042import android.net.LinkAddress;
Benedict Wong8149f6e2018-01-18 18:31:45 -080043import android.net.Network;
Nathan Harolda10003d2017-08-23 13:46:33 -070044import android.net.NetworkUtils;
Benedict Wongbabe5d72017-12-03 19:42:36 -080045import android.net.TrafficStats;
Nathan Harold1afbef42017-03-01 18:55:06 -080046import android.net.util.NetdService;
Nathan Harold93962f32017-03-07 13:23:36 -080047import android.os.Binder;
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;
evitayane4259d32018-03-22 17:53:08 -070068import java.net.Inet4Address;
69import java.net.Inet6Address;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070070import java.net.InetAddress;
71import java.net.InetSocketAddress;
72import java.net.UnknownHostException;
Benedict Wong409c8ca2017-10-26 19:41:43 -070073import java.util.ArrayList;
74import java.util.List;
Nathan Harolda10003d2017-08-23 13:46:33 -070075
Nathan Harold8dc1fd02017-04-04 19:37:48 -070076import libcore.io.IoUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080077
Benedict Wong409c8ca2017-10-26 19:41:43 -070078/**
79 * A service to manage multiple clients that want to access the IpSec API. The service is
80 * responsible for maintaining a list of clients and managing the resources (and related quotas)
81 * that each of them own.
82 *
83 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
84 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
85 * thread is ever running at a time.
86 *
87 * @hide
88 */
Nathan Harold1afbef42017-03-01 18:55:06 -080089public class IpSecService extends IIpSecService.Stub {
90 private static final String TAG = "IpSecService";
91 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Nathan Harold8dc1fd02017-04-04 19:37:48 -070092
Nathan Harold1afbef42017-03-01 18:55:06 -080093 private static final String NETD_SERVICE_NAME = "netd";
Benedict Wong564e2aa2018-05-07 20:06:44 -070094 private static final int[] ADDRESS_FAMILIES =
95 new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
Nathan Harold1afbef42017-03-01 18:55:06 -080096
ludi1a06aa72017-05-12 09:15:00 -070097 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold8dc1fd02017-04-04 19:37:48 -070098 private static final int MAX_PORT_BIND_ATTEMPTS = 10;
99 private static final InetAddress INADDR_ANY;
100
101 static {
102 try {
103 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
104 } catch (UnknownHostException e) {
105 throw new RuntimeException(e);
106 }
107 }
108
109 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
110 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
111
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(
Benedict Wonga0989622018-07-25 13:06:29 -0700619 uid,
Nathan Harolda2523312018-01-05 19:25:13 -0800620 mConfig.getSourceAddress(),
621 mConfig.getDestinationAddress(),
Di Lu0b611f42018-01-11 11:35:25 -0800622 spi,
623 mConfig.getMarkValue(),
624 mConfig.getMarkMask());
Benedict Wongecc9f7c2018-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 Harold22795302018-02-27 19:19:40 -0800682 if (!mOwnedByTransform) {
683 mSrvConfig
684 .getNetdInstance()
685 .ipSecDeleteSecurityAssociation(
Benedict Wonga0989622018-07-25 13:06:29 -0700686 uid, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
Nathan Harold22795302018-02-27 19:19:40 -0800687 }
Benedict Wongecc9f7c2018-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 {
Benedict Wong564e2aa2018-05-07 20:06:44 -0700823 final INetd netd = mSrvConfig.getNetdInstance();
824 netd.removeVirtualTunnelInterface(mInterfaceName);
Benedict Wong0fe58a92018-01-19 17:36:02 -0800825
Benedict Wong564e2aa2018-05-07 20:06:44 -0700826 for (int selAddrFamily : ADDRESS_FAMILIES) {
827 netd.ipSecDeleteSecurityPolicy(
Benedict Wonga0989622018-07-25 13:06:29 -0700828 uid,
Benedict Wong564e2aa2018-05-07 20:06:44 -0700829 selAddrFamily,
830 IpSecManager.DIRECTION_OUT,
831 mOkey,
832 0xffffffff);
833 netd.ipSecDeleteSecurityPolicy(
Benedict Wonga0989622018-07-25 13:06:29 -0700834 uid,
Benedict Wong564e2aa2018-05-07 20:06:44 -0700835 selAddrFamily,
836 IpSecManager.DIRECTION_IN,
837 mIkey,
838 0xffffffff);
Benedict Wong0fe58a92018-01-19 17:36:02 -0800839 }
Benedict Wongecc9f7c2018-03-01 18:53:07 -0800840 } catch (ServiceSpecificException | RemoteException e) {
Benedict Wong0fe58a92018-01-19 17:36:02 -0800841 Log.e(
842 TAG,
843 "Failed to delete VTI with interface name: "
844 + mInterfaceName
845 + " and id: "
Benedict Wongecc9f7c2018-03-01 18:53:07 -0800846 + mResourceId, e);
Benedict Wong0fe58a92018-01-19 17:36:02 -0800847 }
Benedict Wong8149f6e2018-01-18 18:31:45 -0800848
849 getResourceTracker().give();
850 releaseNetId(mIkey);
851 releaseNetId(mOkey);
852 }
853
854 public String getInterfaceName() {
855 return mInterfaceName;
856 }
857
858 public Network getUnderlyingNetwork() {
859 return mUnderlyingNetwork;
860 }
861
862 /** Returns the local, outer address for the tunnelInterface */
863 public String getLocalAddress() {
864 return mLocalAddress;
865 }
866
867 /** Returns the remote, outer address for the tunnelInterface */
868 public String getRemoteAddress() {
869 return mRemoteAddress;
870 }
871
872 public int getIkey() {
873 return mIkey;
874 }
875
876 public int getOkey() {
877 return mOkey;
878 }
879
880 @Override
881 protected ResourceTracker getResourceTracker() {
882 return getUserRecord().mTunnelQuotaTracker;
883 }
884
885 @Override
886 public void invalidate() {
887 getUserRecord().removeTunnelInterfaceRecord(mResourceId);
888 }
889
890 @Override
891 public String toString() {
892 return new StringBuilder()
893 .append("{super=")
894 .append(super.toString())
895 .append(", mInterfaceName=")
896 .append(mInterfaceName)
897 .append(", mUnderlyingNetwork=")
898 .append(mUnderlyingNetwork)
899 .append(", mLocalAddress=")
900 .append(mLocalAddress)
901 .append(", mRemoteAddress=")
902 .append(mRemoteAddress)
903 .append(", mIkey=")
904 .append(mIkey)
905 .append(", mOkey=")
906 .append(mOkey)
907 .append("}")
908 .toString();
909 }
910 }
911
Benedict Wong4f9fb812017-12-13 17:16:53 -0800912 /**
913 * Tracks a UDP encap socket, and manages cleanup paths
914 *
915 * <p>While this class does not manage non-kernel resources, race conditions around socket
916 * binding require that the service creates the encap socket, binds it and applies the socket
917 * policy before handing it to a user.
918 */
919 private final class EncapSocketRecord extends OwnedResourceRecord {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700920 private FileDescriptor mSocket;
921 private final int mPort;
Nathan Harold93962f32017-03-07 13:23:36 -0800922
Benedict Wong344bd622017-11-16 15:27:22 -0800923 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
924 super(resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700925 mSocket = socket;
926 mPort = port;
927 }
928
929 /** always guarded by IpSecService#this */
930 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800931 public void freeUnderlyingResources() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700932 Log.d(TAG, "Closing port " + mPort);
933 IoUtils.closeQuietly(mSocket);
934 mSocket = null;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700935
Benedict Wong344bd622017-11-16 15:27:22 -0800936 getResourceTracker().give();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700937 }
938
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700939 public int getPort() {
940 return mPort;
941 }
942
Benedict Wong4c987eb2018-03-27 16:55:48 -0700943 public FileDescriptor getFileDescriptor() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700944 return mSocket;
945 }
ludib0c95b12017-05-22 10:52:23 -0700946
947 @Override
Benedict Wong344bd622017-11-16 15:27:22 -0800948 protected ResourceTracker getResourceTracker() {
949 return getUserRecord().mSocketQuotaTracker;
950 }
951
952 @Override
953 public void invalidate() {
954 getUserRecord().removeEncapSocketRecord(mResourceId);
955 }
956
957 @Override
ludib0c95b12017-05-22 10:52:23 -0700958 public String toString() {
959 return new StringBuilder()
960 .append("{super=")
961 .append(super.toString())
962 .append(", mSocket=")
963 .append(mSocket)
964 .append(", mPort=")
965 .append(mPort)
966 .append("}")
967 .toString();
968 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700969 }
Nathan Harold93962f32017-03-07 13:23:36 -0800970
Nathan Harold1afbef42017-03-01 18:55:06 -0800971 /**
972 * Constructs a new IpSecService instance
973 *
974 * @param context Binder context for this service
975 */
976 private IpSecService(Context context) {
ludi1a06aa72017-05-12 09:15:00 -0700977 this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
Nathan Harold1afbef42017-03-01 18:55:06 -0800978 }
979
980 static IpSecService create(Context context) throws InterruptedException {
981 final IpSecService service = new IpSecService(context);
982 service.connectNativeNetdService();
983 return service;
984 }
985
Nathan Harold21208ee2018-03-15 18:06:06 -0700986 @NonNull
987 private AppOpsManager getAppOpsManager() {
988 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
989 if(appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
990 return appOps;
991 }
992
ludi1a06aa72017-05-12 09:15:00 -0700993 /** @hide */
994 @VisibleForTesting
995 public IpSecService(Context context, IpSecServiceConfiguration config) {
Nathan Harolda2523312018-01-05 19:25:13 -0800996 this(
997 context,
998 config,
999 (fd, uid) -> {
1000 try {
1001 TrafficStats.setThreadStatsUid(uid);
1002 TrafficStats.tagFileDescriptor(fd);
1003 } finally {
1004 TrafficStats.clearThreadStatsUid();
1005 }
1006 });
Benedict Wongbabe5d72017-12-03 19:42:36 -08001007 }
1008
1009 /** @hide */
1010 @VisibleForTesting
1011 public IpSecService(
1012 Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
ludi1a06aa72017-05-12 09:15:00 -07001013 mContext = context;
1014 mSrvConfig = config;
Benedict Wongbabe5d72017-12-03 19:42:36 -08001015 mUidFdTagger = uidFdTagger;
ludi1a06aa72017-05-12 09:15:00 -07001016 }
1017
Nathan Harold1afbef42017-03-01 18:55:06 -08001018 public void systemReady() {
1019 if (isNetdAlive()) {
1020 Slog.d(TAG, "IpSecService is ready");
1021 } else {
1022 Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
1023 }
1024 }
1025
1026 private void connectNativeNetdService() {
1027 // Avoid blocking the system server to do this
Nathan Haroldb0e05082017-07-17 14:01:53 -07001028 new Thread() {
1029 @Override
1030 public void run() {
1031 synchronized (IpSecService.this) {
ludi1a06aa72017-05-12 09:15:00 -07001032 NetdService.get(NETD_FETCH_TIMEOUT_MS);
Nathan Haroldb0e05082017-07-17 14:01:53 -07001033 }
1034 }
1035 }.start();
Nathan Harold1afbef42017-03-01 18:55:06 -08001036 }
1037
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001038 synchronized boolean isNetdAlive() {
1039 try {
ludi1a06aa72017-05-12 09:15:00 -07001040 final INetd netd = mSrvConfig.getNetdInstance();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001041 if (netd == null) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001042 return false;
1043 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001044 return netd.isAlive();
1045 } catch (RemoteException re) {
1046 return false;
Nathan Harold1afbef42017-03-01 18:55:06 -08001047 }
1048 }
1049
Nathan Harolda10003d2017-08-23 13:46:33 -07001050 /**
1051 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1052 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1053 */
1054 private static void checkInetAddress(String inetAddress) {
1055 if (TextUtils.isEmpty(inetAddress)) {
1056 throw new IllegalArgumentException("Unspecified address");
1057 }
1058
1059 InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
1060
1061 if (checkAddr.isAnyLocalAddress()) {
1062 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1063 }
1064 }
1065
1066 /**
1067 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1068 * DIRECTION_IN or DIRECTION_OUT
1069 */
1070 private static void checkDirection(int direction) {
1071 switch (direction) {
Nathan Harolda2523312018-01-05 19:25:13 -08001072 case IpSecManager.DIRECTION_OUT:
1073 case IpSecManager.DIRECTION_IN:
Nathan Harolda10003d2017-08-23 13:46:33 -07001074 return;
1075 }
1076 throw new IllegalArgumentException("Invalid Direction: " + direction);
1077 }
1078
Nathan Harold93962f32017-03-07 13:23:36 -08001079 /** Get a new SPI and maintain the reservation in the system server */
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001080 @Override
1081 public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
Nathan Harolda2523312018-01-05 19:25:13 -08001082 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1083 checkInetAddress(destinationAddress);
Nathan Harold7f606ee2018-03-28 08:52:51 -07001084 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1085 if (requestedSpi > 0 && requestedSpi < 256) {
1086 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1087 }
Jonathan Basseri5fb92902017-11-16 10:58:01 -08001088 checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
Nathan Harolda10003d2017-08-23 13:46:33 -07001089
Benedict Wonga0989622018-07-25 13:06:29 -07001090 int callingUid = Binder.getCallingUid();
1091 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldd8c74292017-12-13 19:16:33 -08001092 final int resourceId = mNextResourceId++;
Nathan Harold93962f32017-03-07 13:23:36 -08001093
1094 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -08001095 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001096 if (!userRecord.mSpiQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001097 return new IpSecSpiResponse(
Nathan Harolda10003d2017-08-23 13:46:33 -07001098 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harolda1afbd82017-04-24 16:16:34 -07001099 }
Nathan Harolda2523312018-01-05 19:25:13 -08001100
Nathan Harold93962f32017-03-07 13:23:36 -08001101 spi =
ludi1a06aa72017-05-12 09:15:00 -07001102 mSrvConfig
1103 .getNetdInstance()
Benedict Wonga0989622018-07-25 13:06:29 -07001104 .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Nathan Harold93962f32017-03-07 13:23:36 -08001105 Log.d(TAG, "Allocated SPI " + spi);
Benedict Wong344bd622017-11-16 15:27:22 -08001106 userRecord.mSpiRecords.put(
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001107 resourceId,
Benedict Wong344bd622017-11-16 15:27:22 -08001108 new RefcountedResource<SpiRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001109 new SpiRecord(resourceId, "", destinationAddress, spi), binder));
Nathan Harold93962f32017-03-07 13:23:36 -08001110 } catch (ServiceSpecificException e) {
Nathan Harold144ce0a2018-04-03 16:13:19 -07001111 if (e.errorCode == OsConstants.ENOENT) {
1112 return new IpSecSpiResponse(
1113 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1114 }
1115 throw e;
Nathan Harold93962f32017-03-07 13:23:36 -08001116 } catch (RemoteException e) {
1117 throw e.rethrowFromSystemServer();
1118 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001119 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1120 }
1121
1122 /* This method should only be called from Binder threads. Do not call this from
1123 * within the system server as it will crash the system on failure.
1124 */
Benedict Wong344bd622017-11-16 15:27:22 -08001125 private void releaseResource(RefcountedResourceArray resArray, int resourceId)
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001126 throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001127 resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
Nathan Harold93962f32017-03-07 13:23:36 -08001128 }
1129
1130 /** Release a previously allocated SPI that has been registered with the system server */
1131 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001132 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1133 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1134 releaseResource(userRecord.mSpiRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001135 }
1136
1137 /**
1138 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1139 * be unbound.
1140 *
1141 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1142 * a random open port and then bind by number, this function creates a temp socket, binds to a
1143 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1144 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1145 * FileHandle.
1146 *
1147 * <p>The loop in this function handles the inherent race window between un-binding to a port
1148 * and re-binding, during which the system could *technically* hand that port out to someone
1149 * else.
1150 */
Benedict Wongf186d672017-10-10 20:44:28 -07001151 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001152 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1153 try {
1154 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1155 Os.bind(probeSocket, INADDR_ANY, 0);
1156 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1157 Os.close(probeSocket);
1158 Log.v(TAG, "Binding to port " + port);
1159 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongf186d672017-10-10 20:44:28 -07001160 return port;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001161 } catch (ErrnoException e) {
1162 // Someone miraculously claimed the port just after we closed probeSocket.
1163 if (e.errno == OsConstants.EADDRINUSE) {
1164 continue;
1165 }
1166 throw e.rethrowAsIOException();
1167 }
1168 }
1169 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1170 }
Nathan Harold93962f32017-03-07 13:23:36 -08001171
1172 /**
Benedict Wongbabe5d72017-12-03 19:42:36 -08001173 * Functional interface to do traffic tagging of given sockets to UIDs.
1174 *
1175 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1176 * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1177 *
1178 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1179 * methods that cannot be easily mocked/tested.
1180 */
1181 @VisibleForTesting
1182 public interface UidFdTagger {
1183 /**
1184 * Sets socket tag to assign all traffic to the provided UID.
1185 *
1186 * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1187 * should be accounted to the UID of the unprivileged application.
1188 */
1189 public void tag(FileDescriptor fd, int uid) throws IOException;
1190 }
1191
1192 /**
Nathan Harold93962f32017-03-07 13:23:36 -08001193 * Open a socket via the system server and bind it to the specified port (random if port=0).
1194 * This will return a PFD to the user that represent a bound UDP socket. The system server will
1195 * cache the socket and a record of its owner so that it can and must be freed when no longer
1196 * needed.
1197 */
1198 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001199 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1200 throws RemoteException {
1201 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1202 throw new IllegalArgumentException(
1203 "Specified port number must be a valid non-reserved UDP port");
1204 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001205 checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
1206
Benedict Wongbabe5d72017-12-03 19:42:36 -08001207 int callingUid = Binder.getCallingUid();
1208 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Haroldd8c74292017-12-13 19:16:33 -08001209 final int resourceId = mNextResourceId++;
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001210 FileDescriptor sockFd = null;
1211 try {
Benedict Wong344bd622017-11-16 15:27:22 -08001212 if (!userRecord.mSocketQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001213 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1214 }
1215
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001216 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Benedict Wongbabe5d72017-12-03 19:42:36 -08001217 mUidFdTagger.tag(sockFd, callingUid);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001218
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001219 // This code is common to both the unspecified and specified port cases
1220 Os.setsockoptInt(
1221 sockFd,
1222 OsConstants.IPPROTO_UDP,
1223 OsConstants.UDP_ENCAP,
1224 OsConstants.UDP_ENCAP_ESPINUDP);
1225
Benedict Wongba8d3132017-12-06 21:56:35 -08001226 mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
1227 if (port != 0) {
1228 Log.v(TAG, "Binding to port " + port);
1229 Os.bind(sockFd, INADDR_ANY, port);
1230 } else {
1231 port = bindToRandomPort(sockFd);
1232 }
1233
Benedict Wong344bd622017-11-16 15:27:22 -08001234 userRecord.mEncapSocketRecords.put(
1235 resourceId,
1236 new RefcountedResource<EncapSocketRecord>(
1237 new EncapSocketRecord(resourceId, sockFd, port), binder));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001238 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
1239 } catch (IOException | ErrnoException e) {
1240 IoUtils.closeQuietly(sockFd);
1241 }
1242 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1243 // The only reasonable condition that would cause that is resource unavailable.
1244 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001245 }
1246
1247 /** close a socket that has been been allocated by and registered with the system server */
1248 @Override
Benedict Wong344bd622017-11-16 15:27:22 -08001249 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1250 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1251 releaseResource(userRecord.mEncapSocketRecords, resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001252 }
Nathan Harold93962f32017-03-07 13:23:36 -08001253
Benedict Wong8149f6e2018-01-18 18:31:45 -08001254 /**
1255 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1256 * tunnel interface and a record of its owner so that it can and must be freed when no longer
1257 * needed.
1258 */
1259 @Override
1260 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
Nathan Harold21208ee2018-03-15 18:06:06 -07001261 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1262 String callingPackage) {
1263 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001264 checkNotNull(binder, "Null Binder passed to createTunnelInterface");
1265 checkNotNull(underlyingNetwork, "No underlying network was specified");
1266 checkInetAddress(localAddr);
1267 checkInetAddress(remoteAddr);
1268
1269 // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1270 // network (b/72316676).
1271
Benedict Wonga0989622018-07-25 13:06:29 -07001272 int callerUid = Binder.getCallingUid();
1273 UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001274 if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1275 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1276 }
1277
1278 final int resourceId = mNextResourceId++;
1279 final int ikey = reserveNetId();
1280 final int okey = reserveNetId();
Nathan Harold4fd7c322018-04-26 11:47:14 -07001281 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001282
Benedict Wong0fe58a92018-01-19 17:36:02 -08001283 try {
1284 // Calls to netd:
1285 // Create VTI
1286 // Add inbound/outbound global policies
1287 // (use reqid = 0)
Benedict Wong564e2aa2018-05-07 20:06:44 -07001288 final INetd netd = mSrvConfig.getNetdInstance();
1289 netd.addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001290
Benedict Wong564e2aa2018-05-07 20:06:44 -07001291 for (int selAddrFamily : ADDRESS_FAMILIES) {
1292 // Always send down correct local/remote addresses for template.
1293 netd.ipSecAddSecurityPolicy(
Benedict Wonga0989622018-07-25 13:06:29 -07001294 callerUid,
Benedict Wong564e2aa2018-05-07 20:06:44 -07001295 selAddrFamily,
1296 IpSecManager.DIRECTION_OUT,
1297 localAddr,
1298 remoteAddr,
1299 0,
1300 okey,
1301 0xffffffff);
1302 netd.ipSecAddSecurityPolicy(
Benedict Wonga0989622018-07-25 13:06:29 -07001303 callerUid,
Benedict Wong564e2aa2018-05-07 20:06:44 -07001304 selAddrFamily,
1305 IpSecManager.DIRECTION_IN,
1306 remoteAddr,
1307 localAddr,
1308 0,
1309 ikey,
1310 0xffffffff);
Benedict Wong0fe58a92018-01-19 17:36:02 -08001311 }
1312
1313 userRecord.mTunnelInterfaceRecords.put(
1314 resourceId,
1315 new RefcountedResource<TunnelInterfaceRecord>(
1316 new TunnelInterfaceRecord(
1317 resourceId,
1318 intfName,
1319 underlyingNetwork,
1320 localAddr,
1321 remoteAddr,
1322 ikey,
1323 okey),
1324 binder));
1325 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1326 } catch (RemoteException e) {
1327 // Release keys if we got an error.
1328 releaseNetId(ikey);
1329 releaseNetId(okey);
1330 throw e.rethrowFromSystemServer();
Nathan Harold144ce0a2018-04-03 16:13:19 -07001331 } catch (Throwable t) {
1332 // Release keys if we got an error.
1333 releaseNetId(ikey);
1334 releaseNetId(okey);
1335 throw t;
Benedict Wong0fe58a92018-01-19 17:36:02 -08001336 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001337 }
1338
1339 /**
1340 * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1341 * from multiple local IP addresses over the same tunnel.
1342 */
1343 @Override
Benedict Wongecc9f7c2018-03-01 18:53:07 -08001344 public synchronized void addAddressToTunnelInterface(
Nathan Harold21208ee2018-03-15 18:06:06 -07001345 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
1346 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001347 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1348
1349 // Get tunnelInterface record; if no such interface is found, will throw
1350 // IllegalArgumentException
1351 TunnelInterfaceRecord tunnelInterfaceInfo =
1352 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1353
Benedict Wongecc9f7c2018-03-01 18:53:07 -08001354 try {
1355 // We can assume general validity of the IP address, since we get them as a
1356 // LinkAddress, which does some validation.
1357 mSrvConfig
1358 .getNetdInstance()
1359 .interfaceAddAddress(
1360 tunnelInterfaceInfo.mInterfaceName,
1361 localAddr.getAddress().getHostAddress(),
1362 localAddr.getPrefixLength());
1363 } catch (RemoteException e) {
1364 throw e.rethrowFromSystemServer();
Benedict Wongecc9f7c2018-03-01 18:53:07 -08001365 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001366 }
1367
1368 /**
1369 * Remove a new local address from the tunnel interface. After removal, the address will no
1370 * longer be available to send from, or receive on.
1371 */
1372 @Override
1373 public synchronized void removeAddressFromTunnelInterface(
Nathan Harold21208ee2018-03-15 18:06:06 -07001374 int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
1375 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001376
Nathan Harold21208ee2018-03-15 18:06:06 -07001377 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong8149f6e2018-01-18 18:31:45 -08001378 // Get tunnelInterface record; if no such interface is found, will throw
1379 // IllegalArgumentException
1380 TunnelInterfaceRecord tunnelInterfaceInfo =
1381 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1382
Benedict Wongecc9f7c2018-03-01 18:53:07 -08001383 try {
1384 // We can assume general validity of the IP address, since we get them as a
1385 // LinkAddress, which does some validation.
1386 mSrvConfig
1387 .getNetdInstance()
1388 .interfaceDelAddress(
1389 tunnelInterfaceInfo.mInterfaceName,
1390 localAddr.getAddress().getHostAddress(),
1391 localAddr.getPrefixLength());
1392 } catch (RemoteException e) {
1393 throw e.rethrowFromSystemServer();
Benedict Wongecc9f7c2018-03-01 18:53:07 -08001394 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001395 }
1396
1397 /**
1398 * Delete a TunnelInterface that has been been allocated by and registered with the system
1399 * server
1400 */
1401 @Override
Nathan Harold21208ee2018-03-15 18:06:06 -07001402 public synchronized void deleteTunnelInterface(
1403 int resourceId, String callingPackage) throws RemoteException {
1404 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001405 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1406 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1407 }
1408
Benedict Wong4f255702017-11-06 20:49:10 -08001409 @VisibleForTesting
Nathan Harolda2523312018-01-05 19:25:13 -08001410 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1411 IpSecAlgorithm auth = config.getAuthentication();
1412 IpSecAlgorithm crypt = config.getEncryption();
1413 IpSecAlgorithm aead = config.getAuthenticatedEncryption();
Benedict Wong4f255702017-11-06 20:49:10 -08001414
Nathan Harolda2523312018-01-05 19:25:13 -08001415 // Validate the algorithm set
1416 Preconditions.checkArgument(
1417 aead != null || crypt != null || auth != null,
1418 "No Encryption or Authentication algorithms specified");
1419 Preconditions.checkArgument(
1420 auth == null || auth.isAuthentication(),
1421 "Unsupported algorithm for Authentication");
1422 Preconditions.checkArgument(
Benedict Wong4f255702017-11-06 20:49:10 -08001423 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
Nathan Harolda2523312018-01-05 19:25:13 -08001424 Preconditions.checkArgument(
1425 aead == null || aead.isAead(),
1426 "Unsupported algorithm for Authenticated Encryption");
1427 Preconditions.checkArgument(
1428 aead == null || (auth == null && crypt == null),
1429 "Authenticated Encryption is mutually exclusive with other Authentication "
1430 + "or Encryption algorithms");
Benedict Wong4f255702017-11-06 20:49:10 -08001431 }
1432
evitayane4259d32018-03-22 17:53:08 -07001433 private int getFamily(String inetAddress) {
1434 int family = AF_UNSPEC;
1435 InetAddress checkAddress = NetworkUtils.numericToInetAddress(inetAddress);
1436 if (checkAddress instanceof Inet4Address) {
1437 family = AF_INET;
1438 } else if (checkAddress instanceof Inet6Address) {
1439 family = AF_INET6;
1440 }
1441 return family;
1442 }
1443
Nathan Harold93962f32017-03-07 13:23:36 -08001444 /**
Nathan Harolda10003d2017-08-23 13:46:33 -07001445 * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
1446 * IllegalArgumentException if they are not.
1447 */
1448 private void checkIpSecConfig(IpSecConfig config) {
Benedict Wong344bd622017-11-16 15:27:22 -08001449 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1450
Nathan Harolda10003d2017-08-23 13:46:33 -07001451 switch (config.getEncapType()) {
1452 case IpSecTransform.ENCAP_NONE:
1453 break;
1454 case IpSecTransform.ENCAP_ESPINUDP:
1455 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Benedict Wong344bd622017-11-16 15:27:22 -08001456 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1457 userRecord.mEncapSocketRecords.getResourceOrThrow(
1458 config.getEncapSocketResourceId());
Nathan Harolda10003d2017-08-23 13:46:33 -07001459
1460 int port = config.getEncapRemotePort();
1461 if (port <= 0 || port > 0xFFFF) {
1462 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1463 }
1464 break;
1465 default:
1466 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1467 }
1468
Nathan Harolda2523312018-01-05 19:25:13 -08001469 validateAlgorithms(config);
Nathan Harolda10003d2017-08-23 13:46:33 -07001470
Nathan Harolda2523312018-01-05 19:25:13 -08001471 // Retrieve SPI record; will throw IllegalArgumentException if not found
1472 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1473
Benedict Wonge6b42772017-12-13 18:26:40 -08001474 // Check to ensure that SPI has not already been used.
1475 if (s.getOwnedByTransform()) {
1476 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1477 }
1478
Nathan Harolda2523312018-01-05 19:25:13 -08001479 // If no remote address is supplied, then use one from the SPI.
1480 if (TextUtils.isEmpty(config.getDestinationAddress())) {
1481 config.setDestinationAddress(s.getDestinationAddress());
1482 }
1483
1484 // All remote addresses must match
1485 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1486 throw new IllegalArgumentException("Mismatched remote addresseses.");
1487 }
1488
1489 // This check is technically redundant due to the chain of custody between the SPI and
1490 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1491 // the transform, this will prevent us from messing up.
1492 checkInetAddress(config.getDestinationAddress());
1493
1494 // Require a valid source address for all transforms.
1495 checkInetAddress(config.getSourceAddress());
1496
evitayane4259d32018-03-22 17:53:08 -07001497 // Check to ensure source and destination have the same address family.
1498 String sourceAddress = config.getSourceAddress();
1499 String destinationAddress = config.getDestinationAddress();
1500 int sourceFamily = getFamily(sourceAddress);
1501 int destinationFamily = getFamily(destinationAddress);
1502 if (sourceFamily != destinationFamily) {
1503 throw new IllegalArgumentException(
1504 "Source address ("
1505 + sourceAddress
1506 + ") and destination address ("
1507 + destinationAddress
1508 + ") have different address families.");
1509 }
1510
1511 // Throw an error if UDP Encapsulation is not used in IPv4.
1512 if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
1513 throw new IllegalArgumentException(
1514 "UDP Encapsulation is not supported for this address family");
1515 }
1516
Nathan Harolda2523312018-01-05 19:25:13 -08001517 switch (config.getMode()) {
1518 case IpSecTransform.MODE_TRANSPORT:
Nathan Harold5a920ca2018-02-02 18:34:25 -08001519 break;
Nathan Harolda2523312018-01-05 19:25:13 -08001520 case IpSecTransform.MODE_TUNNEL:
1521 break;
1522 default:
1523 throw new IllegalArgumentException(
1524 "Invalid IpSecTransform.mode: " + config.getMode());
Nathan Harolda10003d2017-08-23 13:46:33 -07001525 }
1526 }
1527
Benedict Wongd8db9182018-09-13 16:45:12 -07001528 private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
Nathan Harold8c69bcb2018-05-15 19:18:38 -07001529
Nathan Harold21208ee2018-03-15 18:06:06 -07001530 private void enforceTunnelPermissions(String callingPackage) {
1531 checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
Benedict Wongd8db9182018-09-13 16:45:12 -07001532 switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1533 case AppOpsManager.MODE_DEFAULT:
1534 mContext.enforceCallingOrSelfPermission(
1535 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
1536 break;
1537 case AppOpsManager.MODE_ALLOWED:
1538 return;
1539 default:
1540 throw new SecurityException("Request to ignore AppOps for non-legacy API");
Nathan Harold21208ee2018-03-15 18:06:06 -07001541 }
Nathan Harold5a920ca2018-02-02 18:34:25 -08001542 }
1543
Benedict Wong0fe58a92018-01-19 17:36:02 -08001544 private void createOrUpdateTransform(
1545 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1546 throws RemoteException {
1547
1548 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1549 if (encapType != IpSecTransform.ENCAP_NONE) {
1550 encapLocalPort = socketRecord.getPort();
1551 encapRemotePort = c.getEncapRemotePort();
1552 }
1553
1554 IpSecAlgorithm auth = c.getAuthentication();
1555 IpSecAlgorithm crypt = c.getEncryption();
1556 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1557
Benedict Wongbf013a32018-03-15 19:41:41 -07001558 String cryptName;
1559 if (crypt == null) {
1560 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1561 } else {
1562 cryptName = crypt.getName();
1563 }
1564
Benedict Wong0fe58a92018-01-19 17:36:02 -08001565 mSrvConfig
1566 .getNetdInstance()
1567 .ipSecAddSecurityAssociation(
Benedict Wonga0989622018-07-25 13:06:29 -07001568 Binder.getCallingUid(),
Benedict Wong0fe58a92018-01-19 17:36:02 -08001569 c.getMode(),
1570 c.getSourceAddress(),
1571 c.getDestinationAddress(),
1572 (c.getNetwork() != null) ? c.getNetwork().netId : 0,
1573 spiRecord.getSpi(),
1574 c.getMarkValue(),
1575 c.getMarkMask(),
1576 (auth != null) ? auth.getName() : "",
1577 (auth != null) ? auth.getKey() : new byte[] {},
1578 (auth != null) ? auth.getTruncationLengthBits() : 0,
Benedict Wongbf013a32018-03-15 19:41:41 -07001579 cryptName,
Benedict Wong0fe58a92018-01-19 17:36:02 -08001580 (crypt != null) ? crypt.getKey() : new byte[] {},
1581 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1582 (authCrypt != null) ? authCrypt.getName() : "",
1583 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1584 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1585 encapType,
1586 encapLocalPort,
1587 encapRemotePort);
1588 }
1589
Nathan Harolda10003d2017-08-23 13:46:33 -07001590 /**
Benedict Wong0fe58a92018-01-19 17:36:02 -08001591 * Create a IPsec transform, which represents a single security association in the kernel. The
1592 * transform will be cached by the system server and must be freed when no longer needed. It is
1593 * possible to free one, deleting the SA from underneath sockets that are using it, which will
1594 * result in all of those sockets becoming unable to send or receive data.
Nathan Harold93962f32017-03-07 13:23:36 -08001595 */
1596 @Override
Nathan Harold21208ee2018-03-15 18:06:06 -07001597 public synchronized IpSecTransformResponse createTransform(
1598 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
1599 checkNotNull(c);
1600 if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
1601 enforceTunnelPermissions(callingPackage);
1602 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001603 checkIpSecConfig(c);
Benedict Wongf33f03132018-01-18 14:38:16 -08001604 checkNotNull(binder, "Null Binder passed to createTransform");
Nathan Haroldd8c74292017-12-13 19:16:33 -08001605 final int resourceId = mNextResourceId++;
Benedict Wong344bd622017-11-16 15:27:22 -08001606
1607 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
Benedict Wong4f9fb812017-12-13 17:16:53 -08001608 List<RefcountedResource> dependencies = new ArrayList<>();
Benedict Wong344bd622017-11-16 15:27:22 -08001609
1610 if (!userRecord.mTransformQuotaTracker.isAvailable()) {
Nathan Harolda1afbd82017-04-24 16:16:34 -07001611 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1612 }
Nathan Harolda10003d2017-08-23 13:46:33 -07001613
Benedict Wong344bd622017-11-16 15:27:22 -08001614 EncapSocketRecord socketRecord = null;
Benedict Wong0fe58a92018-01-19 17:36:02 -08001615 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
Benedict Wong344bd622017-11-16 15:27:22 -08001616 RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1617 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1618 c.getEncapSocketResourceId());
1619 dependencies.add(refcountedSocketRecord);
Benedict Wong344bd622017-11-16 15:27:22 -08001620 socketRecord = refcountedSocketRecord.getResource();
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001621 }
1622
Nathan Harolda2523312018-01-05 19:25:13 -08001623 RefcountedResource<SpiRecord> refcountedSpiRecord =
1624 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1625 dependencies.add(refcountedSpiRecord);
1626 SpiRecord spiRecord = refcountedSpiRecord.getResource();
Benedict Wong344bd622017-11-16 15:27:22 -08001627
Nathan Harold144ce0a2018-04-03 16:13:19 -07001628 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
Benedict Wong0fe58a92018-01-19 17:36:02 -08001629
1630 // SA was created successfully, time to construct a record and lock it away
Benedict Wong344bd622017-11-16 15:27:22 -08001631 userRecord.mTransformRecords.put(
1632 resourceId,
1633 new RefcountedResource<TransformRecord>(
Nathan Harolda2523312018-01-05 19:25:13 -08001634 new TransformRecord(resourceId, c, spiRecord, socketRecord),
Benedict Wong344bd622017-11-16 15:27:22 -08001635 binder,
1636 dependencies.toArray(new RefcountedResource[dependencies.size()])));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001637 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001638 }
1639
1640 /**
1641 * Delete a transport mode transform that was previously allocated by + registered with the
1642 * system server. If this is called on an inactive (or non-existent) transform, it will not
1643 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1644 * other reasons.
1645 */
1646 @Override
Benedict Wongf33f03132018-01-18 14:38:16 -08001647 public synchronized void deleteTransform(int resourceId) throws RemoteException {
Benedict Wong344bd622017-11-16 15:27:22 -08001648 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1649 releaseResource(userRecord.mTransformRecords, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001650 }
1651
1652 /**
1653 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1654 * association as a correspondent policy to the provided socket
1655 */
1656 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001657 public synchronized void applyTransportModeTransform(
Nathan Harolda2523312018-01-05 19:25:13 -08001658 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
Benedict Wonga0989622018-07-25 13:06:29 -07001659 int callingUid = Binder.getCallingUid();
1660 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Nathan Harolda2523312018-01-05 19:25:13 -08001661 checkDirection(direction);
Benedict Wong344bd622017-11-16 15:27:22 -08001662 // Get transform record; if no transform is found, will throw IllegalArgumentException
1663 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001664
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001665 // TODO: make this a function.
Benedict Wonga0989622018-07-25 13:06:29 -07001666 if (info.pid != getCallingPid() || info.uid != callingUid) {
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001667 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1668 }
1669
Benedict Wong8149f6e2018-01-18 18:31:45 -08001670 // Get config and check that to-be-applied transform has the correct mode
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001671 IpSecConfig c = info.getConfig();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001672 Preconditions.checkArgument(
1673 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1674 "Transform mode was not Transport mode; cannot be applied to a socket");
1675
Nathan Harold144ce0a2018-04-03 16:13:19 -07001676 mSrvConfig
1677 .getNetdInstance()
1678 .ipSecApplyTransportModeTransform(
1679 socket.getFileDescriptor(),
Benedict Wonga0989622018-07-25 13:06:29 -07001680 callingUid,
Nathan Harold144ce0a2018-04-03 16:13:19 -07001681 direction,
1682 c.getSourceAddress(),
1683 c.getDestinationAddress(),
1684 info.getSpiRecord().getSpi());
Nathan Harold93962f32017-03-07 13:23:36 -08001685 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001686
Nathan Harold93962f32017-03-07 13:23:36 -08001687 /**
Nathan Harolda2523312018-01-05 19:25:13 -08001688 * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1689 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1690 * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1691 * reserved for future improved input validation.
Nathan Harold93962f32017-03-07 13:23:36 -08001692 */
1693 @Override
Nathan Haroldf73d2522018-01-17 01:00:20 -08001694 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1695 throws RemoteException {
Nathan Harold144ce0a2018-04-03 16:13:19 -07001696 mSrvConfig
1697 .getNetdInstance()
1698 .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
Nathan Harold93962f32017-03-07 13:23:36 -08001699 }
1700
Benedict Wong8149f6e2018-01-18 18:31:45 -08001701 /**
1702 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1703 * security association as a correspondent policy to the provided interface
1704 */
1705 @Override
1706 public synchronized void applyTunnelModeTransform(
Nathan Harold21208ee2018-03-15 18:06:06 -07001707 int tunnelResourceId, int direction,
1708 int transformResourceId, String callingPackage) throws RemoteException {
1709 enforceTunnelPermissions(callingPackage);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001710 checkDirection(direction);
1711
Benedict Wonga0989622018-07-25 13:06:29 -07001712 int callingUid = Binder.getCallingUid();
1713 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
Benedict Wong8149f6e2018-01-18 18:31:45 -08001714
1715 // Get transform record; if no transform is found, will throw IllegalArgumentException
1716 TransformRecord transformInfo =
1717 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1718
1719 // Get tunnelInterface record; if no such interface is found, will throw
1720 // IllegalArgumentException
1721 TunnelInterfaceRecord tunnelInterfaceInfo =
1722 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1723
1724 // Get config and check that to-be-applied transform has the correct mode
1725 IpSecConfig c = transformInfo.getConfig();
1726 Preconditions.checkArgument(
1727 c.getMode() == IpSecTransform.MODE_TUNNEL,
1728 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1729
Benedict Wong0fe58a92018-01-19 17:36:02 -08001730 EncapSocketRecord socketRecord = null;
1731 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1732 socketRecord =
1733 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1734 }
1735 SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId());
1736
Benedict Wong8149f6e2018-01-18 18:31:45 -08001737 int mark =
Benedict Wong564e2aa2018-05-07 20:06:44 -07001738 (direction == IpSecManager.DIRECTION_OUT)
1739 ? tunnelInterfaceInfo.getOkey()
1740 : tunnelInterfaceInfo.getIkey();
Benedict Wong8149f6e2018-01-18 18:31:45 -08001741
Benedict Wong0fe58a92018-01-19 17:36:02 -08001742 try {
1743 c.setMarkValue(mark);
1744 c.setMarkMask(0xffffffff);
1745
1746 if (direction == IpSecManager.DIRECTION_OUT) {
1747 // Set output mark via underlying network (output only)
1748 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1749
1750 // If outbound, also add SPI to the policy.
Benedict Wong564e2aa2018-05-07 20:06:44 -07001751 for (int selAddrFamily : ADDRESS_FAMILIES) {
Benedict Wongb8ef5412018-01-24 15:31:39 -08001752 mSrvConfig
1753 .getNetdInstance()
1754 .ipSecUpdateSecurityPolicy(
Benedict Wonga0989622018-07-25 13:06:29 -07001755 callingUid,
Benedict Wong564e2aa2018-05-07 20:06:44 -07001756 selAddrFamily,
Benedict Wongb8ef5412018-01-24 15:31:39 -08001757 direction,
Benedict Wong564e2aa2018-05-07 20:06:44 -07001758 tunnelInterfaceInfo.getLocalAddress(),
1759 tunnelInterfaceInfo.getRemoteAddress(),
Benedict Wongb8ef5412018-01-24 15:31:39 -08001760 transformInfo.getSpiRecord().getSpi(),
1761 mark,
1762 0xffffffff);
1763 }
Benedict Wong0fe58a92018-01-19 17:36:02 -08001764 }
1765
1766 // Update SA with tunnel mark (ikey or okey based on direction)
1767 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1768 } catch (ServiceSpecificException e) {
1769 if (e.errorCode == EINVAL) {
1770 throw new IllegalArgumentException(e.toString());
1771 } else {
1772 throw e;
1773 }
1774 }
Benedict Wong8149f6e2018-01-18 18:31:45 -08001775 }
1776
Nathan Harold93962f32017-03-07 13:23:36 -08001777 @Override
ludib0c95b12017-05-22 10:52:23 -07001778 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001779 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludib0c95b12017-05-22 10:52:23 -07001780
1781 pw.println("IpSecService dump:");
Nathan Harold1afbef42017-03-01 18:55:06 -08001782 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1783 pw.println();
ludib0c95b12017-05-22 10:52:23 -07001784
Benedict Wong344bd622017-11-16 15:27:22 -08001785 pw.println("mUserResourceTracker:");
1786 pw.println(mUserResourceTracker);
Nathan Harold1afbef42017-03-01 18:55:06 -08001787 }
1788}