blob: 92db7cc83405ecb04b301dbbb52c8f94492c6f5d [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;
22import static android.system.OsConstants.IPPROTO_UDP;
23import static android.system.OsConstants.SOCK_DGRAM;
24import static com.android.internal.util.Preconditions.checkNotNull;
Nathan Harold1afbef42017-03-01 18:55:06 -080025
26import android.content.Context;
27import android.net.IIpSecService;
28import android.net.INetd;
Nathan Harold93962f32017-03-07 13:23:36 -080029import android.net.IpSecAlgorithm;
30import android.net.IpSecConfig;
31import android.net.IpSecManager;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070032import android.net.IpSecSpiResponse;
Nathan Harold93962f32017-03-07 13:23:36 -080033import android.net.IpSecTransform;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070034import android.net.IpSecTransformResponse;
35import android.net.IpSecUdpEncapResponse;
Nathan Harolda10003d2017-08-23 13:46:33 -070036import android.net.NetworkUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080037import android.net.util.NetdService;
Nathan Harold93962f32017-03-07 13:23:36 -080038import android.os.Binder;
Nathan Harold93962f32017-03-07 13:23:36 -080039import android.os.IBinder;
40import android.os.ParcelFileDescriptor;
Nathan Harold1afbef42017-03-01 18:55:06 -080041import android.os.RemoteException;
Nathan Harold93962f32017-03-07 13:23:36 -080042import android.os.ServiceSpecificException;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070043import android.system.ErrnoException;
44import android.system.Os;
45import android.system.OsConstants;
Nathan Harolda10003d2017-08-23 13:46:33 -070046import android.text.TextUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080047import android.util.Log;
48import android.util.Slog;
Nathan Harold93962f32017-03-07 13:23:36 -080049import android.util.SparseArray;
Nathan Harolda10003d2017-08-23 13:46:33 -070050
Nathan Harold93962f32017-03-07 13:23:36 -080051import com.android.internal.annotations.GuardedBy;
ludi1a06aa72017-05-12 09:15:00 -070052import com.android.internal.annotations.VisibleForTesting;
Nathan Harolda10003d2017-08-23 13:46:33 -070053
Nathan Harold1afbef42017-03-01 18:55:06 -080054import java.io.FileDescriptor;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070055import java.io.IOException;
Nathan Harold1afbef42017-03-01 18:55:06 -080056import java.io.PrintWriter;
Nathan Harold8dc1fd02017-04-04 19:37:48 -070057import java.net.InetAddress;
58import java.net.InetSocketAddress;
59import java.net.UnknownHostException;
Benedict Wong409c8ca2017-10-26 19:41:43 -070060import java.util.ArrayList;
61import java.util.List;
Nathan Harold93962f32017-03-07 13:23:36 -080062import java.util.concurrent.atomic.AtomicInteger;
Nathan Harolda10003d2017-08-23 13:46:33 -070063
Nathan Harold8dc1fd02017-04-04 19:37:48 -070064import libcore.io.IoUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080065
Benedict Wong409c8ca2017-10-26 19:41:43 -070066/**
67 * A service to manage multiple clients that want to access the IpSec API. The service is
68 * responsible for maintaining a list of clients and managing the resources (and related quotas)
69 * that each of them own.
70 *
71 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
72 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
73 * thread is ever running at a time.
74 *
75 * @hide
76 */
Nathan Harold1afbef42017-03-01 18:55:06 -080077public class IpSecService extends IIpSecService.Stub {
78 private static final String TAG = "IpSecService";
79 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Nathan Harold8dc1fd02017-04-04 19:37:48 -070080
Nathan Harold1afbef42017-03-01 18:55:06 -080081 private static final String NETD_SERVICE_NAME = "netd";
Nathan Harold93962f32017-03-07 13:23:36 -080082 private static final int[] DIRECTIONS =
83 new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
Nathan Harold1afbef42017-03-01 18:55:06 -080084
ludi1a06aa72017-05-12 09:15:00 -070085 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold8dc1fd02017-04-04 19:37:48 -070086 private static final int MAX_PORT_BIND_ATTEMPTS = 10;
87 private static final InetAddress INADDR_ANY;
88
89 static {
90 try {
91 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
92 } catch (UnknownHostException e) {
93 throw new RuntimeException(e);
94 }
95 }
96
97 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
98 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
99
100 /* Binder context for this service */
Nathan Harold1afbef42017-03-01 18:55:06 -0800101 private final Context mContext;
102
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700103 /** Should be a never-repeating global ID for resources */
104 private static AtomicInteger mNextResourceId = new AtomicInteger(0x00FADED0);
Nathan Harold1afbef42017-03-01 18:55:06 -0800105
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700106 @GuardedBy("this")
Benedict Wong409c8ca2017-10-26 19:41:43 -0700107 private final KernelResourceArray<SpiRecord> mSpiRecords = new KernelResourceArray<>();
Nathan Harold1afbef42017-03-01 18:55:06 -0800108
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700109 @GuardedBy("this")
Benedict Wong409c8ca2017-10-26 19:41:43 -0700110 private final KernelResourceArray<TransformRecord> mTransformRecords =
111 new KernelResourceArray<>();
Nathan Harold93962f32017-03-07 13:23:36 -0800112
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700113 @GuardedBy("this")
Benedict Wong409c8ca2017-10-26 19:41:43 -0700114 private final KernelResourceArray<UdpSocketRecord> mUdpSocketRecords =
115 new KernelResourceArray<>();
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700116
ludi1a06aa72017-05-12 09:15:00 -0700117 interface IpSecServiceConfiguration {
118 INetd getNetdInstance() throws RemoteException;
119
120 static IpSecServiceConfiguration GETSRVINSTANCE =
121 new IpSecServiceConfiguration() {
122 @Override
123 public INetd getNetdInstance() throws RemoteException {
124 final INetd netd = NetdService.getInstance();
125 if (netd == null) {
126 throw new RemoteException("Failed to Get Netd Instance");
127 }
128 return netd;
129 }
130 };
131 }
132
133 private final IpSecServiceConfiguration mSrvConfig;
134
Benedict Wong409c8ca2017-10-26 19:41:43 -0700135 /**
136 * Interface for user-reference and kernel-resource cleanup.
137 *
138 * <p>This interface must be implemented for a resource to be reference counted.
139 */
140 @VisibleForTesting
141 public interface IResource {
142 /**
143 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
144 * objects dependent on it.
145 *
146 * <p>Implementations of this method are expected to remove references to the IResource
147 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
148 * the resource is considered invalid for user access or allocation or use in other
149 * resources.
150 *
151 * <p>References to the IResource object may be held by other RefcountedResource objects,
152 * and as such, the kernel resources and quota may not be cleaned up.
153 */
154 void invalidate() throws RemoteException;
155
156 /**
157 * Releases underlying resources and related quotas.
158 *
159 * <p>Implementations of this method are expected to remove all system resources that are
160 * tracked by the IResource object. Due to other RefcountedResource objects potentially
161 * having references to the IResource object, releaseKernelResources may not always be
162 * called from releaseIfUnreferencedRecursively().
163 */
164 void freeUnderlyingResources() throws RemoteException;
165 }
166
167 /**
168 * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
169 *
170 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
171 * RefcountedResource object creates an explicit reference that must be freed by calling
172 * userRelease(). Additionally, adding this object as a child of another RefcountedResource
173 * object will add an implicit reference.
174 *
175 * <p>Resources are cleaned up when all references, both implicit and explicit, are released
176 * (ie, when userRelease() is called and when all parents have called releaseReference() on this
177 * object.)
178 */
179 @VisibleForTesting
180 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
181 private final T mResource;
182 private final List<RefcountedResource> mChildren;
183 int mRefCount = 1; // starts at 1 for user's reference.
184 IBinder mBinder;
185
186 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
187 synchronized (IpSecService.this) {
188 this.mResource = resource;
189 this.mChildren = new ArrayList<>(children.length);
190 this.mBinder = binder;
191
192 for (RefcountedResource child : children) {
193 mChildren.add(child);
194 child.mRefCount++;
195 }
196
197 try {
198 mBinder.linkToDeath(this, 0);
199 } catch (RemoteException e) {
200 binderDied();
201 }
202 }
203 }
204
205 /**
206 * If the Binder object dies, this function is called to free the system resources that are
207 * being managed by this record and to subsequently release this record for garbage
208 * collection
209 */
210 @Override
211 public void binderDied() {
212 synchronized (IpSecService.this) {
213 try {
214 userRelease();
215 } catch (Exception e) {
216 Log.e(TAG, "Failed to release resource: " + e);
217 }
218 }
219 }
220
221 public T getResource() {
222 return mResource;
223 }
224
225 /**
226 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
227 * arrays)
228 *
229 * <p>If this method has been previously called, the RefcountedResource's binder field will
230 * be null, and the method will return without performing the cleanup a second time.
231 *
232 * <p>Note that calling this function does not imply that kernel resources will be freed at
233 * this time, or that the related quota will be returned. Such actions will only be
234 * performed upon the reference count reaching zero.
235 */
236 @GuardedBy("IpSecService.this")
237 public void userRelease() throws RemoteException {
238 // Prevent users from putting reference counts into a bad state by calling
239 // userRelease() multiple times.
240 if (mBinder == null) {
241 return;
242 }
243
244 mBinder.unlinkToDeath(this, 0);
245 mBinder = null;
246
247 mResource.invalidate();
248
249 releaseReference();
250 }
251
252 /**
253 * Removes a reference to this resource. If the resultant reference count is zero, the
254 * underlying resources are freed, and references to all child resources are also dropped
255 * recursively (resulting in them freeing their resources and children, etcetera)
256 *
257 * <p>This method also sets the reference count to an invalid value (-1) to signify that it
258 * has been fully released. Any subsequent calls to this method will result in an
259 * IllegalStateException being thrown due to resource already having been previously
260 * released
261 */
262 @VisibleForTesting
263 @GuardedBy("IpSecService.this")
264 public void releaseReference() throws RemoteException {
265 mRefCount--;
266
267 if (mRefCount > 0) {
268 return;
269 } else if (mRefCount < 0) {
270 throw new IllegalStateException(
271 "Invalid operation - resource has already been released.");
272 }
273
274 // Cleanup own resources
275 mResource.freeUnderlyingResources();
276
277 // Cleanup child resources as needed
278 for (RefcountedResource<? extends IResource> child : mChildren) {
279 child.releaseReference();
280 }
281
282 // Enforce that resource cleanup can only be called once
283 // By decrementing the refcount (from 0 to -1), the next call will throw an
284 // IllegalStateException - it has already been released fully.
285 mRefCount--;
286 }
287
288 @Override
289 public String toString() {
290 return new StringBuilder()
291 .append("{mResource=")
292 .append(mResource)
293 .append(", mRefCount=")
294 .append(mRefCount)
295 .append(", mChildren=")
296 .append(mChildren)
297 .append("}")
298 .toString();
299 }
300 }
301
Nathan Harolda1afbd82017-04-24 16:16:34 -0700302 /* Very simple counting class that looks much like a counting semaphore */
303 public static class ResourceTracker {
304 private final int mMax;
305 int mCurrent;
306
307 ResourceTracker(int max) {
308 mMax = max;
309 mCurrent = 0;
310 }
311
312 synchronized boolean isAvailable() {
313 return (mCurrent < mMax);
314 }
315
316 synchronized void take() {
317 if (!isAvailable()) {
318 Log.wtf(TAG, "Too many resources allocated!");
319 }
320 mCurrent++;
321 }
322
323 synchronized void give() {
324 if (mCurrent <= 0) {
325 Log.wtf(TAG, "We've released this resource too many times");
326 }
327 mCurrent--;
328 }
ludi3e5ea232017-08-10 15:44:40 -0700329
330 @Override
331 public String toString() {
332 return new StringBuilder()
333 .append("{mCurrent=")
334 .append(mCurrent)
335 .append(", mMax=")
336 .append(mMax)
337 .append("}")
338 .toString();
339 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700340 }
341
342 private static final class UserQuotaTracker {
343 /* Maximum number of UDP Encap Sockets that a single UID may possess */
344 public static final int MAX_NUM_ENCAP_SOCKETS = 2;
345
346 /* Maximum number of IPsec Transforms that a single UID may possess */
347 public static final int MAX_NUM_TRANSFORMS = 4;
348
349 /* Maximum number of IPsec Transforms that a single UID may possess */
350 public static final int MAX_NUM_SPIS = 8;
351
352 /* Record for one users's IpSecService-managed objects */
353 public static class UserRecord {
354 public final ResourceTracker socket = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
355 public final ResourceTracker transform = new ResourceTracker(MAX_NUM_TRANSFORMS);
356 public final ResourceTracker spi = new ResourceTracker(MAX_NUM_SPIS);
ludi3e5ea232017-08-10 15:44:40 -0700357
358 @Override
359 public String toString() {
360 return new StringBuilder()
361 .append("{socket=")
362 .append(socket)
363 .append(", transform=")
364 .append(transform)
365 .append(", spi=")
366 .append(spi)
367 .append("}")
368 .toString();
369 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700370 }
371
372 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
373
374 /* a never-fail getter so that we can populate the list of UIDs as-needed */
375 public synchronized UserRecord getUserRecord(int uid) {
376 UserRecord r = mUserRecords.get(uid);
377 if (r == null) {
378 r = new UserRecord();
379 mUserRecords.put(uid, r);
380 }
381 return r;
382 }
ludi3e5ea232017-08-10 15:44:40 -0700383
384 @Override
385 public String toString() {
386 return mUserRecords.toString();
387 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700388 }
389
390 private final UserQuotaTracker mUserQuotaTracker = new UserQuotaTracker();
391
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700392 /**
Benedict Wong409c8ca2017-10-26 19:41:43 -0700393 * The KernelResource class provides a facility to cleanly and reliably release system
394 * resources. It relies on two things: an IBinder that allows KernelResource to automatically
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700395 * clean up in the event that the Binder dies and a user-provided resourceId that should
396 * uniquely identify the managed resource. To use this class, the user should implement the
397 * releaseResources() method that is responsible for releasing system resources when invoked.
398 */
Benedict Wong409c8ca2017-10-26 19:41:43 -0700399 private abstract class KernelResource implements IBinder.DeathRecipient {
Nathan Harold93962f32017-03-07 13:23:36 -0800400 final int pid;
401 final int uid;
402 private IBinder mBinder;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700403 protected int mResourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800404
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700405 private AtomicInteger mReferenceCount = new AtomicInteger(0);
406
Benedict Wong409c8ca2017-10-26 19:41:43 -0700407 KernelResource(int resourceId, IBinder binder) {
Nathan Harold93962f32017-03-07 13:23:36 -0800408 super();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700409 if (resourceId == INVALID_RESOURCE_ID) {
410 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
411 }
Nathan Harold93962f32017-03-07 13:23:36 -0800412 mBinder = binder;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700413 mResourceId = resourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800414 pid = Binder.getCallingPid();
415 uid = Binder.getCallingUid();
416
Nathan Harolda1afbd82017-04-24 16:16:34 -0700417 getResourceTracker().take();
Nathan Harold93962f32017-03-07 13:23:36 -0800418 try {
419 mBinder.linkToDeath(this, 0);
420 } catch (RemoteException e) {
421 binderDied();
422 }
423 }
424
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700425 public void addReference() {
426 mReferenceCount.incrementAndGet();
427 }
428
429 public void removeReference() {
430 if (mReferenceCount.decrementAndGet() < 0) {
431 Log.wtf(TAG, "Programming error: negative reference count");
432 }
433 }
434
435 public boolean isReferenced() {
436 return (mReferenceCount.get() > 0);
437 }
438
Nathan Harolda10003d2017-08-23 13:46:33 -0700439 /**
440 * Ensures that the caller is either the owner of this resource or has the system UID and
441 * throws a SecurityException otherwise.
442 */
443 public void checkOwnerOrSystem() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700444 if (uid != Binder.getCallingUid()
445 && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
446 throw new SecurityException("Only the owner may access managed resources!");
447 }
448 }
449
Nathan Harold93962f32017-03-07 13:23:36 -0800450 /**
451 * When this record is no longer needed for managing system resources this function should
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700452 * clean up all system resources and nullify the record. This function shall perform all
453 * necessary cleanup of the resources managed by this record.
454 *
455 * <p>NOTE: this function verifies ownership before allowing resources to be freed.
Nathan Harold93962f32017-03-07 13:23:36 -0800456 */
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700457 public final void release() throws RemoteException {
458 synchronized (IpSecService.this) {
459 if (isReferenced()) {
460 throw new IllegalStateException(
461 "Cannot release a resource that has active references!");
462 }
Nathan Harold93962f32017-03-07 13:23:36 -0800463
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700464 if (mResourceId == INVALID_RESOURCE_ID) {
465 return;
466 }
467
468 releaseResources();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700469 getResourceTracker().give();
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700470 if (mBinder != null) {
471 mBinder.unlinkToDeath(this, 0);
472 }
473 mBinder = null;
474
475 mResourceId = INVALID_RESOURCE_ID;
Nathan Harold93962f32017-03-07 13:23:36 -0800476 }
Nathan Harold93962f32017-03-07 13:23:36 -0800477 }
478
479 /**
480 * If the Binder object dies, this function is called to free the system resources that are
481 * being managed by this record and to subsequently release this record for garbage
482 * collection
483 */
484 public final void binderDied() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700485 try {
486 release();
487 } catch (Exception e) {
488 Log.e(TAG, "Failed to release resource: " + e);
489 }
Nathan Harold93962f32017-03-07 13:23:36 -0800490 }
491
492 /**
Nathan Harold93962f32017-03-07 13:23:36 -0800493 * Implement this method to release all system resources that are being protected by this
494 * record. Once the resources are released, the record should be invalidated and no longer
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700495 * used by calling release(). This should NEVER be called directly.
496 *
497 * <p>Calls to this are always guarded by IpSecService#this
Nathan Harold93962f32017-03-07 13:23:36 -0800498 */
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700499 protected abstract void releaseResources() throws RemoteException;
ludib0c95b12017-05-22 10:52:23 -0700500
Nathan Harolda1afbd82017-04-24 16:16:34 -0700501 /** Get the resource tracker for this resource */
502 protected abstract ResourceTracker getResourceTracker();
503
ludib0c95b12017-05-22 10:52:23 -0700504 @Override
505 public String toString() {
506 return new StringBuilder()
507 .append("{mResourceId=")
508 .append(mResourceId)
509 .append(", pid=")
510 .append(pid)
511 .append(", uid=")
512 .append(uid)
513 .append(", mReferenceCount=")
514 .append(mReferenceCount.get())
515 .append("}")
516 .toString();
517 }
Nathan Harold93962f32017-03-07 13:23:36 -0800518 };
519
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700520 /**
ludi1a06aa72017-05-12 09:15:00 -0700521 * Minimal wrapper around SparseArray that performs ownership validation on element accesses.
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700522 */
Benedict Wong409c8ca2017-10-26 19:41:43 -0700523 private class KernelResourceArray<T extends KernelResource> {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700524 SparseArray<T> mArray = new SparseArray<>();
Nathan Harold93962f32017-03-07 13:23:36 -0800525
Nathan Haroldd6f50b22017-10-04 12:58:55 -0700526 T getAndCheckOwner(int key) {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700527 T val = mArray.get(key);
Nathan Haroldb6d2eff2017-07-06 16:57:51 -0700528 // The value should never be null unless the resource doesn't exist
529 // (since we do not allow null resources to be added).
530 if (val != null) {
Nathan Harolda10003d2017-08-23 13:46:33 -0700531 val.checkOwnerOrSystem();
Nathan Haroldb6d2eff2017-07-06 16:57:51 -0700532 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700533 return val;
534 }
535
536 void put(int key, T obj) {
537 checkNotNull(obj, "Null resources cannot be added");
538 mArray.put(key, obj);
539 }
540
541 void remove(int key) {
542 mArray.remove(key);
543 }
ludib0c95b12017-05-22 10:52:23 -0700544
545 @Override
546 public String toString() {
547 return mArray.toString();
548 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700549 }
550
Benedict Wong409c8ca2017-10-26 19:41:43 -0700551 private final class TransformRecord extends KernelResource {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700552 private final IpSecConfig mConfig;
553 private final SpiRecord[] mSpis;
554 private final UdpSocketRecord mSocket;
555
556 TransformRecord(
557 int resourceId,
558 IBinder binder,
559 IpSecConfig config,
560 SpiRecord[] spis,
561 UdpSocketRecord socket) {
562 super(resourceId, binder);
Nathan Harold93962f32017-03-07 13:23:36 -0800563 mConfig = config;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700564 mSpis = spis;
565 mSocket = socket;
566
567 for (int direction : DIRECTIONS) {
568 mSpis[direction].addReference();
569 mSpis[direction].setOwnedByTransform();
570 }
571
572 if (mSocket != null) {
573 mSocket.addReference();
574 }
Nathan Harold93962f32017-03-07 13:23:36 -0800575 }
576
577 public IpSecConfig getConfig() {
578 return mConfig;
579 }
580
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700581 public SpiRecord getSpiRecord(int direction) {
582 return mSpis[direction];
583 }
584
585 /** always guarded by IpSecService#this */
Nathan Harold93962f32017-03-07 13:23:36 -0800586 @Override
587 protected void releaseResources() {
588 for (int direction : DIRECTIONS) {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700589 int spi = mSpis[direction].getSpi();
Nathan Harold93962f32017-03-07 13:23:36 -0800590 try {
ludi1a06aa72017-05-12 09:15:00 -0700591 mSrvConfig
592 .getNetdInstance()
Nathan Harold93962f32017-03-07 13:23:36 -0800593 .ipSecDeleteSecurityAssociation(
594 mResourceId,
595 direction,
Nathan Harolda10003d2017-08-23 13:46:33 -0700596 mConfig.getLocalAddress(),
597 mConfig.getRemoteAddress(),
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700598 spi);
Nathan Harold93962f32017-03-07 13:23:36 -0800599 } catch (ServiceSpecificException e) {
600 // FIXME: get the error code and throw is at an IOException from Errno Exception
601 } catch (RemoteException e) {
602 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
603 }
604 }
Nathan Harold93962f32017-03-07 13:23:36 -0800605
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700606 for (int direction : DIRECTIONS) {
607 mSpis[direction].removeReference();
608 }
609
610 if (mSocket != null) {
611 mSocket.removeReference();
612 }
Nathan Harold93962f32017-03-07 13:23:36 -0800613 }
ludib0c95b12017-05-22 10:52:23 -0700614
Nathan Harolda1afbd82017-04-24 16:16:34 -0700615 protected ResourceTracker getResourceTracker() {
616 return mUserQuotaTracker.getUserRecord(this.uid).transform;
617 }
618
ludib0c95b12017-05-22 10:52:23 -0700619 @Override
620 public String toString() {
621 StringBuilder strBuilder = new StringBuilder();
622 strBuilder
623 .append("{super=")
624 .append(super.toString())
625 .append(", mSocket=")
626 .append(mSocket)
627 .append(", mSpis[OUT].mResourceId=")
628 .append(mSpis[IpSecTransform.DIRECTION_OUT].mResourceId)
629 .append(", mSpis[IN].mResourceId=")
630 .append(mSpis[IpSecTransform.DIRECTION_IN].mResourceId)
631 .append(", mConfig=")
632 .append(mConfig)
633 .append("}");
634 return strBuilder.toString();
635 }
Nathan Harold93962f32017-03-07 13:23:36 -0800636 }
637
Benedict Wong409c8ca2017-10-26 19:41:43 -0700638 private final class SpiRecord extends KernelResource {
Nathan Harold93962f32017-03-07 13:23:36 -0800639 private final int mDirection;
640 private final String mLocalAddress;
641 private final String mRemoteAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800642 private int mSpi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700643
644 private boolean mOwnedByTransform = false;
Nathan Harold93962f32017-03-07 13:23:36 -0800645
646 SpiRecord(
647 int resourceId,
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700648 IBinder binder,
Nathan Harold93962f32017-03-07 13:23:36 -0800649 int direction,
650 String localAddress,
651 String remoteAddress,
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700652 int spi) {
653 super(resourceId, binder);
Nathan Harold93962f32017-03-07 13:23:36 -0800654 mDirection = direction;
655 mLocalAddress = localAddress;
656 mRemoteAddress = remoteAddress;
657 mSpi = spi;
Nathan Harold93962f32017-03-07 13:23:36 -0800658 }
659
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700660 /** always guarded by IpSecService#this */
661 @Override
Nathan Harold93962f32017-03-07 13:23:36 -0800662 protected void releaseResources() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700663 if (mOwnedByTransform) {
664 Log.d(TAG, "Cannot release Spi " + mSpi + ": Currently locked by a Transform");
665 // Because SPIs are "handed off" to transform, objects, they should never be
666 // freed from the SpiRecord once used in a transform. (They refer to the same SA,
667 // thus ownership and responsibility for freeing these resources passes to the
668 // Transform object). Thus, we should let the user free them without penalty once
669 // they are applied in a Transform object.
670 return;
671 }
672
Nathan Harold93962f32017-03-07 13:23:36 -0800673 try {
ludi1a06aa72017-05-12 09:15:00 -0700674 mSrvConfig
675 .getNetdInstance()
Nathan Harold93962f32017-03-07 13:23:36 -0800676 .ipSecDeleteSecurityAssociation(
677 mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi);
678 } catch (ServiceSpecificException e) {
679 // FIXME: get the error code and throw is at an IOException from Errno Exception
680 } catch (RemoteException e) {
681 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId);
682 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700683
684 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -0800685 }
686
Nathan Harolda1afbd82017-04-24 16:16:34 -0700687 @Override
688 protected ResourceTracker getResourceTracker() {
689 return mUserQuotaTracker.getUserRecord(this.uid).spi;
690 }
691
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700692 public int getSpi() {
693 return mSpi;
694 }
695
696 public void setOwnedByTransform() {
697 if (mOwnedByTransform) {
698 // Programming error
Andreas Gamped6d8e452017-07-11 10:25:09 -0700699 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700700 }
701
702 mOwnedByTransform = true;
Nathan Harold93962f32017-03-07 13:23:36 -0800703 }
ludib0c95b12017-05-22 10:52:23 -0700704
705 @Override
706 public String toString() {
707 StringBuilder strBuilder = new StringBuilder();
708 strBuilder
709 .append("{super=")
710 .append(super.toString())
711 .append(", mSpi=")
712 .append(mSpi)
713 .append(", mDirection=")
714 .append(mDirection)
715 .append(", mLocalAddress=")
716 .append(mLocalAddress)
717 .append(", mRemoteAddress=")
718 .append(mRemoteAddress)
719 .append(", mOwnedByTransform=")
720 .append(mOwnedByTransform)
721 .append("}");
722 return strBuilder.toString();
723 }
Nathan Harold93962f32017-03-07 13:23:36 -0800724 }
725
Benedict Wong409c8ca2017-10-26 19:41:43 -0700726 private final class UdpSocketRecord extends KernelResource {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700727 private FileDescriptor mSocket;
728 private final int mPort;
Nathan Harold93962f32017-03-07 13:23:36 -0800729
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700730 UdpSocketRecord(int resourceId, IBinder binder, FileDescriptor socket, int port) {
731 super(resourceId, binder);
732 mSocket = socket;
733 mPort = port;
734 }
735
736 /** always guarded by IpSecService#this */
737 @Override
738 protected void releaseResources() {
739 Log.d(TAG, "Closing port " + mPort);
740 IoUtils.closeQuietly(mSocket);
741 mSocket = null;
742 }
743
Nathan Harolda1afbd82017-04-24 16:16:34 -0700744 @Override
745 protected ResourceTracker getResourceTracker() {
746 return mUserQuotaTracker.getUserRecord(this.uid).socket;
747 }
748
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700749 public int getPort() {
750 return mPort;
751 }
752
753 public FileDescriptor getSocket() {
754 return mSocket;
755 }
ludib0c95b12017-05-22 10:52:23 -0700756
757 @Override
758 public String toString() {
759 return new StringBuilder()
760 .append("{super=")
761 .append(super.toString())
762 .append(", mSocket=")
763 .append(mSocket)
764 .append(", mPort=")
765 .append(mPort)
766 .append("}")
767 .toString();
768 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700769 }
Nathan Harold93962f32017-03-07 13:23:36 -0800770
Nathan Harold1afbef42017-03-01 18:55:06 -0800771 /**
772 * Constructs a new IpSecService instance
773 *
774 * @param context Binder context for this service
775 */
776 private IpSecService(Context context) {
ludi1a06aa72017-05-12 09:15:00 -0700777 this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
Nathan Harold1afbef42017-03-01 18:55:06 -0800778 }
779
780 static IpSecService create(Context context) throws InterruptedException {
781 final IpSecService service = new IpSecService(context);
782 service.connectNativeNetdService();
783 return service;
784 }
785
ludi1a06aa72017-05-12 09:15:00 -0700786 /** @hide */
787 @VisibleForTesting
788 public IpSecService(Context context, IpSecServiceConfiguration config) {
789 mContext = context;
790 mSrvConfig = config;
791 }
792
Nathan Harold1afbef42017-03-01 18:55:06 -0800793 public void systemReady() {
794 if (isNetdAlive()) {
795 Slog.d(TAG, "IpSecService is ready");
796 } else {
797 Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
798 }
799 }
800
801 private void connectNativeNetdService() {
802 // Avoid blocking the system server to do this
Nathan Haroldb0e05082017-07-17 14:01:53 -0700803 new Thread() {
804 @Override
805 public void run() {
806 synchronized (IpSecService.this) {
ludi1a06aa72017-05-12 09:15:00 -0700807 NetdService.get(NETD_FETCH_TIMEOUT_MS);
Nathan Haroldb0e05082017-07-17 14:01:53 -0700808 }
809 }
810 }.start();
Nathan Harold1afbef42017-03-01 18:55:06 -0800811 }
812
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700813 synchronized boolean isNetdAlive() {
814 try {
ludi1a06aa72017-05-12 09:15:00 -0700815 final INetd netd = mSrvConfig.getNetdInstance();
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700816 if (netd == null) {
Nathan Harold1afbef42017-03-01 18:55:06 -0800817 return false;
818 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700819 return netd.isAlive();
820 } catch (RemoteException re) {
821 return false;
Nathan Harold1afbef42017-03-01 18:55:06 -0800822 }
823 }
824
Nathan Harolda10003d2017-08-23 13:46:33 -0700825 /**
826 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
827 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
828 */
829 private static void checkInetAddress(String inetAddress) {
830 if (TextUtils.isEmpty(inetAddress)) {
831 throw new IllegalArgumentException("Unspecified address");
832 }
833
834 InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
835
836 if (checkAddr.isAnyLocalAddress()) {
837 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
838 }
839 }
840
841 /**
842 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
843 * DIRECTION_IN or DIRECTION_OUT
844 */
845 private static void checkDirection(int direction) {
846 switch (direction) {
847 case IpSecTransform.DIRECTION_OUT:
848 case IpSecTransform.DIRECTION_IN:
849 return;
850 }
851 throw new IllegalArgumentException("Invalid Direction: " + direction);
852 }
853
Nathan Harold1afbef42017-03-01 18:55:06 -0800854 @Override
Nathan Harold93962f32017-03-07 13:23:36 -0800855 /** Get a new SPI and maintain the reservation in the system server */
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700856 public synchronized IpSecSpiResponse reserveSecurityParameterIndex(
Nathan Harold93962f32017-03-07 13:23:36 -0800857 int direction, String remoteAddress, int requestedSpi, IBinder binder)
858 throws RemoteException {
Nathan Harolda10003d2017-08-23 13:46:33 -0700859 checkDirection(direction);
860 checkInetAddress(remoteAddress);
861 /* requestedSpi can be anything in the int range, so no check is needed. */
862 checkNotNull(binder, "Null Binder passed to reserveSecurityParameterIndex");
863
Nathan Harold93962f32017-03-07 13:23:36 -0800864 int resourceId = mNextResourceId.getAndIncrement();
865
866 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
867 String localAddress = "";
Nathan Harolda1afbd82017-04-24 16:16:34 -0700868
Nathan Harold93962f32017-03-07 13:23:36 -0800869 try {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700870 if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).spi.isAvailable()) {
871 return new IpSecSpiResponse(
Nathan Harolda10003d2017-08-23 13:46:33 -0700872 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harolda1afbd82017-04-24 16:16:34 -0700873 }
Nathan Harold93962f32017-03-07 13:23:36 -0800874 spi =
ludi1a06aa72017-05-12 09:15:00 -0700875 mSrvConfig
876 .getNetdInstance()
Nathan Harold93962f32017-03-07 13:23:36 -0800877 .ipSecAllocateSpi(
878 resourceId,
879 direction,
880 localAddress,
881 remoteAddress,
882 requestedSpi);
883 Log.d(TAG, "Allocated SPI " + spi);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700884 mSpiRecords.put(
885 resourceId,
886 new SpiRecord(resourceId, binder, direction, localAddress, remoteAddress, spi));
Nathan Harold93962f32017-03-07 13:23:36 -0800887 } catch (ServiceSpecificException e) {
888 // TODO: Add appropriate checks when other ServiceSpecificException types are supported
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700889 return new IpSecSpiResponse(
Nathan Harolda1afbd82017-04-24 16:16:34 -0700890 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold93962f32017-03-07 13:23:36 -0800891 } catch (RemoteException e) {
892 throw e.rethrowFromSystemServer();
893 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700894 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
895 }
896
897 /* This method should only be called from Binder threads. Do not call this from
898 * within the system server as it will crash the system on failure.
899 */
Benedict Wong409c8ca2017-10-26 19:41:43 -0700900 private synchronized <T extends KernelResource> void releaseKernelResource(
901 KernelResourceArray<T> resArray, int resourceId, String typeName)
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700902 throws RemoteException {
903 // We want to non-destructively get so that we can check credentials before removing
904 // this from the records.
Nathan Haroldd6f50b22017-10-04 12:58:55 -0700905 T record = resArray.getAndCheckOwner(resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700906
907 if (record == null) {
908 throw new IllegalArgumentException(
909 typeName + " " + resourceId + " is not available to be deleted");
910 }
911
912 record.release();
913 resArray.remove(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -0800914 }
915
916 /** Release a previously allocated SPI that has been registered with the system server */
917 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700918 public void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
Benedict Wong409c8ca2017-10-26 19:41:43 -0700919 releaseKernelResource(mSpiRecords, resourceId, "SecurityParameterIndex");
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700920 }
921
922 /**
923 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
924 * be unbound.
925 *
926 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
927 * a random open port and then bind by number, this function creates a temp socket, binds to a
928 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
929 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
930 * FileHandle.
931 *
932 * <p>The loop in this function handles the inherent race window between un-binding to a port
933 * and re-binding, during which the system could *technically* hand that port out to someone
934 * else.
935 */
Benedict Wongf186d672017-10-10 20:44:28 -0700936 private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700937 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
938 try {
939 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
940 Os.bind(probeSocket, INADDR_ANY, 0);
941 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
942 Os.close(probeSocket);
943 Log.v(TAG, "Binding to port " + port);
944 Os.bind(sockFd, INADDR_ANY, port);
Benedict Wongf186d672017-10-10 20:44:28 -0700945 return port;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700946 } catch (ErrnoException e) {
947 // Someone miraculously claimed the port just after we closed probeSocket.
948 if (e.errno == OsConstants.EADDRINUSE) {
949 continue;
950 }
951 throw e.rethrowAsIOException();
952 }
953 }
954 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
955 }
Nathan Harold93962f32017-03-07 13:23:36 -0800956
957 /**
958 * Open a socket via the system server and bind it to the specified port (random if port=0).
959 * This will return a PFD to the user that represent a bound UDP socket. The system server will
960 * cache the socket and a record of its owner so that it can and must be freed when no longer
961 * needed.
962 */
963 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700964 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
965 throws RemoteException {
966 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
967 throw new IllegalArgumentException(
968 "Specified port number must be a valid non-reserved UDP port");
969 }
Nathan Harolda10003d2017-08-23 13:46:33 -0700970 checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
971
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700972 int resourceId = mNextResourceId.getAndIncrement();
973 FileDescriptor sockFd = null;
974 try {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700975 if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).socket.isAvailable()) {
976 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
977 }
978
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700979 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
980
981 if (port != 0) {
982 Log.v(TAG, "Binding to port " + port);
983 Os.bind(sockFd, INADDR_ANY, port);
984 } else {
Benedict Wongf186d672017-10-10 20:44:28 -0700985 port = bindToRandomPort(sockFd);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700986 }
987 // This code is common to both the unspecified and specified port cases
988 Os.setsockoptInt(
989 sockFd,
990 OsConstants.IPPROTO_UDP,
991 OsConstants.UDP_ENCAP,
992 OsConstants.UDP_ENCAP_ESPINUDP);
993
994 mUdpSocketRecords.put(
995 resourceId, new UdpSocketRecord(resourceId, binder, sockFd, port));
996 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
997 } catch (IOException | ErrnoException e) {
998 IoUtils.closeQuietly(sockFd);
999 }
1000 // If we make it to here, then something has gone wrong and we couldn't open a socket.
1001 // The only reasonable condition that would cause that is resource unavailable.
1002 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001003 }
1004
1005 /** close a socket that has been been allocated by and registered with the system server */
1006 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001007 public void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1008
Benedict Wong409c8ca2017-10-26 19:41:43 -07001009 releaseKernelResource(mUdpSocketRecords, resourceId, "UdpEncapsulationSocket");
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001010 }
Nathan Harold93962f32017-03-07 13:23:36 -08001011
1012 /**
Nathan Harolda10003d2017-08-23 13:46:33 -07001013 * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
1014 * IllegalArgumentException if they are not.
1015 */
1016 private void checkIpSecConfig(IpSecConfig config) {
1017 if (config.getLocalAddress() == null) {
1018 throw new IllegalArgumentException("Invalid null Local InetAddress");
1019 }
1020
1021 if (config.getRemoteAddress() == null) {
1022 throw new IllegalArgumentException("Invalid null Remote InetAddress");
1023 }
1024
1025 switch (config.getMode()) {
1026 case IpSecTransform.MODE_TRANSPORT:
1027 if (!config.getLocalAddress().isEmpty()) {
1028 throw new IllegalArgumentException("Non-empty Local Address");
1029 }
1030 // Must be valid, and not a wildcard
1031 checkInetAddress(config.getRemoteAddress());
1032 break;
1033 case IpSecTransform.MODE_TUNNEL:
1034 break;
1035 default:
1036 throw new IllegalArgumentException(
1037 "Invalid IpSecTransform.mode: " + config.getMode());
1038 }
1039
1040 switch (config.getEncapType()) {
1041 case IpSecTransform.ENCAP_NONE:
1042 break;
1043 case IpSecTransform.ENCAP_ESPINUDP:
1044 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Nathan Haroldd6f50b22017-10-04 12:58:55 -07001045 if (mUdpSocketRecords.getAndCheckOwner(
1046 config.getEncapSocketResourceId()) == null) {
Nathan Harolda10003d2017-08-23 13:46:33 -07001047 throw new IllegalStateException(
1048 "No Encapsulation socket for Resource Id: "
1049 + config.getEncapSocketResourceId());
1050 }
1051
1052 int port = config.getEncapRemotePort();
1053 if (port <= 0 || port > 0xFFFF) {
1054 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1055 }
1056 break;
1057 default:
1058 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1059 }
1060
1061 for (int direction : DIRECTIONS) {
1062 IpSecAlgorithm crypt = config.getEncryption(direction);
1063 IpSecAlgorithm auth = config.getAuthentication(direction);
Benedict Wong0febe5e2017-08-22 21:42:33 -07001064 IpSecAlgorithm authenticatedEncryption = config.getAuthenticatedEncryption(direction);
1065 if (authenticatedEncryption == null && crypt == null && auth == null) {
1066 throw new IllegalArgumentException(
1067 "No Encryption or Authentication algorithms specified");
1068 } else if (authenticatedEncryption != null && (auth != null || crypt != null)) {
1069 throw new IllegalArgumentException(
1070 "Authenticated Encryption is mutually"
1071 + " exclusive with other Authentication or Encryption algorithms");
Nathan Harolda10003d2017-08-23 13:46:33 -07001072 }
1073
Nathan Haroldd6f50b22017-10-04 12:58:55 -07001074 if (mSpiRecords.getAndCheckOwner(config.getSpiResourceId(direction)) == null) {
Nathan Harolda10003d2017-08-23 13:46:33 -07001075 throw new IllegalStateException("No SPI for specified Resource Id");
1076 }
1077 }
1078 }
1079
1080 /**
Nathan Harold93962f32017-03-07 13:23:36 -08001081 * Create a transport mode transform, which represent two security associations (one in each
1082 * direction) in the kernel. The transform will be cached by the system server and must be freed
1083 * when no longer needed. It is possible to free one, deleting the SA from underneath sockets
1084 * that are using it, which will result in all of those sockets becoming unable to send or
1085 * receive data.
1086 */
1087 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001088 public synchronized IpSecTransformResponse createTransportModeTransform(
1089 IpSecConfig c, IBinder binder) throws RemoteException {
Nathan Harolda10003d2017-08-23 13:46:33 -07001090 checkIpSecConfig(c);
1091 checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
Nathan Harold93962f32017-03-07 13:23:36 -08001092 int resourceId = mNextResourceId.getAndIncrement();
Nathan Harolda1afbd82017-04-24 16:16:34 -07001093 if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).transform.isAvailable()) {
1094 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1095 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001096 SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
Nathan Harolda10003d2017-08-23 13:46:33 -07001097
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001098 int encapType, encapLocalPort = 0, encapRemotePort = 0;
1099 UdpSocketRecord socketRecord = null;
1100 encapType = c.getEncapType();
1101 if (encapType != IpSecTransform.ENCAP_NONE) {
Nathan Haroldd6f50b22017-10-04 12:58:55 -07001102 socketRecord = mUdpSocketRecords.getAndCheckOwner(c.getEncapSocketResourceId());
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001103 encapLocalPort = socketRecord.getPort();
1104 encapRemotePort = c.getEncapRemotePort();
1105 }
1106
Nathan Harold93962f32017-03-07 13:23:36 -08001107 for (int direction : DIRECTIONS) {
1108 IpSecAlgorithm auth = c.getAuthentication(direction);
1109 IpSecAlgorithm crypt = c.getEncryption(direction);
Benedict Wong0febe5e2017-08-22 21:42:33 -07001110 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption(direction);
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001111
Nathan Haroldd6f50b22017-10-04 12:58:55 -07001112 spis[direction] = mSpiRecords.getAndCheckOwner(c.getSpiResourceId(direction));
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001113 int spi = spis[direction].getSpi();
Nathan Harold93962f32017-03-07 13:23:36 -08001114 try {
Nathan Harolda10003d2017-08-23 13:46:33 -07001115 mSrvConfig
1116 .getNetdInstance()
ludi0f807892017-05-20 14:15:09 -07001117 .ipSecAddSecurityAssociation(
1118 resourceId,
1119 c.getMode(),
1120 direction,
Nathan Harolda10003d2017-08-23 13:46:33 -07001121 c.getLocalAddress(),
1122 c.getRemoteAddress(),
1123 (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0,
ludi0f807892017-05-20 14:15:09 -07001124 spi,
1125 (auth != null) ? auth.getName() : "",
Manoj Boopathi Rajfffa8112017-10-26 11:49:02 -07001126 (auth != null) ? auth.getKey() : new byte[] {},
ludi0f807892017-05-20 14:15:09 -07001127 (auth != null) ? auth.getTruncationLengthBits() : 0,
1128 (crypt != null) ? crypt.getName() : "",
Manoj Boopathi Rajfffa8112017-10-26 11:49:02 -07001129 (crypt != null) ? crypt.getKey() : new byte[] {},
ludi0f807892017-05-20 14:15:09 -07001130 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
Benedict Wong0febe5e2017-08-22 21:42:33 -07001131 (authCrypt != null) ? authCrypt.getName() : "",
Manoj Boopathi Rajfffa8112017-10-26 11:49:02 -07001132 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
Benedict Wong0febe5e2017-08-22 21:42:33 -07001133 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
ludi0f807892017-05-20 14:15:09 -07001134 encapType,
1135 encapLocalPort,
1136 encapRemotePort);
Nathan Harold93962f32017-03-07 13:23:36 -08001137 } catch (ServiceSpecificException e) {
1138 // FIXME: get the error code and throw is at an IOException from Errno Exception
ludi0f807892017-05-20 14:15:09 -07001139 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -08001140 }
1141 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001142 // Both SAs were created successfully, time to construct a record and lock it away
1143 mTransformRecords.put(
1144 resourceId, new TransformRecord(resourceId, binder, c, spis, socketRecord));
1145 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001146 }
1147
1148 /**
1149 * Delete a transport mode transform that was previously allocated by + registered with the
1150 * system server. If this is called on an inactive (or non-existent) transform, it will not
1151 * return an error. It's safe to de-allocate transforms that may have already been deleted for
1152 * other reasons.
1153 */
1154 @Override
1155 public void deleteTransportModeTransform(int resourceId) throws RemoteException {
Benedict Wong409c8ca2017-10-26 19:41:43 -07001156 releaseKernelResource(mTransformRecords, resourceId, "IpSecTransform");
Nathan Harold93962f32017-03-07 13:23:36 -08001157 }
1158
1159 /**
1160 * Apply an active transport mode transform to a socket, which will apply the IPsec security
1161 * association as a correspondent policy to the provided socket
1162 */
1163 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001164 public synchronized void applyTransportModeTransform(
1165 ParcelFileDescriptor socket, int resourceId) throws RemoteException {
Benedict Wong409c8ca2017-10-26 19:41:43 -07001166 // Synchronize liberally here because we are using KernelResources in this block
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001167 TransformRecord info;
1168 // FIXME: this code should be factored out into a security check + getter
Nathan Haroldd6f50b22017-10-04 12:58:55 -07001169 info = mTransformRecords.getAndCheckOwner(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -08001170
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001171 if (info == null) {
1172 throw new IllegalArgumentException("Transform " + resourceId + " is not active");
1173 }
Nathan Harold93962f32017-03-07 13:23:36 -08001174
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001175 // TODO: make this a function.
1176 if (info.pid != getCallingPid() || info.uid != getCallingUid()) {
1177 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1178 }
1179
1180 IpSecConfig c = info.getConfig();
1181 try {
1182 for (int direction : DIRECTIONS) {
ludi1a06aa72017-05-12 09:15:00 -07001183 mSrvConfig
1184 .getNetdInstance()
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001185 .ipSecApplyTransportModeTransform(
1186 socket.getFileDescriptor(),
1187 resourceId,
1188 direction,
Nathan Harolda10003d2017-08-23 13:46:33 -07001189 c.getLocalAddress(),
1190 c.getRemoteAddress(),
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001191 info.getSpiRecord(direction).getSpi());
Nathan Harold93962f32017-03-07 13:23:36 -08001192 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001193 } catch (ServiceSpecificException e) {
1194 // FIXME: get the error code and throw is at an IOException from Errno Exception
Nathan Harold93962f32017-03-07 13:23:36 -08001195 }
1196 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001197
Nathan Harold93962f32017-03-07 13:23:36 -08001198 /**
1199 * Remove a transport mode transform from a socket, applying the default (empty) policy. This
1200 * will ensure that NO IPsec policy is applied to the socket (would be the equivalent of
1201 * applying a policy that performs no IPsec). Today the resourceId parameter is passed but not
1202 * used: reserved for future improved input validation.
1203 */
1204 @Override
1205 public void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
1206 throws RemoteException {
1207 try {
ludi1a06aa72017-05-12 09:15:00 -07001208 mSrvConfig
1209 .getNetdInstance()
1210 .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
Nathan Harold93962f32017-03-07 13:23:36 -08001211 } catch (ServiceSpecificException e) {
1212 // FIXME: get the error code and throw is at an IOException from Errno Exception
1213 }
1214 }
1215
1216 @Override
ludib0c95b12017-05-22 10:52:23 -07001217 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001218 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludib0c95b12017-05-22 10:52:23 -07001219
1220 pw.println("IpSecService dump:");
Nathan Harold1afbef42017-03-01 18:55:06 -08001221 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1222 pw.println();
ludib0c95b12017-05-22 10:52:23 -07001223
ludi3e5ea232017-08-10 15:44:40 -07001224 pw.println("mUserQuotaTracker:");
1225 pw.println(mUserQuotaTracker);
ludib0c95b12017-05-22 10:52:23 -07001226 pw.println("mTransformRecords:");
1227 pw.println(mTransformRecords);
1228 pw.println("mUdpSocketRecords:");
1229 pw.println(mUdpSocketRecords);
1230 pw.println("mSpiRecords:");
1231 pw.println(mSpiRecords);
Nathan Harold1afbef42017-03-01 18:55:06 -08001232 }
1233}