blob: 48b5bd5c3d5b359c8b0a96ef571bf4cccf3ae9d9 [file] [log] [blame]
Nathan Harold330e1082017-01-12 18:38:57 -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 */
16package android.net;
17
Nathan Harold93962f32017-03-07 13:23:36 -080018import static android.net.IpSecManager.INVALID_RESOURCE_ID;
Nathan Harold93962f32017-03-07 13:23:36 -080019
Nathan Harold330e1082017-01-12 18:38:57 -080020import android.annotation.IntDef;
Nathan Harold93962f32017-03-07 13:23:36 -080021import android.annotation.NonNull;
Nathan Harold330e1082017-01-12 18:38:57 -080022import android.annotation.SystemApi;
23import android.content.Context;
Nathan Harold93962f32017-03-07 13:23:36 -080024import android.os.Binder;
Nathan Harold93962f32017-03-07 13:23:36 -080025import android.os.IBinder;
26import android.os.RemoteException;
27import android.os.ServiceManager;
Nathan Harold330e1082017-01-12 18:38:57 -080028import android.util.Log;
Nathan Haroldd999d222017-09-11 19:53:33 -070029
ludi1a06aa72017-05-12 09:15:00 -070030import com.android.internal.annotations.VisibleForTesting;
Nathan Harold93962f32017-03-07 13:23:36 -080031import com.android.internal.util.Preconditions;
Nathan Haroldd999d222017-09-11 19:53:33 -070032
Nathan Harold330e1082017-01-12 18:38:57 -080033import dalvik.system.CloseGuard;
Nathan Haroldd999d222017-09-11 19:53:33 -070034
Nathan Harold330e1082017-01-12 18:38:57 -080035import java.io.IOException;
36import java.lang.annotation.Retention;
37import java.lang.annotation.RetentionPolicy;
38import java.net.InetAddress;
39
40/**
41 * This class represents an IpSecTransform, which encapsulates both properties and state of IPsec.
42 *
43 * <p>IpSecTransforms must be built from an IpSecTransform.Builder, and they must persist throughout
44 * the lifetime of the underlying transform. If a transform object leaves scope, the underlying
45 * transform may be disabled automatically, with likely undesirable results.
46 *
47 * <p>An IpSecTransform may either represent a tunnel mode transform that operates on a wide array
48 * of traffic or may represent a transport mode transform operating on a Socket or Sockets.
49 */
50public final class IpSecTransform implements AutoCloseable {
51 private static final String TAG = "IpSecTransform";
52
53 /**
54 * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies
55 * to traffic towards the host.
56 */
57 public static final int DIRECTION_IN = 0;
58
59 /**
60 * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies
61 * to traffic from the host.
62 */
63 public static final int DIRECTION_OUT = 1;
64
65 /** @hide */
66 @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
67 @Retention(RetentionPolicy.SOURCE)
68 public @interface TransformDirection {}
69
70 /** @hide */
Nathan Harolda10003d2017-08-23 13:46:33 -070071 public static final int MODE_TRANSPORT = 0;
Nathan Harold330e1082017-01-12 18:38:57 -080072
73 /** @hide */
Nathan Harolda10003d2017-08-23 13:46:33 -070074 public static final int MODE_TUNNEL = 1;
Nathan Harold330e1082017-01-12 18:38:57 -080075
76 /** @hide */
77 public static final int ENCAP_NONE = 0;
78
79 /**
Nathan Harold330e1082017-01-12 18:38:57 -080080 * IpSec traffic will be encapsulated within a UDP header with an additional 8-byte header pad
81 * (of '0'-value bytes) that prevents traffic from being interpreted as IKE or as ESP over UDP.
82 *
83 * @hide
84 */
Nathan Harold8dc1fd02017-04-04 19:37:48 -070085 public static final int ENCAP_ESPINUDP_NON_IKE = 1;
86
87 /**
88 * IpSec traffic will be encapsulated within UDP as per <a
89 * href="https://tools.ietf.org/html/rfc3948">RFC3498</a>.
90 *
91 * @hide
92 */
93 public static final int ENCAP_ESPINUDP = 2;
Nathan Harold330e1082017-01-12 18:38:57 -080094
95 /** @hide */
Nathan Harold8dc1fd02017-04-04 19:37:48 -070096 @IntDef(value = {ENCAP_NONE, ENCAP_ESPINUDP, ENCAP_ESPINUDP_NON_IKE})
Nathan Harold330e1082017-01-12 18:38:57 -080097 @Retention(RetentionPolicy.SOURCE)
98 public @interface EncapType {}
99
Nathan Harold330e1082017-01-12 18:38:57 -0800100 private IpSecTransform(Context context, IpSecConfig config) {
101 mContext = context;
102 mConfig = config;
Nathan Harold93962f32017-03-07 13:23:36 -0800103 mResourceId = INVALID_RESOURCE_ID;
104 }
105
106 private IIpSecService getIpSecService() {
107 IBinder b = ServiceManager.getService(android.content.Context.IPSEC_SERVICE);
108 if (b == null) {
109 throw new RemoteException("Failed to connect to IpSecService")
110 .rethrowAsRuntimeException();
111 }
112
113 return IIpSecService.Stub.asInterface(b);
114 }
115
Nathan Harolda10003d2017-08-23 13:46:33 -0700116 /**
117 * Checks the result status and throws an appropriate exception if
118 * the status is not Status.OK.
119 */
120 private void checkResultStatus(int status)
Nathan Harold93962f32017-03-07 13:23:36 -0800121 throws IOException, IpSecManager.ResourceUnavailableException,
122 IpSecManager.SpiUnavailableException {
123 switch (status) {
124 case IpSecManager.Status.OK:
125 return;
126 // TODO: Pass Error string back from bundle so that errors can be more specific
127 case IpSecManager.Status.RESOURCE_UNAVAILABLE:
128 throw new IpSecManager.ResourceUnavailableException(
129 "Failed to allocate a new IpSecTransform");
130 case IpSecManager.Status.SPI_UNAVAILABLE:
131 Log.wtf(TAG, "Attempting to use an SPI that was somehow not reserved");
132 // Fall through
133 default:
134 throw new IllegalStateException(
135 "Failed to Create a Transform with status code " + status);
136 }
Nathan Harold330e1082017-01-12 18:38:57 -0800137 }
138
139 private IpSecTransform activate()
140 throws IOException, IpSecManager.ResourceUnavailableException,
141 IpSecManager.SpiUnavailableException {
Nathan Harold330e1082017-01-12 18:38:57 -0800142 synchronized (this) {
Nathan Harold93962f32017-03-07 13:23:36 -0800143 try {
144 IIpSecService svc = getIpSecService();
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700145 IpSecTransformResponse result =
146 svc.createTransportModeTransform(mConfig, new Binder());
147 int status = result.status;
Nathan Harolda10003d2017-08-23 13:46:33 -0700148 checkResultStatus(status);
Nathan Harold8dc1fd02017-04-04 19:37:48 -0700149 mResourceId = result.resourceId;
Nathan Harold330e1082017-01-12 18:38:57 -0800150
Nathan Harold93962f32017-03-07 13:23:36 -0800151 /* Keepalive will silently fail if not needed by the config; but, if needed and
152 * it fails to start, we need to bail because a transform will not be reliable
153 * to use if keepalive is expected to offload and fails.
154 */
155 // FIXME: if keepalive fails, we need to fail spectacularly
156 startKeepalive(mContext);
157 Log.d(TAG, "Added Transform with Id " + mResourceId);
158 mCloseGuard.open("build");
159 } catch (RemoteException e) {
160 throw e.rethrowAsRuntimeException();
Nathan Harold330e1082017-01-12 18:38:57 -0800161 }
Nathan Harold330e1082017-01-12 18:38:57 -0800162 }
Nathan Harold330e1082017-01-12 18:38:57 -0800163
164 return this;
165 }
166
167 /**
168 * Deactivate an IpSecTransform and free all resources for that transform that are managed by
169 * the system for this Transform.
170 *
171 * <p>Deactivating a transform while it is still applied to any Socket will result in sockets
172 * refusing to send or receive data. This method will silently succeed if the specified
173 * transform has already been removed; thus, it is always safe to attempt cleanup when a
174 * transform is no longer needed.
175 */
176 public void close() {
Nathan Harold93962f32017-03-07 13:23:36 -0800177 Log.d(TAG, "Removing Transform with Id " + mResourceId);
Nathan Harold330e1082017-01-12 18:38:57 -0800178
179 // Always safe to attempt cleanup
Nathan Harold93962f32017-03-07 13:23:36 -0800180 if (mResourceId == INVALID_RESOURCE_ID) {
181 mCloseGuard.close();
Nathan Harold330e1082017-01-12 18:38:57 -0800182 return;
183 }
Nathan Harold93962f32017-03-07 13:23:36 -0800184 try {
185 /* Order matters here because the keepalive is best-effort but could fail in some
186 * horrible way to be removed if the wifi (or cell) subsystem has crashed, and we
187 * still want to clear out the transform.
188 */
189 IIpSecService svc = getIpSecService();
190 svc.deleteTransportModeTransform(mResourceId);
191 stopKeepalive();
192 } catch (RemoteException e) {
193 throw e.rethrowAsRuntimeException();
194 } finally {
195 mResourceId = INVALID_RESOURCE_ID;
196 mCloseGuard.close();
197 }
Nathan Harold330e1082017-01-12 18:38:57 -0800198 }
199
200 @Override
201 protected void finalize() throws Throwable {
202 if (mCloseGuard != null) {
203 mCloseGuard.warnIfOpen();
204 }
205 close();
206 }
207
208 /* Package */
209 IpSecConfig getConfig() {
210 return mConfig;
211 }
212
213 private final IpSecConfig mConfig;
Nathan Harold93962f32017-03-07 13:23:36 -0800214 private int mResourceId;
Nathan Harold330e1082017-01-12 18:38:57 -0800215 private final Context mContext;
216 private final CloseGuard mCloseGuard = CloseGuard.get();
217 private ConnectivityManager.PacketKeepalive mKeepalive;
218 private int mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
219 private Object mKeepaliveSyncLock = new Object();
220 private ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
221 new ConnectivityManager.PacketKeepaliveCallback() {
222
223 @Override
224 public void onStarted() {
225 synchronized (mKeepaliveSyncLock) {
226 mKeepaliveStatus = ConnectivityManager.PacketKeepalive.SUCCESS;
227 mKeepaliveSyncLock.notifyAll();
228 }
229 }
230
231 @Override
232 public void onStopped() {
233 synchronized (mKeepaliveSyncLock) {
234 mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
235 mKeepaliveSyncLock.notifyAll();
236 }
237 }
238
239 @Override
240 public void onError(int error) {
241 synchronized (mKeepaliveSyncLock) {
242 mKeepaliveStatus = error;
243 mKeepaliveSyncLock.notifyAll();
244 }
245 }
246 };
247
248 /* Package */
249 void startKeepalive(Context c) {
Nathan Harolda10003d2017-08-23 13:46:33 -0700250 if (mConfig.getNattKeepaliveInterval() != 0) {
251 Log.wtf(TAG, "Keepalive not yet supported.");
Nathan Harold330e1082017-01-12 18:38:57 -0800252 }
253 }
254
Nathan Harolda10003d2017-08-23 13:46:33 -0700255 /** @hide */
256 @VisibleForTesting
257 public int getResourceId() {
Nathan Harold93962f32017-03-07 13:23:36 -0800258 return mResourceId;
259 }
260
261 /* Package */
Nathan Harold330e1082017-01-12 18:38:57 -0800262 void stopKeepalive() {
Nathan Harolda10003d2017-08-23 13:46:33 -0700263 return;
Nathan Harold330e1082017-01-12 18:38:57 -0800264 }
265
Nathan Harold330e1082017-01-12 18:38:57 -0800266 /**
267 * Builder object to facilitate the creation of IpSecTransform objects.
268 *
269 * <p>Apply additional properties to the transform and then call a build() method to return an
270 * IpSecTransform object.
271 *
272 * @see Builder#buildTransportModeTransform(InetAddress)
273 */
274 public static class Builder {
275 private Context mContext;
276 private IpSecConfig mConfig;
277
278 /**
279 * Add an encryption algorithm to the transform for the given direction.
280 *
281 * <p>If encryption is set for a given direction without also providing an SPI for that
282 * direction, creation of an IpSecTransform will fail upon calling a build() method.
283 *
Benedict Wong0febe5e2017-08-22 21:42:33 -0700284 * <p>Authenticated encryption is mutually exclusive with encryption and authentication.
285 *
Nathan Harold330e1082017-01-12 18:38:57 -0800286 * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
287 * @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
288 */
289 public IpSecTransform.Builder setEncryption(
290 @TransformDirection int direction, IpSecAlgorithm algo) {
Nathan Harolda10003d2017-08-23 13:46:33 -0700291 mConfig.setEncryption(direction, algo);
Nathan Harold330e1082017-01-12 18:38:57 -0800292 return this;
293 }
294
295 /**
296 * Add an authentication/integrity algorithm to the transform.
297 *
298 * <p>If authentication is set for a given direction without also providing an SPI for that
299 * direction, creation of an IpSecTransform will fail upon calling a build() method.
300 *
Benedict Wong0febe5e2017-08-22 21:42:33 -0700301 * <p>Authenticated encryption is mutually exclusive with encryption and authentication.
302 *
Nathan Harold330e1082017-01-12 18:38:57 -0800303 * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
304 * @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
305 */
306 public IpSecTransform.Builder setAuthentication(
307 @TransformDirection int direction, IpSecAlgorithm algo) {
Nathan Harolda10003d2017-08-23 13:46:33 -0700308 mConfig.setAuthentication(direction, algo);
Nathan Harold330e1082017-01-12 18:38:57 -0800309 return this;
310 }
311
312 /**
Benedict Wong0febe5e2017-08-22 21:42:33 -0700313 * Add an authenticated encryption algorithm to the transform for the given direction.
314 *
315 * <p>If an authenticated encryption algorithm is set for a given direction without also
316 * providing an SPI for that direction, creation of an IpSecTransform will fail upon calling
317 * a build() method.
318 *
319 * <p>The Authenticated Encryption (AE) class of algorithms are also known as Authenticated
320 * Encryption with Associated Data (AEAD) algorithms, or Combined mode algorithms (as
321 * referred to in RFC 4301)
322 *
323 * <p>Authenticated encryption is mutually exclusive with encryption and authentication.
324 *
325 * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
326 * @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
327 * be applied.
328 */
329 public IpSecTransform.Builder setAuthenticatedEncryption(
330 @TransformDirection int direction, IpSecAlgorithm algo) {
331 mConfig.setAuthenticatedEncryption(direction, algo);
332 return this;
333 }
334
335 /**
Nathan Harold330e1082017-01-12 18:38:57 -0800336 * Set the SPI, which uniquely identifies a particular IPsec session from others. Because
337 * IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a
338 * given destination address.
339 *
340 * <p>Care should be chosen when selecting an SPI to ensure that is is as unique as
Nathan Harold48b56652017-03-30 11:01:37 -0700341 * possible. To reserve a value call {@link IpSecManager#reserveSecurityParameterIndex(int,
342 * InetAddress, int)}. Otherwise, SPI collisions would prevent a transform from being
343 * activated. IpSecManager#reserveSecurityParameterIndex(int, InetAddres$s, int)}.
Nathan Harold330e1082017-01-12 18:38:57 -0800344 *
345 * <p>Unless an SPI is set for a given direction, traffic in that direction will be
346 * sent/received without any IPsec applied.
347 *
348 * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
349 * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
350 * traffic
351 */
352 public IpSecTransform.Builder setSpi(
353 @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
Nathan Harolda10003d2017-08-23 13:46:33 -0700354 mConfig.setSpiResourceId(direction, spi.getResourceId());
Nathan Harold330e1082017-01-12 18:38:57 -0800355 return this;
356 }
357
358 /**
359 * Specify the network on which this transform will emit its traffic; (otherwise it will
360 * emit on the default network).
361 *
362 * <p>Restricts the transformed traffic to a particular {@link Network}. This is required in
363 * tunnel mode.
364 *
365 * @hide
366 */
367 @SystemApi
368 public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
Nathan Harolda10003d2017-08-23 13:46:33 -0700369 mConfig.setNetwork(net);
Nathan Harold330e1082017-01-12 18:38:57 -0800370 return this;
371 }
372
373 /**
374 * Add UDP encapsulation to an IPv4 transform
375 *
376 * <p>This option allows IPsec traffic to pass through NAT. Refer to RFC 3947 and 3948 for
377 * details on how UDP should be applied to IPsec.
378 *
379 * @param localSocket a {@link IpSecManager.UdpEncapsulationSocket} for sending and
380 * receiving encapsulating traffic.
381 * @param remotePort the UDP port number of the remote that will send and receive
382 * encapsulated traffic. In the case of IKE, this is likely port 4500.
383 */
384 public IpSecTransform.Builder setIpv4Encapsulation(
385 IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
Nathan Harolda10003d2017-08-23 13:46:33 -0700386 mConfig.setEncapType(ENCAP_ESPINUDP);
387 mConfig.setEncapSocketResourceId(localSocket.getResourceId());
388 mConfig.setEncapRemotePort(remotePort);
Nathan Harold330e1082017-01-12 18:38:57 -0800389 return this;
390 }
391
392 // TODO: Decrease the minimum keepalive to maybe 10?
393 // TODO: Probably a better exception to throw for NATTKeepalive failure
394 // TODO: Specify the needed NATT keepalive permission.
395 /**
396 * Send a NATT Keepalive packet with a given maximum interval. This will create an offloaded
397 * request to do power-efficient NATT Keepalive. If NATT keepalive is requested but cannot
398 * be activated, then the transform will fail to activate and throw an IOException.
399 *
400 * @param intervalSeconds the maximum number of seconds between keepalive packets, no less
401 * than 20s and no more than 3600s.
402 * @hide
403 */
404 @SystemApi
405 public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
Nathan Harolda10003d2017-08-23 13:46:33 -0700406 mConfig.setNattKeepaliveInterval(intervalSeconds);
Nathan Harold330e1082017-01-12 18:38:57 -0800407 return this;
408 }
409
410 /**
411 * Build and return an active {@link IpSecTransform} object as a Transport Mode Transform.
412 * Some parameters have interdependencies that are checked at build time. If a well-formed
413 * transform cannot be created from the supplied parameters, this method will throw an
414 * Exception.
415 *
416 * <p>Upon a successful return from this call, the provided IpSecTransform will be active
417 * and may be applied to sockets. If too many IpSecTransform objects are active for a given
418 * user this operation will fail and throw ResourceUnavailableException. To avoid these
419 * exceptions, unused Transform objects must be cleaned up by calling {@link
420 * IpSecTransform#close()} when they are no longer needed.
421 *
422 * @param remoteAddress the {@link InetAddress} that, when matched on traffic to/from this
423 * socket will cause the transform to be applied.
424 * <p>Note that an active transform will not impact any network traffic until it has
425 * been applied to one or more Sockets. Calling this method is a necessary precondition
426 * for applying it to a socket, but is not sufficient to actually apply IPsec.
427 * @throws IllegalArgumentException indicating that a particular combination of transform
428 * properties is invalid.
429 * @throws IpSecManager.ResourceUnavailableException in the event that no more Transforms
430 * may be allocated
431 * @throws SpiUnavailableException if the SPI collides with an existing transform
432 * (unlikely).
433 * @throws ResourceUnavailableException if the current user currently has exceeded the
434 * number of allowed active transforms.
435 */
436 public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
437 throws IpSecManager.ResourceUnavailableException,
438 IpSecManager.SpiUnavailableException, IOException {
Nathan Harolda10003d2017-08-23 13:46:33 -0700439 mConfig.setMode(MODE_TRANSPORT);
440 mConfig.setRemoteAddress(remoteAddress.getHostAddress());
Nathan Harold330e1082017-01-12 18:38:57 -0800441 return new IpSecTransform(mContext, mConfig).activate();
442 }
443
444 /**
445 * Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
446 * parameters have interdependencies that are checked at build time.
447 *
448 * @param localAddress the {@link InetAddress} that provides the local endpoint for this
449 * IPsec tunnel. This is almost certainly an address belonging to the {@link Network}
450 * that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}.
451 * @param remoteAddress the {@link InetAddress} representing the remote endpoint of this
452 * IPsec tunnel.
453 * @throws IllegalArgumentException indicating that a particular combination of transform
454 * properties is invalid.
455 * @hide
456 */
Nathan Harold330e1082017-01-12 18:38:57 -0800457 public IpSecTransform buildTunnelModeTransform(
458 InetAddress localAddress, InetAddress remoteAddress) {
459 //FIXME: argument validation here
460 //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
Nathan Harolda10003d2017-08-23 13:46:33 -0700461 mConfig.setLocalAddress(localAddress.getHostAddress());
462 mConfig.setRemoteAddress(remoteAddress.getHostAddress());
463 mConfig.setMode(MODE_TUNNEL);
Nathan Harold330e1082017-01-12 18:38:57 -0800464 return new IpSecTransform(mContext, mConfig);
465 }
466
467 /**
468 * Create a new IpSecTransform.Builder to construct an IpSecTransform
469 *
470 * @param context current Context
471 */
Nathan Harold93962f32017-03-07 13:23:36 -0800472 public Builder(@NonNull Context context) {
473 Preconditions.checkNotNull(context);
Nathan Harold330e1082017-01-12 18:38:57 -0800474 mContext = context;
475 mConfig = new IpSecConfig();
476 }
Nathan Harold330e1082017-01-12 18:38:57 -0800477 }
478}