1 /* 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 package android.net; 17 18 import static android.net.IpSecManager.INVALID_RESOURCE_ID; 19 import static android.net.IpSecManager.KEY_RESOURCE_ID; 20 import static android.net.IpSecManager.KEY_STATUS; 21 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.SystemApi; 25 import android.content.Context; 26 import android.os.Binder; 27 import android.os.Bundle; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.os.ServiceManager; 31 import android.util.Log; 32 import com.android.internal.util.Preconditions; 33 import dalvik.system.CloseGuard; 34 import java.io.IOException; 35 import java.lang.annotation.Retention; 36 import java.lang.annotation.RetentionPolicy; 37 import java.net.InetAddress; 38 39 /** 40 * This class represents an IpSecTransform, which encapsulates both properties and state of IPsec. 41 * 42 * <p>IpSecTransforms must be built from an IpSecTransform.Builder, and they must persist throughout 43 * the lifetime of the underlying transform. If a transform object leaves scope, the underlying 44 * transform may be disabled automatically, with likely undesirable results. 45 * 46 * <p>An IpSecTransform may either represent a tunnel mode transform that operates on a wide array 47 * of traffic or may represent a transport mode transform operating on a Socket or Sockets. 48 * 49 * @hide 50 */ 51 public final class IpSecTransform implements AutoCloseable { 52 private static final String TAG = "IpSecTransform"; 53 54 /** 55 * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies 56 * to traffic towards the host. 57 */ 58 public static final int DIRECTION_IN = 0; 59 60 /** 61 * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies 62 * to traffic from the host. 63 */ 64 public static final int DIRECTION_OUT = 1; 65 66 /** @hide */ 67 @IntDef(value = {DIRECTION_IN, DIRECTION_OUT}) 68 @Retention(RetentionPolicy.SOURCE) 69 public @interface TransformDirection {} 70 71 /** @hide */ 72 private static final int MODE_TUNNEL = 0; 73 74 /** @hide */ 75 private static final int MODE_TRANSPORT = 1; 76 77 /** @hide */ 78 public static final int ENCAP_NONE = 0; 79 80 /** 81 * IpSec traffic will be encapsulated within UDP as per <a 82 * href="https://tools.ietf.org/html/rfc3948">RFC3498</a>. 83 * 84 * @hide 85 */ 86 public static final int ENCAP_ESPINUDP = 1; 87 88 /** 89 * IpSec traffic will be encapsulated within a UDP header with an additional 8-byte header pad 90 * (of '0'-value bytes) that prevents traffic from being interpreted as IKE or as ESP over UDP. 91 * 92 * @hide 93 */ 94 public static final int ENCAP_ESPINUDP_NONIKE = 2; 95 96 /** @hide */ 97 @IntDef(value = {ENCAP_NONE, ENCAP_ESPINUDP, ENCAP_ESPINUDP_NONIKE}) 98 @Retention(RetentionPolicy.SOURCE) 99 public @interface EncapType {} 100 IpSecTransform(Context context, IpSecConfig config)101 private IpSecTransform(Context context, IpSecConfig config) { 102 mContext = context; 103 mConfig = config; 104 mResourceId = INVALID_RESOURCE_ID; 105 } 106 getIpSecService()107 private IIpSecService getIpSecService() { 108 IBinder b = ServiceManager.getService(android.content.Context.IPSEC_SERVICE); 109 if (b == null) { 110 throw new RemoteException("Failed to connect to IpSecService") 111 .rethrowAsRuntimeException(); 112 } 113 114 return IIpSecService.Stub.asInterface(b); 115 } 116 checkResultStatusAndThrow(int status)117 private void checkResultStatusAndThrow(int status) 118 throws IOException, IpSecManager.ResourceUnavailableException, 119 IpSecManager.SpiUnavailableException { 120 switch (status) { 121 case IpSecManager.Status.OK: 122 return; 123 // TODO: Pass Error string back from bundle so that errors can be more specific 124 case IpSecManager.Status.RESOURCE_UNAVAILABLE: 125 throw new IpSecManager.ResourceUnavailableException( 126 "Failed to allocate a new IpSecTransform"); 127 case IpSecManager.Status.SPI_UNAVAILABLE: 128 Log.wtf(TAG, "Attempting to use an SPI that was somehow not reserved"); 129 // Fall through 130 default: 131 throw new IllegalStateException( 132 "Failed to Create a Transform with status code " + status); 133 } 134 } 135 activate()136 private IpSecTransform activate() 137 throws IOException, IpSecManager.ResourceUnavailableException, 138 IpSecManager.SpiUnavailableException { 139 synchronized (this) { 140 try { 141 IIpSecService svc = getIpSecService(); 142 Bundle result = svc.createTransportModeTransform(mConfig, new Binder()); 143 int status = result.getInt(KEY_STATUS); 144 checkResultStatusAndThrow(status); 145 mResourceId = result.getInt(KEY_RESOURCE_ID, INVALID_RESOURCE_ID); 146 147 /* Keepalive will silently fail if not needed by the config; but, if needed and 148 * it fails to start, we need to bail because a transform will not be reliable 149 * to use if keepalive is expected to offload and fails. 150 */ 151 // FIXME: if keepalive fails, we need to fail spectacularly 152 startKeepalive(mContext); 153 Log.d(TAG, "Added Transform with Id " + mResourceId); 154 mCloseGuard.open("build"); 155 } catch (RemoteException e) { 156 throw e.rethrowAsRuntimeException(); 157 } 158 } 159 160 return this; 161 } 162 163 /** 164 * Deactivate an IpSecTransform and free all resources for that transform that are managed by 165 * the system for this Transform. 166 * 167 * <p>Deactivating a transform while it is still applied to any Socket will result in sockets 168 * refusing to send or receive data. This method will silently succeed if the specified 169 * transform has already been removed; thus, it is always safe to attempt cleanup when a 170 * transform is no longer needed. 171 */ close()172 public void close() { 173 Log.d(TAG, "Removing Transform with Id " + mResourceId); 174 175 // Always safe to attempt cleanup 176 if (mResourceId == INVALID_RESOURCE_ID) { 177 mCloseGuard.close(); 178 return; 179 } 180 try { 181 /* Order matters here because the keepalive is best-effort but could fail in some 182 * horrible way to be removed if the wifi (or cell) subsystem has crashed, and we 183 * still want to clear out the transform. 184 */ 185 IIpSecService svc = getIpSecService(); 186 svc.deleteTransportModeTransform(mResourceId); 187 stopKeepalive(); 188 } catch (RemoteException e) { 189 throw e.rethrowAsRuntimeException(); 190 } finally { 191 mResourceId = INVALID_RESOURCE_ID; 192 mCloseGuard.close(); 193 } 194 } 195 196 @Override finalize()197 protected void finalize() throws Throwable { 198 if (mCloseGuard != null) { 199 mCloseGuard.warnIfOpen(); 200 } 201 close(); 202 } 203 204 /* Package */ getConfig()205 IpSecConfig getConfig() { 206 return mConfig; 207 } 208 209 private final IpSecConfig mConfig; 210 private int mResourceId; 211 private final Context mContext; 212 private final CloseGuard mCloseGuard = CloseGuard.get(); 213 private ConnectivityManager.PacketKeepalive mKeepalive; 214 private int mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE; 215 private Object mKeepaliveSyncLock = new Object(); 216 private ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback = 217 new ConnectivityManager.PacketKeepaliveCallback() { 218 219 @Override 220 public void onStarted() { 221 synchronized (mKeepaliveSyncLock) { 222 mKeepaliveStatus = ConnectivityManager.PacketKeepalive.SUCCESS; 223 mKeepaliveSyncLock.notifyAll(); 224 } 225 } 226 227 @Override 228 public void onStopped() { 229 synchronized (mKeepaliveSyncLock) { 230 mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE; 231 mKeepaliveSyncLock.notifyAll(); 232 } 233 } 234 235 @Override 236 public void onError(int error) { 237 synchronized (mKeepaliveSyncLock) { 238 mKeepaliveStatus = error; 239 mKeepaliveSyncLock.notifyAll(); 240 } 241 } 242 }; 243 244 /* Package */ startKeepalive(Context c)245 void startKeepalive(Context c) { 246 // FIXME: NO_KEEPALIVE needs to be a constant 247 if (mConfig.getNattKeepaliveInterval() == 0) { 248 return; 249 } 250 251 ConnectivityManager cm = 252 (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE); 253 254 if (mKeepalive != null) { 255 Log.wtf(TAG, "Keepalive already started for this IpSecTransform."); 256 return; 257 } 258 259 synchronized (mKeepaliveSyncLock) { 260 mKeepalive = 261 cm.startNattKeepalive( 262 mConfig.getNetwork(), 263 mConfig.getNattKeepaliveInterval(), 264 mKeepaliveCallback, 265 mConfig.getLocalAddress(), 266 mConfig.getEncapLocalPort(), 267 mConfig.getRemoteAddress()); 268 try { 269 // FIXME: this is still a horrible way to fudge the synchronous callback 270 mKeepaliveSyncLock.wait(2000); 271 } catch (InterruptedException e) { 272 } 273 } 274 if (mKeepaliveStatus != ConnectivityManager.PacketKeepalive.SUCCESS) { 275 throw new UnsupportedOperationException("Packet Keepalive cannot be started"); 276 } 277 } 278 279 /* Package */ getResourceId()280 int getResourceId() { 281 return mResourceId; 282 } 283 284 /* Package */ stopKeepalive()285 void stopKeepalive() { 286 if (mKeepalive == null) { 287 return; 288 } 289 mKeepalive.stop(); 290 synchronized (mKeepaliveSyncLock) { 291 if (mKeepaliveStatus == ConnectivityManager.PacketKeepalive.SUCCESS) { 292 try { 293 mKeepaliveSyncLock.wait(2000); 294 } catch (InterruptedException e) { 295 } 296 } 297 } 298 } 299 300 /** 301 * Builder object to facilitate the creation of IpSecTransform objects. 302 * 303 * <p>Apply additional properties to the transform and then call a build() method to return an 304 * IpSecTransform object. 305 * 306 * @see Builder#buildTransportModeTransform(InetAddress) 307 */ 308 public static class Builder { 309 private Context mContext; 310 private IpSecConfig mConfig; 311 312 /** 313 * Add an encryption algorithm to the transform for the given direction. 314 * 315 * <p>If encryption is set for a given direction without also providing an SPI for that 316 * direction, creation of an IpSecTransform will fail upon calling a build() method. 317 * 318 * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT} 319 * @param algo {@link IpSecAlgorithm} specifying the encryption to be applied. 320 */ setEncryption( @ransformDirection int direction, IpSecAlgorithm algo)321 public IpSecTransform.Builder setEncryption( 322 @TransformDirection int direction, IpSecAlgorithm algo) { 323 mConfig.flow[direction].encryption = algo; 324 return this; 325 } 326 327 /** 328 * Add an authentication/integrity algorithm to the transform. 329 * 330 * <p>If authentication is set for a given direction without also providing an SPI for that 331 * direction, creation of an IpSecTransform will fail upon calling a build() method. 332 * 333 * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT} 334 * @param algo {@link IpSecAlgorithm} specifying the authentication to be applied. 335 */ setAuthentication( @ransformDirection int direction, IpSecAlgorithm algo)336 public IpSecTransform.Builder setAuthentication( 337 @TransformDirection int direction, IpSecAlgorithm algo) { 338 mConfig.flow[direction].authentication = algo; 339 return this; 340 } 341 342 /** 343 * Set the SPI, which uniquely identifies a particular IPsec session from others. Because 344 * IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a 345 * given destination address. 346 * 347 * <p>Care should be chosen when selecting an SPI to ensure that is is as unique as 348 * possible. To reserve a value call {@link IpSecManager#reserveSecurityParameterIndex(int, 349 * InetAddress, int)}. Otherwise, SPI collisions would prevent a transform from being 350 * activated. IpSecManager#reserveSecurityParameterIndex(int, InetAddres$s, int)}. 351 * 352 * <p>Unless an SPI is set for a given direction, traffic in that direction will be 353 * sent/received without any IPsec applied. 354 * 355 * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT} 356 * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed 357 * traffic 358 */ setSpi( @ransformDirection int direction, IpSecManager.SecurityParameterIndex spi)359 public IpSecTransform.Builder setSpi( 360 @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) { 361 // TODO: convert to using the resource Id of the SPI. Then build() can validate 362 // the owner in the IpSecService 363 mConfig.flow[direction].spi = spi.getSpi(); 364 return this; 365 } 366 367 /** 368 * Specify the network on which this transform will emit its traffic; (otherwise it will 369 * emit on the default network). 370 * 371 * <p>Restricts the transformed traffic to a particular {@link Network}. This is required in 372 * tunnel mode. 373 * 374 * @hide 375 */ 376 @SystemApi setUnderlyingNetwork(Network net)377 public IpSecTransform.Builder setUnderlyingNetwork(Network net) { 378 mConfig.network = net; 379 return this; 380 } 381 382 /** 383 * Add UDP encapsulation to an IPv4 transform 384 * 385 * <p>This option allows IPsec traffic to pass through NAT. Refer to RFC 3947 and 3948 for 386 * details on how UDP should be applied to IPsec. 387 * 388 * @param localSocket a {@link IpSecManager.UdpEncapsulationSocket} for sending and 389 * receiving encapsulating traffic. 390 * @param remotePort the UDP port number of the remote that will send and receive 391 * encapsulated traffic. In the case of IKE, this is likely port 4500. 392 */ setIpv4Encapsulation( IpSecManager.UdpEncapsulationSocket localSocket, int remotePort)393 public IpSecTransform.Builder setIpv4Encapsulation( 394 IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) { 395 // TODO: check encap type is valid. 396 mConfig.encapType = ENCAP_ESPINUDP; 397 mConfig.encapLocalPort = localSocket.getPort(); // TODO: plug in the encap socket 398 mConfig.encapRemotePort = remotePort; 399 return this; 400 } 401 402 // TODO: Decrease the minimum keepalive to maybe 10? 403 // TODO: Probably a better exception to throw for NATTKeepalive failure 404 // TODO: Specify the needed NATT keepalive permission. 405 /** 406 * Send a NATT Keepalive packet with a given maximum interval. This will create an offloaded 407 * request to do power-efficient NATT Keepalive. If NATT keepalive is requested but cannot 408 * be activated, then the transform will fail to activate and throw an IOException. 409 * 410 * @param intervalSeconds the maximum number of seconds between keepalive packets, no less 411 * than 20s and no more than 3600s. 412 * @hide 413 */ 414 @SystemApi setNattKeepalive(int intervalSeconds)415 public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) { 416 mConfig.nattKeepaliveInterval = intervalSeconds; 417 return this; 418 } 419 420 /** 421 * Build and return an active {@link IpSecTransform} object as a Transport Mode Transform. 422 * Some parameters have interdependencies that are checked at build time. If a well-formed 423 * transform cannot be created from the supplied parameters, this method will throw an 424 * Exception. 425 * 426 * <p>Upon a successful return from this call, the provided IpSecTransform will be active 427 * and may be applied to sockets. If too many IpSecTransform objects are active for a given 428 * user this operation will fail and throw ResourceUnavailableException. To avoid these 429 * exceptions, unused Transform objects must be cleaned up by calling {@link 430 * IpSecTransform#close()} when they are no longer needed. 431 * 432 * @param remoteAddress the {@link InetAddress} that, when matched on traffic to/from this 433 * socket will cause the transform to be applied. 434 * <p>Note that an active transform will not impact any network traffic until it has 435 * been applied to one or more Sockets. Calling this method is a necessary precondition 436 * for applying it to a socket, but is not sufficient to actually apply IPsec. 437 * @throws IllegalArgumentException indicating that a particular combination of transform 438 * properties is invalid. 439 * @throws IpSecManager.ResourceUnavailableException in the event that no more Transforms 440 * may be allocated 441 * @throws SpiUnavailableException if the SPI collides with an existing transform 442 * (unlikely). 443 * @throws ResourceUnavailableException if the current user currently has exceeded the 444 * number of allowed active transforms. 445 */ buildTransportModeTransform(InetAddress remoteAddress)446 public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress) 447 throws IpSecManager.ResourceUnavailableException, 448 IpSecManager.SpiUnavailableException, IOException { 449 //FIXME: argument validation here 450 //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation"); 451 mConfig.mode = MODE_TRANSPORT; 452 mConfig.remoteAddress = remoteAddress; 453 return new IpSecTransform(mContext, mConfig).activate(); 454 } 455 456 /** 457 * Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some 458 * parameters have interdependencies that are checked at build time. 459 * 460 * @param localAddress the {@link InetAddress} that provides the local endpoint for this 461 * IPsec tunnel. This is almost certainly an address belonging to the {@link Network} 462 * that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}. 463 * @param remoteAddress the {@link InetAddress} representing the remote endpoint of this 464 * IPsec tunnel. 465 * @throws IllegalArgumentException indicating that a particular combination of transform 466 * properties is invalid. 467 * @hide 468 */ buildTunnelModeTransform( InetAddress localAddress, InetAddress remoteAddress)469 public IpSecTransform buildTunnelModeTransform( 470 InetAddress localAddress, InetAddress remoteAddress) { 471 //FIXME: argument validation here 472 //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation"); 473 mConfig.localAddress = localAddress; 474 mConfig.remoteAddress = remoteAddress; 475 mConfig.mode = MODE_TUNNEL; 476 return new IpSecTransform(mContext, mConfig); 477 } 478 479 /** 480 * Create a new IpSecTransform.Builder to construct an IpSecTransform 481 * 482 * @param context current Context 483 */ Builder(@onNull Context context)484 public Builder(@NonNull Context context) { 485 Preconditions.checkNotNull(context); 486 mContext = context; 487 mConfig = new IpSecConfig(); 488 } 489 } 490 } 491