blob: cf1d33c86173322b8cbb60e8818ea495b2608b64 [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;
Nathan Harold93962f32017-03-07 13:23:36 -080060import java.util.concurrent.atomic.AtomicInteger;
Nathan Harolda10003d2017-08-23 13:46:33 -070061
Nathan Harold8dc1fd02017-04-04 19:37:48 -070062import libcore.io.IoUtils;
Nathan Harold1afbef42017-03-01 18:55:06 -080063
64/** @hide */
65public class IpSecService extends IIpSecService.Stub {
66 private static final String TAG = "IpSecService";
67 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Nathan Harold8dc1fd02017-04-04 19:37:48 -070068
Nathan Harold1afbef42017-03-01 18:55:06 -080069 private static final String NETD_SERVICE_NAME = "netd";
Nathan Harold93962f32017-03-07 13:23:36 -080070 private static final int[] DIRECTIONS =
71 new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
Nathan Harold1afbef42017-03-01 18:55:06 -080072
ludi1a06aa72017-05-12 09:15:00 -070073 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
Nathan Harold8dc1fd02017-04-04 19:37:48 -070074 private static final int MAX_PORT_BIND_ATTEMPTS = 10;
75 private static final InetAddress INADDR_ANY;
76
77 static {
78 try {
79 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
80 } catch (UnknownHostException e) {
81 throw new RuntimeException(e);
82 }
83 }
84
85 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
86 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
87
88 /* Binder context for this service */
Nathan Harold1afbef42017-03-01 18:55:06 -080089 private final Context mContext;
90
Nathan Harold8dc1fd02017-04-04 19:37:48 -070091 /** Should be a never-repeating global ID for resources */
92 private static AtomicInteger mNextResourceId = new AtomicInteger(0x00FADED0);
Nathan Harold1afbef42017-03-01 18:55:06 -080093
Nathan Harold8dc1fd02017-04-04 19:37:48 -070094 @GuardedBy("this")
95 private final ManagedResourceArray<SpiRecord> mSpiRecords = new ManagedResourceArray<>();
Nathan Harold1afbef42017-03-01 18:55:06 -080096
Nathan Harold8dc1fd02017-04-04 19:37:48 -070097 @GuardedBy("this")
98 private final ManagedResourceArray<TransformRecord> mTransformRecords =
99 new ManagedResourceArray<>();
Nathan Harold93962f32017-03-07 13:23:36 -0800100
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700101 @GuardedBy("this")
102 private final ManagedResourceArray<UdpSocketRecord> mUdpSocketRecords =
103 new ManagedResourceArray<>();
104
ludi1a06aa72017-05-12 09:15:00 -0700105 interface IpSecServiceConfiguration {
106 INetd getNetdInstance() throws RemoteException;
107
108 static IpSecServiceConfiguration GETSRVINSTANCE =
109 new IpSecServiceConfiguration() {
110 @Override
111 public INetd getNetdInstance() throws RemoteException {
112 final INetd netd = NetdService.getInstance();
113 if (netd == null) {
114 throw new RemoteException("Failed to Get Netd Instance");
115 }
116 return netd;
117 }
118 };
119 }
120
121 private final IpSecServiceConfiguration mSrvConfig;
122
Nathan Harolda1afbd82017-04-24 16:16:34 -0700123 /* Very simple counting class that looks much like a counting semaphore */
124 public static class ResourceTracker {
125 private final int mMax;
126 int mCurrent;
127
128 ResourceTracker(int max) {
129 mMax = max;
130 mCurrent = 0;
131 }
132
133 synchronized boolean isAvailable() {
134 return (mCurrent < mMax);
135 }
136
137 synchronized void take() {
138 if (!isAvailable()) {
139 Log.wtf(TAG, "Too many resources allocated!");
140 }
141 mCurrent++;
142 }
143
144 synchronized void give() {
145 if (mCurrent <= 0) {
146 Log.wtf(TAG, "We've released this resource too many times");
147 }
148 mCurrent--;
149 }
ludi3e5ea232017-08-10 15:44:40 -0700150
151 @Override
152 public String toString() {
153 return new StringBuilder()
154 .append("{mCurrent=")
155 .append(mCurrent)
156 .append(", mMax=")
157 .append(mMax)
158 .append("}")
159 .toString();
160 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700161 }
162
163 private static final class UserQuotaTracker {
164 /* Maximum number of UDP Encap Sockets that a single UID may possess */
165 public static final int MAX_NUM_ENCAP_SOCKETS = 2;
166
167 /* Maximum number of IPsec Transforms that a single UID may possess */
168 public static final int MAX_NUM_TRANSFORMS = 4;
169
170 /* Maximum number of IPsec Transforms that a single UID may possess */
171 public static final int MAX_NUM_SPIS = 8;
172
173 /* Record for one users's IpSecService-managed objects */
174 public static class UserRecord {
175 public final ResourceTracker socket = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
176 public final ResourceTracker transform = new ResourceTracker(MAX_NUM_TRANSFORMS);
177 public final ResourceTracker spi = new ResourceTracker(MAX_NUM_SPIS);
ludi3e5ea232017-08-10 15:44:40 -0700178
179 @Override
180 public String toString() {
181 return new StringBuilder()
182 .append("{socket=")
183 .append(socket)
184 .append(", transform=")
185 .append(transform)
186 .append(", spi=")
187 .append(spi)
188 .append("}")
189 .toString();
190 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700191 }
192
193 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
194
195 /* a never-fail getter so that we can populate the list of UIDs as-needed */
196 public synchronized UserRecord getUserRecord(int uid) {
197 UserRecord r = mUserRecords.get(uid);
198 if (r == null) {
199 r = new UserRecord();
200 mUserRecords.put(uid, r);
201 }
202 return r;
203 }
ludi3e5ea232017-08-10 15:44:40 -0700204
205 @Override
206 public String toString() {
207 return mUserRecords.toString();
208 }
Nathan Harolda1afbd82017-04-24 16:16:34 -0700209 }
210
211 private final UserQuotaTracker mUserQuotaTracker = new UserQuotaTracker();
212
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700213 /**
214 * The ManagedResource class provides a facility to cleanly and reliably release system
215 * resources. It relies on two things: an IBinder that allows ManagedResource to automatically
216 * clean up in the event that the Binder dies and a user-provided resourceId that should
217 * uniquely identify the managed resource. To use this class, the user should implement the
218 * releaseResources() method that is responsible for releasing system resources when invoked.
219 */
Nathan Harold93962f32017-03-07 13:23:36 -0800220 private abstract class ManagedResource implements IBinder.DeathRecipient {
221 final int pid;
222 final int uid;
223 private IBinder mBinder;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700224 protected int mResourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800225
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700226 private AtomicInteger mReferenceCount = new AtomicInteger(0);
227
228 ManagedResource(int resourceId, IBinder binder) {
Nathan Harold93962f32017-03-07 13:23:36 -0800229 super();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700230 if (resourceId == INVALID_RESOURCE_ID) {
231 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
232 }
Nathan Harold93962f32017-03-07 13:23:36 -0800233 mBinder = binder;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700234 mResourceId = resourceId;
Nathan Harold93962f32017-03-07 13:23:36 -0800235 pid = Binder.getCallingPid();
236 uid = Binder.getCallingUid();
237
Nathan Harolda1afbd82017-04-24 16:16:34 -0700238 getResourceTracker().take();
Nathan Harold93962f32017-03-07 13:23:36 -0800239 try {
240 mBinder.linkToDeath(this, 0);
241 } catch (RemoteException e) {
242 binderDied();
243 }
244 }
245
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700246 public void addReference() {
247 mReferenceCount.incrementAndGet();
248 }
249
250 public void removeReference() {
251 if (mReferenceCount.decrementAndGet() < 0) {
252 Log.wtf(TAG, "Programming error: negative reference count");
253 }
254 }
255
256 public boolean isReferenced() {
257 return (mReferenceCount.get() > 0);
258 }
259
Nathan Harolda10003d2017-08-23 13:46:33 -0700260 /**
261 * Ensures that the caller is either the owner of this resource or has the system UID and
262 * throws a SecurityException otherwise.
263 */
264 public void checkOwnerOrSystem() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700265 if (uid != Binder.getCallingUid()
266 && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
267 throw new SecurityException("Only the owner may access managed resources!");
268 }
269 }
270
Nathan Harold93962f32017-03-07 13:23:36 -0800271 /**
272 * When this record is no longer needed for managing system resources this function should
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700273 * clean up all system resources and nullify the record. This function shall perform all
274 * necessary cleanup of the resources managed by this record.
275 *
276 * <p>NOTE: this function verifies ownership before allowing resources to be freed.
Nathan Harold93962f32017-03-07 13:23:36 -0800277 */
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700278 public final void release() throws RemoteException {
279 synchronized (IpSecService.this) {
280 if (isReferenced()) {
281 throw new IllegalStateException(
282 "Cannot release a resource that has active references!");
283 }
Nathan Harold93962f32017-03-07 13:23:36 -0800284
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700285 if (mResourceId == INVALID_RESOURCE_ID) {
286 return;
287 }
288
289 releaseResources();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700290 getResourceTracker().give();
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700291 if (mBinder != null) {
292 mBinder.unlinkToDeath(this, 0);
293 }
294 mBinder = null;
295
296 mResourceId = INVALID_RESOURCE_ID;
Nathan Harold93962f32017-03-07 13:23:36 -0800297 }
Nathan Harold93962f32017-03-07 13:23:36 -0800298 }
299
300 /**
301 * If the Binder object dies, this function is called to free the system resources that are
302 * being managed by this record and to subsequently release this record for garbage
303 * collection
304 */
305 public final void binderDied() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700306 try {
307 release();
308 } catch (Exception e) {
309 Log.e(TAG, "Failed to release resource: " + e);
310 }
Nathan Harold93962f32017-03-07 13:23:36 -0800311 }
312
313 /**
Nathan Harold93962f32017-03-07 13:23:36 -0800314 * Implement this method to release all system resources that are being protected by this
315 * record. Once the resources are released, the record should be invalidated and no longer
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700316 * used by calling release(). This should NEVER be called directly.
317 *
318 * <p>Calls to this are always guarded by IpSecService#this
Nathan Harold93962f32017-03-07 13:23:36 -0800319 */
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700320 protected abstract void releaseResources() throws RemoteException;
ludib0c95b12017-05-22 10:52:23 -0700321
Nathan Harolda1afbd82017-04-24 16:16:34 -0700322 /** Get the resource tracker for this resource */
323 protected abstract ResourceTracker getResourceTracker();
324
ludib0c95b12017-05-22 10:52:23 -0700325 @Override
326 public String toString() {
327 return new StringBuilder()
328 .append("{mResourceId=")
329 .append(mResourceId)
330 .append(", pid=")
331 .append(pid)
332 .append(", uid=")
333 .append(uid)
334 .append(", mReferenceCount=")
335 .append(mReferenceCount.get())
336 .append("}")
337 .toString();
338 }
Nathan Harold93962f32017-03-07 13:23:36 -0800339 };
340
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700341 /**
ludi1a06aa72017-05-12 09:15:00 -0700342 * Minimal wrapper around SparseArray that performs ownership validation on element accesses.
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700343 */
344 private class ManagedResourceArray<T extends ManagedResource> {
345 SparseArray<T> mArray = new SparseArray<>();
Nathan Harold93962f32017-03-07 13:23:36 -0800346
Nathan Haroldd6f50b22017-10-04 12:58:55 -0700347 T getAndCheckOwner(int key) {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700348 T val = mArray.get(key);
Nathan Haroldb6d2eff2017-07-06 16:57:51 -0700349 // The value should never be null unless the resource doesn't exist
350 // (since we do not allow null resources to be added).
351 if (val != null) {
Nathan Harolda10003d2017-08-23 13:46:33 -0700352 val.checkOwnerOrSystem();
Nathan Haroldb6d2eff2017-07-06 16:57:51 -0700353 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700354 return val;
355 }
356
357 void put(int key, T obj) {
358 checkNotNull(obj, "Null resources cannot be added");
359 mArray.put(key, obj);
360 }
361
362 void remove(int key) {
363 mArray.remove(key);
364 }
ludib0c95b12017-05-22 10:52:23 -0700365
366 @Override
367 public String toString() {
368 return mArray.toString();
369 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700370 }
371
372 private final class TransformRecord extends ManagedResource {
373 private final IpSecConfig mConfig;
374 private final SpiRecord[] mSpis;
375 private final UdpSocketRecord mSocket;
376
377 TransformRecord(
378 int resourceId,
379 IBinder binder,
380 IpSecConfig config,
381 SpiRecord[] spis,
382 UdpSocketRecord socket) {
383 super(resourceId, binder);
Nathan Harold93962f32017-03-07 13:23:36 -0800384 mConfig = config;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700385 mSpis = spis;
386 mSocket = socket;
387
388 for (int direction : DIRECTIONS) {
389 mSpis[direction].addReference();
390 mSpis[direction].setOwnedByTransform();
391 }
392
393 if (mSocket != null) {
394 mSocket.addReference();
395 }
Nathan Harold93962f32017-03-07 13:23:36 -0800396 }
397
398 public IpSecConfig getConfig() {
399 return mConfig;
400 }
401
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700402 public SpiRecord getSpiRecord(int direction) {
403 return mSpis[direction];
404 }
405
406 /** always guarded by IpSecService#this */
Nathan Harold93962f32017-03-07 13:23:36 -0800407 @Override
408 protected void releaseResources() {
409 for (int direction : DIRECTIONS) {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700410 int spi = mSpis[direction].getSpi();
Nathan Harold93962f32017-03-07 13:23:36 -0800411 try {
ludi1a06aa72017-05-12 09:15:00 -0700412 mSrvConfig
413 .getNetdInstance()
Nathan Harold93962f32017-03-07 13:23:36 -0800414 .ipSecDeleteSecurityAssociation(
415 mResourceId,
416 direction,
Nathan Harolda10003d2017-08-23 13:46:33 -0700417 mConfig.getLocalAddress(),
418 mConfig.getRemoteAddress(),
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700419 spi);
Nathan Harold93962f32017-03-07 13:23:36 -0800420 } catch (ServiceSpecificException e) {
421 // FIXME: get the error code and throw is at an IOException from Errno Exception
422 } catch (RemoteException e) {
423 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
424 }
425 }
Nathan Harold93962f32017-03-07 13:23:36 -0800426
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700427 for (int direction : DIRECTIONS) {
428 mSpis[direction].removeReference();
429 }
430
431 if (mSocket != null) {
432 mSocket.removeReference();
433 }
Nathan Harold93962f32017-03-07 13:23:36 -0800434 }
ludib0c95b12017-05-22 10:52:23 -0700435
Nathan Harolda1afbd82017-04-24 16:16:34 -0700436 protected ResourceTracker getResourceTracker() {
437 return mUserQuotaTracker.getUserRecord(this.uid).transform;
438 }
439
ludib0c95b12017-05-22 10:52:23 -0700440 @Override
441 public String toString() {
442 StringBuilder strBuilder = new StringBuilder();
443 strBuilder
444 .append("{super=")
445 .append(super.toString())
446 .append(", mSocket=")
447 .append(mSocket)
448 .append(", mSpis[OUT].mResourceId=")
449 .append(mSpis[IpSecTransform.DIRECTION_OUT].mResourceId)
450 .append(", mSpis[IN].mResourceId=")
451 .append(mSpis[IpSecTransform.DIRECTION_IN].mResourceId)
452 .append(", mConfig=")
453 .append(mConfig)
454 .append("}");
455 return strBuilder.toString();
456 }
Nathan Harold93962f32017-03-07 13:23:36 -0800457 }
458
459 private final class SpiRecord extends ManagedResource {
460 private final int mDirection;
461 private final String mLocalAddress;
462 private final String mRemoteAddress;
Nathan Harold93962f32017-03-07 13:23:36 -0800463 private int mSpi;
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700464
465 private boolean mOwnedByTransform = false;
Nathan Harold93962f32017-03-07 13:23:36 -0800466
467 SpiRecord(
468 int resourceId,
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700469 IBinder binder,
Nathan Harold93962f32017-03-07 13:23:36 -0800470 int direction,
471 String localAddress,
472 String remoteAddress,
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700473 int spi) {
474 super(resourceId, binder);
Nathan Harold93962f32017-03-07 13:23:36 -0800475 mDirection = direction;
476 mLocalAddress = localAddress;
477 mRemoteAddress = remoteAddress;
478 mSpi = spi;
Nathan Harold93962f32017-03-07 13:23:36 -0800479 }
480
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700481 /** always guarded by IpSecService#this */
482 @Override
Nathan Harold93962f32017-03-07 13:23:36 -0800483 protected void releaseResources() {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700484 if (mOwnedByTransform) {
485 Log.d(TAG, "Cannot release Spi " + mSpi + ": Currently locked by a Transform");
486 // Because SPIs are "handed off" to transform, objects, they should never be
487 // freed from the SpiRecord once used in a transform. (They refer to the same SA,
488 // thus ownership and responsibility for freeing these resources passes to the
489 // Transform object). Thus, we should let the user free them without penalty once
490 // they are applied in a Transform object.
491 return;
492 }
493
Nathan Harold93962f32017-03-07 13:23:36 -0800494 try {
ludi1a06aa72017-05-12 09:15:00 -0700495 mSrvConfig
496 .getNetdInstance()
Nathan Harold93962f32017-03-07 13:23:36 -0800497 .ipSecDeleteSecurityAssociation(
498 mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi);
499 } catch (ServiceSpecificException e) {
500 // FIXME: get the error code and throw is at an IOException from Errno Exception
501 } catch (RemoteException e) {
502 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId);
503 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700504
505 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
Nathan Harold93962f32017-03-07 13:23:36 -0800506 }
507
Nathan Harolda1afbd82017-04-24 16:16:34 -0700508 @Override
509 protected ResourceTracker getResourceTracker() {
510 return mUserQuotaTracker.getUserRecord(this.uid).spi;
511 }
512
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700513 public int getSpi() {
514 return mSpi;
515 }
516
517 public void setOwnedByTransform() {
518 if (mOwnedByTransform) {
519 // Programming error
Andreas Gamped6d8e452017-07-11 10:25:09 -0700520 throw new IllegalStateException("Cannot own an SPI twice!");
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700521 }
522
523 mOwnedByTransform = true;
Nathan Harold93962f32017-03-07 13:23:36 -0800524 }
ludib0c95b12017-05-22 10:52:23 -0700525
526 @Override
527 public String toString() {
528 StringBuilder strBuilder = new StringBuilder();
529 strBuilder
530 .append("{super=")
531 .append(super.toString())
532 .append(", mSpi=")
533 .append(mSpi)
534 .append(", mDirection=")
535 .append(mDirection)
536 .append(", mLocalAddress=")
537 .append(mLocalAddress)
538 .append(", mRemoteAddress=")
539 .append(mRemoteAddress)
540 .append(", mOwnedByTransform=")
541 .append(mOwnedByTransform)
542 .append("}");
543 return strBuilder.toString();
544 }
Nathan Harold93962f32017-03-07 13:23:36 -0800545 }
546
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700547 private final class UdpSocketRecord extends ManagedResource {
548 private FileDescriptor mSocket;
549 private final int mPort;
Nathan Harold93962f32017-03-07 13:23:36 -0800550
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700551 UdpSocketRecord(int resourceId, IBinder binder, FileDescriptor socket, int port) {
552 super(resourceId, binder);
553 mSocket = socket;
554 mPort = port;
555 }
556
557 /** always guarded by IpSecService#this */
558 @Override
559 protected void releaseResources() {
560 Log.d(TAG, "Closing port " + mPort);
561 IoUtils.closeQuietly(mSocket);
562 mSocket = null;
563 }
564
Nathan Harolda1afbd82017-04-24 16:16:34 -0700565 @Override
566 protected ResourceTracker getResourceTracker() {
567 return mUserQuotaTracker.getUserRecord(this.uid).socket;
568 }
569
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700570 public int getPort() {
571 return mPort;
572 }
573
574 public FileDescriptor getSocket() {
575 return mSocket;
576 }
ludib0c95b12017-05-22 10:52:23 -0700577
578 @Override
579 public String toString() {
580 return new StringBuilder()
581 .append("{super=")
582 .append(super.toString())
583 .append(", mSocket=")
584 .append(mSocket)
585 .append(", mPort=")
586 .append(mPort)
587 .append("}")
588 .toString();
589 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700590 }
Nathan Harold93962f32017-03-07 13:23:36 -0800591
Nathan Harold1afbef42017-03-01 18:55:06 -0800592 /**
593 * Constructs a new IpSecService instance
594 *
595 * @param context Binder context for this service
596 */
597 private IpSecService(Context context) {
ludi1a06aa72017-05-12 09:15:00 -0700598 this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
Nathan Harold1afbef42017-03-01 18:55:06 -0800599 }
600
601 static IpSecService create(Context context) throws InterruptedException {
602 final IpSecService service = new IpSecService(context);
603 service.connectNativeNetdService();
604 return service;
605 }
606
ludi1a06aa72017-05-12 09:15:00 -0700607 /** @hide */
608 @VisibleForTesting
609 public IpSecService(Context context, IpSecServiceConfiguration config) {
610 mContext = context;
611 mSrvConfig = config;
612 }
613
Nathan Harold1afbef42017-03-01 18:55:06 -0800614 public void systemReady() {
615 if (isNetdAlive()) {
616 Slog.d(TAG, "IpSecService is ready");
617 } else {
618 Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
619 }
620 }
621
622 private void connectNativeNetdService() {
623 // Avoid blocking the system server to do this
Nathan Haroldb0e05082017-07-17 14:01:53 -0700624 new Thread() {
625 @Override
626 public void run() {
627 synchronized (IpSecService.this) {
ludi1a06aa72017-05-12 09:15:00 -0700628 NetdService.get(NETD_FETCH_TIMEOUT_MS);
Nathan Haroldb0e05082017-07-17 14:01:53 -0700629 }
630 }
631 }.start();
Nathan Harold1afbef42017-03-01 18:55:06 -0800632 }
633
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700634 synchronized boolean isNetdAlive() {
635 try {
ludi1a06aa72017-05-12 09:15:00 -0700636 final INetd netd = mSrvConfig.getNetdInstance();
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700637 if (netd == null) {
Nathan Harold1afbef42017-03-01 18:55:06 -0800638 return false;
639 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700640 return netd.isAlive();
641 } catch (RemoteException re) {
642 return false;
Nathan Harold1afbef42017-03-01 18:55:06 -0800643 }
644 }
645
Nathan Harolda10003d2017-08-23 13:46:33 -0700646 /**
647 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
648 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
649 */
650 private static void checkInetAddress(String inetAddress) {
651 if (TextUtils.isEmpty(inetAddress)) {
652 throw new IllegalArgumentException("Unspecified address");
653 }
654
655 InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
656
657 if (checkAddr.isAnyLocalAddress()) {
658 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
659 }
660 }
661
662 /**
663 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
664 * DIRECTION_IN or DIRECTION_OUT
665 */
666 private static void checkDirection(int direction) {
667 switch (direction) {
668 case IpSecTransform.DIRECTION_OUT:
669 case IpSecTransform.DIRECTION_IN:
670 return;
671 }
672 throw new IllegalArgumentException("Invalid Direction: " + direction);
673 }
674
Nathan Harold1afbef42017-03-01 18:55:06 -0800675 @Override
Nathan Harold93962f32017-03-07 13:23:36 -0800676 /** Get a new SPI and maintain the reservation in the system server */
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700677 public synchronized IpSecSpiResponse reserveSecurityParameterIndex(
Nathan Harold93962f32017-03-07 13:23:36 -0800678 int direction, String remoteAddress, int requestedSpi, IBinder binder)
679 throws RemoteException {
Nathan Harolda10003d2017-08-23 13:46:33 -0700680 checkDirection(direction);
681 checkInetAddress(remoteAddress);
682 /* requestedSpi can be anything in the int range, so no check is needed. */
683 checkNotNull(binder, "Null Binder passed to reserveSecurityParameterIndex");
684
Nathan Harold93962f32017-03-07 13:23:36 -0800685 int resourceId = mNextResourceId.getAndIncrement();
686
687 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
688 String localAddress = "";
Nathan Harolda1afbd82017-04-24 16:16:34 -0700689
Nathan Harold93962f32017-03-07 13:23:36 -0800690 try {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700691 if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).spi.isAvailable()) {
692 return new IpSecSpiResponse(
Nathan Harolda10003d2017-08-23 13:46:33 -0700693 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harolda1afbd82017-04-24 16:16:34 -0700694 }
Nathan Harold93962f32017-03-07 13:23:36 -0800695 spi =
ludi1a06aa72017-05-12 09:15:00 -0700696 mSrvConfig
697 .getNetdInstance()
Nathan Harold93962f32017-03-07 13:23:36 -0800698 .ipSecAllocateSpi(
699 resourceId,
700 direction,
701 localAddress,
702 remoteAddress,
703 requestedSpi);
704 Log.d(TAG, "Allocated SPI " + spi);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700705 mSpiRecords.put(
706 resourceId,
707 new SpiRecord(resourceId, binder, direction, localAddress, remoteAddress, spi));
Nathan Harold93962f32017-03-07 13:23:36 -0800708 } catch (ServiceSpecificException e) {
709 // TODO: Add appropriate checks when other ServiceSpecificException types are supported
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700710 return new IpSecSpiResponse(
Nathan Harolda1afbd82017-04-24 16:16:34 -0700711 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
Nathan Harold93962f32017-03-07 13:23:36 -0800712 } catch (RemoteException e) {
713 throw e.rethrowFromSystemServer();
714 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700715 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
716 }
717
718 /* This method should only be called from Binder threads. Do not call this from
719 * within the system server as it will crash the system on failure.
720 */
721 private synchronized <T extends ManagedResource> void releaseManagedResource(
722 ManagedResourceArray<T> resArray, int resourceId, String typeName)
723 throws RemoteException {
724 // We want to non-destructively get so that we can check credentials before removing
725 // this from the records.
Nathan Haroldd6f50b22017-10-04 12:58:55 -0700726 T record = resArray.getAndCheckOwner(resourceId);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700727
728 if (record == null) {
729 throw new IllegalArgumentException(
730 typeName + " " + resourceId + " is not available to be deleted");
731 }
732
733 record.release();
734 resArray.remove(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -0800735 }
736
737 /** Release a previously allocated SPI that has been registered with the system server */
738 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700739 public void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
740 releaseManagedResource(mSpiRecords, resourceId, "SecurityParameterIndex");
741 }
742
743 /**
744 * This function finds and forcibly binds to a random system port, ensuring that the port cannot
745 * be unbound.
746 *
747 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
748 * a random open port and then bind by number, this function creates a temp socket, binds to a
749 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
750 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
751 * FileHandle.
752 *
753 * <p>The loop in this function handles the inherent race window between un-binding to a port
754 * and re-binding, during which the system could *technically* hand that port out to someone
755 * else.
756 */
757 private void bindToRandomPort(FileDescriptor sockFd) throws IOException {
758 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
759 try {
760 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
761 Os.bind(probeSocket, INADDR_ANY, 0);
762 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
763 Os.close(probeSocket);
764 Log.v(TAG, "Binding to port " + port);
765 Os.bind(sockFd, INADDR_ANY, port);
766 return;
767 } catch (ErrnoException e) {
768 // Someone miraculously claimed the port just after we closed probeSocket.
769 if (e.errno == OsConstants.EADDRINUSE) {
770 continue;
771 }
772 throw e.rethrowAsIOException();
773 }
774 }
775 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
776 }
Nathan Harold93962f32017-03-07 13:23:36 -0800777
778 /**
779 * Open a socket via the system server and bind it to the specified port (random if port=0).
780 * This will return a PFD to the user that represent a bound UDP socket. The system server will
781 * cache the socket and a record of its owner so that it can and must be freed when no longer
782 * needed.
783 */
784 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700785 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
786 throws RemoteException {
787 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
788 throw new IllegalArgumentException(
789 "Specified port number must be a valid non-reserved UDP port");
790 }
Nathan Harolda10003d2017-08-23 13:46:33 -0700791 checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
792
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700793 int resourceId = mNextResourceId.getAndIncrement();
794 FileDescriptor sockFd = null;
795 try {
Nathan Harolda1afbd82017-04-24 16:16:34 -0700796 if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).socket.isAvailable()) {
797 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
798 }
799
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700800 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
801
802 if (port != 0) {
803 Log.v(TAG, "Binding to port " + port);
804 Os.bind(sockFd, INADDR_ANY, port);
805 } else {
806 bindToRandomPort(sockFd);
807 }
808 // This code is common to both the unspecified and specified port cases
809 Os.setsockoptInt(
810 sockFd,
811 OsConstants.IPPROTO_UDP,
812 OsConstants.UDP_ENCAP,
813 OsConstants.UDP_ENCAP_ESPINUDP);
814
815 mUdpSocketRecords.put(
816 resourceId, new UdpSocketRecord(resourceId, binder, sockFd, port));
817 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
818 } catch (IOException | ErrnoException e) {
819 IoUtils.closeQuietly(sockFd);
820 }
821 // If we make it to here, then something has gone wrong and we couldn't open a socket.
822 // The only reasonable condition that would cause that is resource unavailable.
823 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -0800824 }
825
826 /** close a socket that has been been allocated by and registered with the system server */
827 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700828 public void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
829
830 releaseManagedResource(mUdpSocketRecords, resourceId, "UdpEncapsulationSocket");
831 }
Nathan Harold93962f32017-03-07 13:23:36 -0800832
833 /**
Nathan Harolda10003d2017-08-23 13:46:33 -0700834 * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
835 * IllegalArgumentException if they are not.
836 */
837 private void checkIpSecConfig(IpSecConfig config) {
838 if (config.getLocalAddress() == null) {
839 throw new IllegalArgumentException("Invalid null Local InetAddress");
840 }
841
842 if (config.getRemoteAddress() == null) {
843 throw new IllegalArgumentException("Invalid null Remote InetAddress");
844 }
845
846 switch (config.getMode()) {
847 case IpSecTransform.MODE_TRANSPORT:
848 if (!config.getLocalAddress().isEmpty()) {
849 throw new IllegalArgumentException("Non-empty Local Address");
850 }
851 // Must be valid, and not a wildcard
852 checkInetAddress(config.getRemoteAddress());
853 break;
854 case IpSecTransform.MODE_TUNNEL:
855 break;
856 default:
857 throw new IllegalArgumentException(
858 "Invalid IpSecTransform.mode: " + config.getMode());
859 }
860
861 switch (config.getEncapType()) {
862 case IpSecTransform.ENCAP_NONE:
863 break;
864 case IpSecTransform.ENCAP_ESPINUDP:
865 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
Nathan Haroldd6f50b22017-10-04 12:58:55 -0700866 if (mUdpSocketRecords.getAndCheckOwner(
867 config.getEncapSocketResourceId()) == null) {
Nathan Harolda10003d2017-08-23 13:46:33 -0700868 throw new IllegalStateException(
869 "No Encapsulation socket for Resource Id: "
870 + config.getEncapSocketResourceId());
871 }
872
873 int port = config.getEncapRemotePort();
874 if (port <= 0 || port > 0xFFFF) {
875 throw new IllegalArgumentException("Invalid remote UDP port: " + port);
876 }
877 break;
878 default:
879 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
880 }
881
882 for (int direction : DIRECTIONS) {
883 IpSecAlgorithm crypt = config.getEncryption(direction);
884 IpSecAlgorithm auth = config.getAuthentication(direction);
Benedict Wong0febe5e2017-08-22 21:42:33 -0700885 IpSecAlgorithm authenticatedEncryption = config.getAuthenticatedEncryption(direction);
886 if (authenticatedEncryption == null && crypt == null && auth == null) {
887 throw new IllegalArgumentException(
888 "No Encryption or Authentication algorithms specified");
889 } else if (authenticatedEncryption != null && (auth != null || crypt != null)) {
890 throw new IllegalArgumentException(
891 "Authenticated Encryption is mutually"
892 + " exclusive with other Authentication or Encryption algorithms");
Nathan Harolda10003d2017-08-23 13:46:33 -0700893 }
894
Nathan Haroldd6f50b22017-10-04 12:58:55 -0700895 if (mSpiRecords.getAndCheckOwner(config.getSpiResourceId(direction)) == null) {
Nathan Harolda10003d2017-08-23 13:46:33 -0700896 throw new IllegalStateException("No SPI for specified Resource Id");
897 }
898 }
899 }
900
901 /**
Nathan Harold93962f32017-03-07 13:23:36 -0800902 * Create a transport mode transform, which represent two security associations (one in each
903 * direction) in the kernel. The transform will be cached by the system server and must be freed
904 * when no longer needed. It is possible to free one, deleting the SA from underneath sockets
905 * that are using it, which will result in all of those sockets becoming unable to send or
906 * receive data.
907 */
908 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700909 public synchronized IpSecTransformResponse createTransportModeTransform(
910 IpSecConfig c, IBinder binder) throws RemoteException {
Nathan Harolda10003d2017-08-23 13:46:33 -0700911 checkIpSecConfig(c);
912 checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
Nathan Harold93962f32017-03-07 13:23:36 -0800913 int resourceId = mNextResourceId.getAndIncrement();
Nathan Harolda1afbd82017-04-24 16:16:34 -0700914 if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).transform.isAvailable()) {
915 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
916 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700917 SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
Nathan Harolda10003d2017-08-23 13:46:33 -0700918
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700919 int encapType, encapLocalPort = 0, encapRemotePort = 0;
920 UdpSocketRecord socketRecord = null;
921 encapType = c.getEncapType();
922 if (encapType != IpSecTransform.ENCAP_NONE) {
Nathan Haroldd6f50b22017-10-04 12:58:55 -0700923 socketRecord = mUdpSocketRecords.getAndCheckOwner(c.getEncapSocketResourceId());
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700924 encapLocalPort = socketRecord.getPort();
925 encapRemotePort = c.getEncapRemotePort();
926 }
927
Nathan Harold93962f32017-03-07 13:23:36 -0800928 for (int direction : DIRECTIONS) {
929 IpSecAlgorithm auth = c.getAuthentication(direction);
930 IpSecAlgorithm crypt = c.getEncryption(direction);
Benedict Wong0febe5e2017-08-22 21:42:33 -0700931 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption(direction);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700932
Nathan Haroldd6f50b22017-10-04 12:58:55 -0700933 spis[direction] = mSpiRecords.getAndCheckOwner(c.getSpiResourceId(direction));
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700934 int spi = spis[direction].getSpi();
Nathan Harold93962f32017-03-07 13:23:36 -0800935 try {
Nathan Harolda10003d2017-08-23 13:46:33 -0700936 mSrvConfig
937 .getNetdInstance()
ludi0f807892017-05-20 14:15:09 -0700938 .ipSecAddSecurityAssociation(
939 resourceId,
940 c.getMode(),
941 direction,
Nathan Harolda10003d2017-08-23 13:46:33 -0700942 c.getLocalAddress(),
943 c.getRemoteAddress(),
944 (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0,
ludi0f807892017-05-20 14:15:09 -0700945 spi,
946 (auth != null) ? auth.getName() : "",
947 (auth != null) ? auth.getKey() : null,
948 (auth != null) ? auth.getTruncationLengthBits() : 0,
949 (crypt != null) ? crypt.getName() : "",
950 (crypt != null) ? crypt.getKey() : null,
951 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
Benedict Wong0febe5e2017-08-22 21:42:33 -0700952 (authCrypt != null) ? authCrypt.getName() : "",
953 (authCrypt != null) ? authCrypt.getKey() : null,
954 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
ludi0f807892017-05-20 14:15:09 -0700955 encapType,
956 encapLocalPort,
957 encapRemotePort);
Nathan Harold93962f32017-03-07 13:23:36 -0800958 } catch (ServiceSpecificException e) {
959 // FIXME: get the error code and throw is at an IOException from Errno Exception
ludi0f807892017-05-20 14:15:09 -0700960 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
Nathan Harold93962f32017-03-07 13:23:36 -0800961 }
962 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700963 // Both SAs were created successfully, time to construct a record and lock it away
964 mTransformRecords.put(
965 resourceId, new TransformRecord(resourceId, binder, c, spis, socketRecord));
966 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -0800967 }
968
969 /**
970 * Delete a transport mode transform that was previously allocated by + registered with the
971 * system server. If this is called on an inactive (or non-existent) transform, it will not
972 * return an error. It's safe to de-allocate transforms that may have already been deleted for
973 * other reasons.
974 */
975 @Override
976 public void deleteTransportModeTransform(int resourceId) throws RemoteException {
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700977 releaseManagedResource(mTransformRecords, resourceId, "IpSecTransform");
Nathan Harold93962f32017-03-07 13:23:36 -0800978 }
979
980 /**
981 * Apply an active transport mode transform to a socket, which will apply the IPsec security
982 * association as a correspondent policy to the provided socket
983 */
984 @Override
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700985 public synchronized void applyTransportModeTransform(
986 ParcelFileDescriptor socket, int resourceId) throws RemoteException {
987 // Synchronize liberally here because we are using ManagedResources in this block
988 TransformRecord info;
989 // FIXME: this code should be factored out into a security check + getter
Nathan Haroldd6f50b22017-10-04 12:58:55 -0700990 info = mTransformRecords.getAndCheckOwner(resourceId);
Nathan Harold93962f32017-03-07 13:23:36 -0800991
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700992 if (info == null) {
993 throw new IllegalArgumentException("Transform " + resourceId + " is not active");
994 }
Nathan Harold93962f32017-03-07 13:23:36 -0800995
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700996 // TODO: make this a function.
997 if (info.pid != getCallingPid() || info.uid != getCallingUid()) {
998 throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
999 }
1000
1001 IpSecConfig c = info.getConfig();
1002 try {
1003 for (int direction : DIRECTIONS) {
ludi1a06aa72017-05-12 09:15:00 -07001004 mSrvConfig
1005 .getNetdInstance()
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001006 .ipSecApplyTransportModeTransform(
1007 socket.getFileDescriptor(),
1008 resourceId,
1009 direction,
Nathan Harolda10003d2017-08-23 13:46:33 -07001010 c.getLocalAddress(),
1011 c.getRemoteAddress(),
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001012 info.getSpiRecord(direction).getSpi());
Nathan Harold93962f32017-03-07 13:23:36 -08001013 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001014 } catch (ServiceSpecificException e) {
1015 // FIXME: get the error code and throw is at an IOException from Errno Exception
Nathan Harold93962f32017-03-07 13:23:36 -08001016 }
1017 }
Nathan Harold8dc1fd02017-04-04 19:37:48 -07001018
Nathan Harold93962f32017-03-07 13:23:36 -08001019 /**
1020 * Remove a transport mode transform from a socket, applying the default (empty) policy. This
1021 * will ensure that NO IPsec policy is applied to the socket (would be the equivalent of
1022 * applying a policy that performs no IPsec). Today the resourceId parameter is passed but not
1023 * used: reserved for future improved input validation.
1024 */
1025 @Override
1026 public void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
1027 throws RemoteException {
1028 try {
ludi1a06aa72017-05-12 09:15:00 -07001029 mSrvConfig
1030 .getNetdInstance()
1031 .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
Nathan Harold93962f32017-03-07 13:23:36 -08001032 } catch (ServiceSpecificException e) {
1033 // FIXME: get the error code and throw is at an IOException from Errno Exception
1034 }
1035 }
1036
1037 @Override
ludib0c95b12017-05-22 10:52:23 -07001038 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Nathan Harold1afbef42017-03-01 18:55:06 -08001039 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
ludib0c95b12017-05-22 10:52:23 -07001040
1041 pw.println("IpSecService dump:");
Nathan Harold1afbef42017-03-01 18:55:06 -08001042 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1043 pw.println();
ludib0c95b12017-05-22 10:52:23 -07001044
ludi3e5ea232017-08-10 15:44:40 -07001045 pw.println("mUserQuotaTracker:");
1046 pw.println(mUserQuotaTracker);
ludib0c95b12017-05-22 10:52:23 -07001047 pw.println("mTransformRecords:");
1048 pw.println(mTransformRecords);
1049 pw.println("mUdpSocketRecords:");
1050 pw.println(mUdpSocketRecords);
1051 pw.println("mSpiRecords:");
1052 pw.println(mSpiRecords);
Nathan Harold1afbef42017-03-01 18:55:06 -08001053 }
1054}