IpSecManager.java revision 09098dc441913f30905f6ffd6f73262924858dd0
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 */ 16package android.net; 17 18import static com.android.internal.util.Preconditions.checkNotNull; 19 20import android.annotation.NonNull; 21import android.os.Binder; 22import android.os.Bundle; 23import android.os.ParcelFileDescriptor; 24import android.os.RemoteException; 25import android.util.AndroidException; 26import dalvik.system.CloseGuard; 27import java.io.FileDescriptor; 28import java.io.IOException; 29import java.net.DatagramSocket; 30import java.net.InetAddress; 31import java.net.Socket; 32 33/** 34 * This class contains methods for managing IPsec sessions, which will perform kernel-space 35 * encryption and decryption of socket or Network traffic. 36 * 37 * <p>An IpSecManager may be obtained by calling {@link 38 * android.content.Context#getSystemService(String) Context#getSystemService(String)} with {@link 39 * android.content.Context#IPSEC_SERVICE Context#IPSEC_SERVICE} 40 */ 41public final class IpSecManager { 42 private static final String TAG = "IpSecManager"; 43 44 /** 45 * The Security Parameter Index, SPI, 0 indicates an unknown or invalid index. 46 * 47 * <p>No IPsec packet may contain an SPI of 0. 48 */ 49 public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; 50 51 /** @hide */ 52 public interface Status { 53 public static final int OK = 0; 54 public static final int RESOURCE_UNAVAILABLE = 1; 55 public static final int SPI_UNAVAILABLE = 2; 56 } 57 58 /** @hide */ 59 public static final String KEY_STATUS = "status"; 60 /** @hide */ 61 public static final String KEY_RESOURCE_ID = "resourceId"; 62 /** @hide */ 63 public static final String KEY_SPI = "spi"; 64 /** @hide */ 65 public static final int INVALID_RESOURCE_ID = 0; 66 67 /** 68 * Indicates that the combination of remote InetAddress and SPI was non-unique for a given 69 * request. If encountered, selection of a new SPI is required before a transform may be 70 * created. Note, this should happen very rarely if the SPI is chosen to be sufficiently random 71 * or reserved using reserveSecurityParameterIndex. 72 */ 73 public static final class SpiUnavailableException extends AndroidException { 74 private final int mSpi; 75 76 /** 77 * Construct an exception indicating that a transform with the given SPI is already in use 78 * or otherwise unavailable. 79 * 80 * @param msg Description indicating the colliding SPI 81 * @param spi the SPI that could not be used due to a collision 82 */ 83 SpiUnavailableException(String msg, int spi) { 84 super(msg + "(spi: " + spi + ")"); 85 mSpi = spi; 86 } 87 88 /** Retrieve the SPI that caused a collision */ 89 public int getSpi() { 90 return mSpi; 91 } 92 } 93 94 /** 95 * Indicates that the requested system resource for IPsec, such as a socket or other system 96 * resource is unavailable. If this exception is thrown, try releasing allocated objects of the 97 * type requested. 98 */ 99 public static final class ResourceUnavailableException extends AndroidException { 100 101 ResourceUnavailableException(String msg) { 102 super(msg); 103 } 104 } 105 106 private final IIpSecService mService; 107 108 public static final class SecurityParameterIndex implements AutoCloseable { 109 private final IIpSecService mService; 110 private final InetAddress mRemoteAddress; 111 private final CloseGuard mCloseGuard = CloseGuard.get(); 112 private int mSpi = INVALID_SECURITY_PARAMETER_INDEX; 113 private int mResourceId; 114 115 /** Return the underlying SPI held by this object */ 116 public int getSpi() { 117 return mSpi; 118 } 119 120 /** 121 * Release an SPI that was previously reserved. 122 * 123 * <p>Release an SPI for use by other users in the system. If a SecurityParameterIndex is 124 * applied to an IpSecTransform, it will become unusable for future transforms but should 125 * still be closed to ensure system resources are released. 126 */ 127 @Override 128 public void close() { 129 mSpi = INVALID_SECURITY_PARAMETER_INDEX; 130 mCloseGuard.close(); 131 } 132 133 @Override 134 protected void finalize() { 135 if (mCloseGuard != null) { 136 mCloseGuard.warnIfOpen(); 137 } 138 139 close(); 140 } 141 142 private SecurityParameterIndex( 143 @NonNull IIpSecService service, int direction, InetAddress remoteAddress, int spi) 144 throws ResourceUnavailableException, SpiUnavailableException { 145 mService = service; 146 mRemoteAddress = remoteAddress; 147 try { 148 Bundle result = 149 mService.reserveSecurityParameterIndex( 150 direction, remoteAddress.getHostAddress(), spi, new Binder()); 151 152 if (result == null) { 153 throw new NullPointerException("Received null response from IpSecService"); 154 } 155 156 int status = result.getInt(KEY_STATUS); 157 switch (status) { 158 case Status.OK: 159 break; 160 case Status.RESOURCE_UNAVAILABLE: 161 throw new ResourceUnavailableException( 162 "No more SPIs may be allocated by this requester."); 163 case Status.SPI_UNAVAILABLE: 164 throw new SpiUnavailableException("Requested SPI is unavailable", spi); 165 default: 166 throw new RuntimeException( 167 "Unknown status returned by IpSecService: " + status); 168 } 169 mSpi = result.getInt(KEY_SPI); 170 mResourceId = result.getInt(KEY_RESOURCE_ID); 171 172 if (mSpi == INVALID_SECURITY_PARAMETER_INDEX) { 173 throw new RuntimeException("Invalid SPI returned by IpSecService: " + status); 174 } 175 176 if (mResourceId == INVALID_RESOURCE_ID) { 177 throw new RuntimeException( 178 "Invalid Resource ID returned by IpSecService: " + status); 179 } 180 181 } catch (RemoteException e) { 182 throw e.rethrowFromSystemServer(); 183 } 184 mCloseGuard.open("open"); 185 } 186 } 187 188 /** 189 * Reserve an SPI for traffic bound towards the specified remote address. 190 * 191 * <p>If successful, this SPI is guaranteed available until released by a call to {@link 192 * SecurityParameterIndex#close()}. 193 * 194 * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT} 195 * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress. 196 * @return the reserved SecurityParameterIndex 197 * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated 198 * for this user 199 * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved 200 */ 201 public SecurityParameterIndex reserveSecurityParameterIndex( 202 int direction, InetAddress remoteAddress) 203 throws ResourceUnavailableException { 204 try { 205 return new SecurityParameterIndex( 206 mService, 207 direction, 208 remoteAddress, 209 IpSecManager.INVALID_SECURITY_PARAMETER_INDEX); 210 } catch (SpiUnavailableException unlikely) { 211 throw new ResourceUnavailableException("No SPIs available"); 212 } 213 } 214 215 /** 216 * Reserve an SPI for traffic bound towards the specified remote address. 217 * 218 * <p>If successful, this SPI is guaranteed available until released by a call to {@link 219 * SecurityParameterIndex#close()}. 220 * 221 * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT} 222 * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress. 223 * @param requestedSpi the requested SPI, or '0' to allocate a random SPI. 224 * @return the reserved SecurityParameterIndex 225 * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated 226 * for this user 227 */ 228 public SecurityParameterIndex reserveSecurityParameterIndex( 229 int direction, InetAddress remoteAddress, int requestedSpi) 230 throws SpiUnavailableException, ResourceUnavailableException { 231 if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) { 232 throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI"); 233 } 234 return new SecurityParameterIndex(mService, direction, remoteAddress, requestedSpi); 235 } 236 237 /** 238 * Apply an active Transport Mode IPsec Transform to a stream socket to perform IPsec 239 * encapsulation of the traffic flowing between the socket and the remote InetAddress of that 240 * transform. For security reasons, attempts to send traffic to any IP address other than the 241 * address associated with that transform will throw an IOException. In addition, if the 242 * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to 243 * send() or receive() until the transform is removed from the socket by calling {@link 244 * #removeTransportModeTransform(Socket, IpSecTransform)}; 245 * 246 * @param socket a stream socket 247 * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform. 248 */ 249 public void applyTransportModeTransform(Socket socket, IpSecTransform transform) 250 throws IOException { 251 applyTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform); 252 } 253 254 /** 255 * Apply an active Transport Mode IPsec Transform to a datagram socket to perform IPsec 256 * encapsulation of the traffic flowing between the socket and the remote InetAddress of that 257 * transform. For security reasons, attempts to send traffic to any IP address other than the 258 * address associated with that transform will throw an IOException. In addition, if the 259 * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to 260 * send() or receive() until the transform is removed from the socket by calling {@link 261 * #removeTransportModeTransform(DatagramSocket, IpSecTransform)}; 262 * 263 * @param socket a datagram socket 264 * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform. 265 */ 266 public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform) 267 throws IOException { 268 applyTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform); 269 } 270 271 /* Call down to activate a transform */ 272 private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) { 273 try { 274 mService.applyTransportModeTransform(pfd, transform.getResourceId()); 275 } catch (RemoteException e) { 276 throw e.rethrowFromSystemServer(); 277 } 278 } 279 280 /** 281 * Apply an active Transport Mode IPsec Transform to a stream socket to perform IPsec 282 * encapsulation of the traffic flowing between the socket and the remote InetAddress of that 283 * transform. For security reasons, attempts to send traffic to any IP address other than the 284 * address associated with that transform will throw an IOException. In addition, if the 285 * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to 286 * send() or receive() until the transform is removed from the socket by calling {@link 287 * #removeTransportModeTransform(Socket, IpSecTransform)}; 288 * 289 * @param socket a socket file descriptor 290 * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform. 291 */ 292 public void applyTransportModeTransform(FileDescriptor socket, IpSecTransform transform) 293 throws IOException { 294 applyTransportModeTransform(new ParcelFileDescriptor(socket), transform); 295 } 296 297 /** 298 * Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to 299 * and from that network's interface with IPsec (applies an outer IP header and IPsec Header to 300 * all traffic, and expects an additional IP header and IPsec Header on all inbound traffic). 301 * Applications should probably not use this API directly. Instead, they should use {@link 302 * VpnService} to provide VPN capability in a more generic fashion. 303 * 304 * @param net a {@link Network} that will be tunneled via IP Sec. 305 * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform. 306 * @hide 307 */ 308 public void applyTunnelModeTransform(Network net, IpSecTransform transform) {} 309 310 /** 311 * Remove a transform from a given stream socket. Once removed, traffic on the socket will not 312 * be encypted. This allows sockets that have been used for IPsec to be reclaimed for 313 * communication in the clear in the event socket reuse is desired. This operation will succeed 314 * regardless of the underlying state of a transform. If a transform is removed, communication 315 * on all sockets to which that transform was applied will fail until this method is called. 316 * 317 * @param socket a socket that previously had a transform applied to it. 318 * @param transform the IPsec Transform that was previously applied to the given socket 319 */ 320 public void removeTransportModeTransform(Socket socket, IpSecTransform transform) { 321 removeTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform); 322 } 323 324 /** 325 * Remove a transform from a given datagram socket. Once removed, traffic on the socket will not 326 * be encypted. This allows sockets that have been used for IPsec to be reclaimed for 327 * communication in the clear in the event socket reuse is desired. This operation will succeed 328 * regardless of the underlying state of a transform. If a transform is removed, communication 329 * on all sockets to which that transform was applied will fail until this method is called. 330 * 331 * @param socket a socket that previously had a transform applied to it. 332 * @param transform the IPsec Transform that was previously applied to the given socket 333 */ 334 public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform) { 335 removeTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform); 336 } 337 338 /** 339 * Remove a transform from a given stream socket. Once removed, traffic on the socket will not 340 * be encypted. This allows sockets that have been used for IPsec to be reclaimed for 341 * communication in the clear in the event socket reuse is desired. This operation will succeed 342 * regardless of the underlying state of a transform. If a transform is removed, communication 343 * on all sockets to which that transform was applied will fail until this method is called. 344 * 345 * @param socket a socket file descriptor that previously had a transform applied to it. 346 * @param transform the IPsec Transform that was previously applied to the given socket 347 */ 348 public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform) { 349 removeTransportModeTransform(new ParcelFileDescriptor(socket), transform); 350 } 351 352 /* Call down to activate a transform */ 353 private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) { 354 try { 355 mService.removeTransportModeTransform(pfd, transform.getResourceId()); 356 } catch (RemoteException e) { 357 throw e.rethrowFromSystemServer(); 358 } 359 } 360 361 /** 362 * Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of 363 * cleanup if a tunneled Network experiences a change in default route. The Network will drop 364 * all traffic that cannot be routed to the Tunnel's outbound interface. If that interface is 365 * lost, all traffic will drop. 366 * 367 * @param net a network that currently has transform applied to it. 368 * @param transform a Tunnel Mode IPsec Transform that has been previously applied to the given 369 * network 370 * @hide 371 */ 372 public void removeTunnelModeTransform(Network net, IpSecTransform transform) {} 373 374 /** 375 * Class providing access to a system-provided UDP Encapsulation Socket, which may be used for 376 * IKE signalling as well as for inbound and outbound UDP encapsulated IPsec traffic. 377 * 378 * <p>The socket provided by this class cannot be re-bound or closed via the inner 379 * FileDescriptor. Instead, disposing of this socket requires a call to close(). 380 */ 381 public static final class UdpEncapsulationSocket implements AutoCloseable { 382 private final FileDescriptor mFd; 383 private final IIpSecService mService; 384 private final CloseGuard mCloseGuard = CloseGuard.get(); 385 386 private UdpEncapsulationSocket(@NonNull IIpSecService service, int port) 387 throws ResourceUnavailableException { 388 mService = service; 389 mCloseGuard.open("constructor"); 390 // TODO: go down to the kernel and get a socket on the specified 391 mFd = new FileDescriptor(); 392 } 393 394 private UdpEncapsulationSocket(IIpSecService service) throws ResourceUnavailableException { 395 mService = service; 396 mCloseGuard.open("constructor"); 397 // TODO: go get a random socket on a random port 398 mFd = new FileDescriptor(); 399 } 400 401 /** Access the inner UDP Encapsulation Socket */ 402 public FileDescriptor getSocket() { 403 return mFd; 404 } 405 406 /** Retrieve the port number of the inner encapsulation socket */ 407 public int getPort() { 408 return 0; // TODO get the port number from the Socket; 409 } 410 411 @Override 412 /** 413 * Release the resources that have been reserved for this Socket. 414 * 415 * <p>This method closes the underlying socket, reducing a user's allocated sockets in the 416 * system. This must be done as part of cleanup following use of a socket. Failure to do so 417 * will cause the socket to count against a total allocation limit for IpSec and eventually 418 * fail due to resource limits. 419 * 420 * @param fd a file descriptor previously returned as a UDP Encapsulation socket. 421 */ 422 public void close() { 423 // TODO: Go close the socket 424 mCloseGuard.close(); 425 } 426 427 @Override 428 protected void finalize() throws Throwable { 429 if (mCloseGuard != null) { 430 mCloseGuard.warnIfOpen(); 431 } 432 433 close(); 434 } 435 }; 436 437 /** 438 * Open a socket that is bound to a free UDP port on the system. 439 * 440 * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by 441 * the caller. This provides safe access to a socket on a port that can later be used as a UDP 442 * Encapsulation port. 443 * 444 * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the 445 * socket port. Explicitly opening this port is only necessary if communication is desired on 446 * that port. 447 * 448 * @param port a local UDP port to be reserved for UDP Encapsulation. is provided, then this 449 * method will bind to the specified port or fail. To retrieve the port number, call {@link 450 * android.system.Os#getsockname(FileDescriptor)}. 451 * @return a {@link UdpEncapsulationSocket} that is bound to the requested port for the lifetime 452 * of the object. 453 */ 454 // Returning a socket in this fashion that has been created and bound by the system 455 // is the only safe way to ensure that a socket is both accessible to the user and 456 // safely usable for Encapsulation without allowing a user to possibly unbind from/close 457 // the port, which could potentially impact the traffic of the next user who binds to that 458 // socket. 459 public UdpEncapsulationSocket openUdpEncapsulationSocket(int port) 460 throws IOException, ResourceUnavailableException { 461 // Temporary code 462 return new UdpEncapsulationSocket(mService, port); 463 } 464 465 /** 466 * Open a socket that is bound to a port selected by the system. 467 * 468 * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by 469 * the caller. This provides safe access to a socket on a port that can later be used as a UDP 470 * Encapsulation port. 471 * 472 * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the 473 * socket port. Explicitly opening this port is only necessary if communication is desired on 474 * that port. 475 * 476 * @return a {@link UdpEncapsulationSocket} that is bound to an arbitrarily selected port 477 */ 478 // Returning a socket in this fashion that has been created and bound by the system 479 // is the only safe way to ensure that a socket is both accessible to the user and 480 // safely usable for Encapsulation without allowing a user to possibly unbind from/close 481 // the port, which could potentially impact the traffic of the next user who binds to that 482 // socket. 483 public UdpEncapsulationSocket openUdpEncapsulationSocket() 484 throws IOException, ResourceUnavailableException { 485 // Temporary code 486 return new UdpEncapsulationSocket(mService); 487 } 488 489 /** 490 * Retrieve an instance of an IpSecManager within you application context 491 * 492 * @param context the application context for this manager 493 * @hide 494 */ 495 public IpSecManager(IIpSecService service) { 496 mService = checkNotNull(service, "missing service"); 497 } 498} 499