IpSecManager.java revision 8dc1fd0237992e1d693376b4f6eea45e7447e9db
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.ParcelFileDescriptor; 23import android.os.RemoteException; 24import android.util.AndroidException; 25import android.util.Log; 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 * 41 * @hide 42 */ 43public final class IpSecManager { 44 private static final String TAG = "IpSecManager"; 45 46 /** 47 * The Security Parameter Index, SPI, 0 indicates an unknown or invalid index. 48 * 49 * <p>No IPsec packet may contain an SPI of 0. 50 */ 51 public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; 52 53 /** @hide */ 54 public interface Status { 55 public static final int OK = 0; 56 public static final int RESOURCE_UNAVAILABLE = 1; 57 public static final int SPI_UNAVAILABLE = 2; 58 } 59 60 /** @hide */ 61 public static final int INVALID_RESOURCE_ID = 0; 62 63 /** 64 * Indicates that the combination of remote InetAddress and SPI was non-unique for a given 65 * request. If encountered, selection of a new SPI is required before a transform may be 66 * created. Note, this should happen very rarely if the SPI is chosen to be sufficiently random 67 * or reserved using reserveSecurityParameterIndex. 68 */ 69 public static final class SpiUnavailableException extends AndroidException { 70 private final int mSpi; 71 72 /** 73 * Construct an exception indicating that a transform with the given SPI is already in use 74 * or otherwise unavailable. 75 * 76 * @param msg Description indicating the colliding SPI 77 * @param spi the SPI that could not be used due to a collision 78 */ 79 SpiUnavailableException(String msg, int spi) { 80 super(msg + "(spi: " + spi + ")"); 81 mSpi = spi; 82 } 83 84 /** Retrieve the SPI that caused a collision */ 85 public int getSpi() { 86 return mSpi; 87 } 88 } 89 90 /** 91 * Indicates that the requested system resource for IPsec, such as a socket or other system 92 * resource is unavailable. If this exception is thrown, try releasing allocated objects of the 93 * type requested. 94 */ 95 public static final class ResourceUnavailableException extends AndroidException { 96 97 ResourceUnavailableException(String msg) { 98 super(msg); 99 } 100 } 101 102 private final IIpSecService mService; 103 104 public static final class SecurityParameterIndex implements AutoCloseable { 105 private final IIpSecService mService; 106 private final InetAddress mRemoteAddress; 107 private final CloseGuard mCloseGuard = CloseGuard.get(); 108 private int mSpi = INVALID_SECURITY_PARAMETER_INDEX; 109 private int mResourceId; 110 111 /** Return the underlying SPI held by this object */ 112 public int getSpi() { 113 return mSpi; 114 } 115 116 /** 117 * Release an SPI that was previously reserved. 118 * 119 * <p>Release an SPI for use by other users in the system. If a SecurityParameterIndex is 120 * applied to an IpSecTransform, it will become unusable for future transforms but should 121 * still be closed to ensure system resources are released. 122 */ 123 @Override 124 public void close() { 125 try { 126 mService.releaseSecurityParameterIndex(mResourceId); 127 } catch (RemoteException e) { 128 throw e.rethrowFromSystemServer(); 129 } 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 IpSecSpiResponse 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.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.spi; 170 mResourceId = result.resourceId; 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 /** @hide */ 188 int getResourceId() { 189 return mResourceId; 190 } 191 } 192 193 /** 194 * Reserve an SPI for traffic bound towards the specified remote address. 195 * 196 * <p>If successful, this SPI is guaranteed available until released by a call to {@link 197 * SecurityParameterIndex#close()}. 198 * 199 * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT} 200 * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress. 201 * @return the reserved SecurityParameterIndex 202 * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated 203 * for this user 204 * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved 205 */ 206 public SecurityParameterIndex reserveSecurityParameterIndex( 207 int direction, InetAddress remoteAddress) throws ResourceUnavailableException { 208 try { 209 return new SecurityParameterIndex( 210 mService, 211 direction, 212 remoteAddress, 213 IpSecManager.INVALID_SECURITY_PARAMETER_INDEX); 214 } catch (SpiUnavailableException unlikely) { 215 throw new ResourceUnavailableException("No SPIs available"); 216 } 217 } 218 219 /** 220 * Reserve an SPI for traffic bound towards the specified remote address. 221 * 222 * <p>If successful, this SPI is guaranteed available until released by a call to {@link 223 * SecurityParameterIndex#close()}. 224 * 225 * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT} 226 * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress. 227 * @param requestedSpi the requested SPI, or '0' to allocate a random SPI. 228 * @return the reserved SecurityParameterIndex 229 * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated 230 * for this user 231 */ 232 public SecurityParameterIndex reserveSecurityParameterIndex( 233 int direction, InetAddress remoteAddress, int requestedSpi) 234 throws SpiUnavailableException, ResourceUnavailableException { 235 if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) { 236 throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI"); 237 } 238 return new SecurityParameterIndex(mService, direction, remoteAddress, requestedSpi); 239 } 240 241 /** 242 * Apply an active Transport Mode IPsec Transform to a stream socket to perform IPsec 243 * encapsulation of the traffic flowing between the socket and the remote InetAddress of that 244 * transform. For security reasons, attempts to send traffic to any IP address other than the 245 * address associated with that transform will throw an IOException. In addition, if the 246 * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to 247 * send() or receive() until the transform is removed from the socket by calling {@link 248 * #removeTransportModeTransform(Socket, IpSecTransform)}; 249 * 250 * @param socket a stream socket 251 * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform. 252 * @hide 253 */ 254 public void applyTransportModeTransform(Socket socket, IpSecTransform transform) 255 throws IOException { 256 try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) { 257 applyTransportModeTransform(pfd, transform); 258 } 259 } 260 261 /** 262 * Apply an active Transport Mode IPsec Transform to a datagram socket to perform IPsec 263 * encapsulation of the traffic flowing between the socket and the remote InetAddress of that 264 * transform. For security reasons, attempts to send traffic to any IP address other than the 265 * address associated with that transform will throw an IOException. In addition, if the 266 * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to 267 * send() or receive() until the transform is removed from the socket by calling {@link 268 * #removeTransportModeTransform(DatagramSocket, IpSecTransform)}; 269 * 270 * @param socket a datagram socket 271 * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform. 272 * @hide 273 */ 274 public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform) 275 throws IOException { 276 try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) { 277 applyTransportModeTransform(pfd, transform); 278 } 279 } 280 281 /** 282 * Apply an active Transport Mode IPsec Transform to a stream socket to perform IPsec 283 * encapsulation of the traffic flowing between the socket and the remote InetAddress of that 284 * transform. For security reasons, attempts to send traffic to any IP address other than the 285 * address associated with that transform will throw an IOException. In addition, if the 286 * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to 287 * send() or receive() until the transform is removed from the socket by calling {@link 288 * #removeTransportModeTransform(FileDescriptor, IpSecTransform)}; 289 * 290 * @param socket a socket file descriptor 291 * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform. 292 */ 293 public void applyTransportModeTransform(FileDescriptor socket, IpSecTransform transform) 294 throws IOException { 295 // We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor() 296 // constructor takes control and closes the user's FD when we exit the method 297 // This is behaviorally the same as the other versions, but the PFD constructor does not 298 // dup() automatically, whereas PFD.fromSocket() and PDF.fromDatagramSocket() do dup(). 299 try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) { 300 applyTransportModeTransform(pfd, transform); 301 } 302 } 303 304 /* Call down to activate a transform */ 305 private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) { 306 try { 307 mService.applyTransportModeTransform(pfd, transform.getResourceId()); 308 } catch (RemoteException e) { 309 throw e.rethrowFromSystemServer(); 310 } 311 } 312 313 /** 314 * Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to 315 * and from that network's interface with IPsec (applies an outer IP header and IPsec Header to 316 * all traffic, and expects an additional IP header and IPsec Header on all inbound traffic). 317 * Applications should probably not use this API directly. Instead, they should use {@link 318 * VpnService} to provide VPN capability in a more generic fashion. 319 * 320 * @param net a {@link Network} that will be tunneled via IP Sec. 321 * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform. 322 * @hide 323 */ 324 public void applyTunnelModeTransform(Network net, IpSecTransform transform) {} 325 326 /** 327 * Remove a transform from a given stream socket. Once removed, traffic on the socket will not 328 * be encypted. This allows sockets that have been used for IPsec to be reclaimed for 329 * communication in the clear in the event socket reuse is desired. This operation will succeed 330 * regardless of the underlying state of a transform. If a transform is removed, communication 331 * on all sockets to which that transform was applied will fail until this method is called. 332 * 333 * @param socket a socket that previously had a transform applied to it. 334 * @param transform the IPsec Transform that was previously applied to the given socket 335 * @hide 336 */ 337 public void removeTransportModeTransform(Socket socket, IpSecTransform transform) 338 throws IOException { 339 try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) { 340 removeTransportModeTransform(pfd, transform); 341 } 342 } 343 344 /** 345 * Remove a transform from a given datagram socket. Once removed, traffic on the socket will not 346 * be encypted. This allows sockets that have been used for IPsec to be reclaimed for 347 * communication in the clear in the event socket reuse is desired. This operation will succeed 348 * regardless of the underlying state of a transform. If a transform is removed, communication 349 * on all sockets to which that transform was applied will fail until this method is called. 350 * 351 * @param socket a socket that previously had a transform applied to it. 352 * @param transform the IPsec Transform that was previously applied to the given socket 353 * @hide 354 */ 355 public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform) 356 throws IOException { 357 try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) { 358 removeTransportModeTransform(pfd, transform); 359 } 360 } 361 362 /** 363 * Remove a transform from a given stream socket. Once removed, traffic on the socket will not 364 * be encypted. This allows sockets that have been used for IPsec to be reclaimed for 365 * communication in the clear in the event socket reuse is desired. This operation will succeed 366 * regardless of the underlying state of a transform. If a transform is removed, communication 367 * on all sockets to which that transform was applied will fail until this method is called. 368 * 369 * @param socket a socket file descriptor that previously had a transform applied to it. 370 * @param transform the IPsec Transform that was previously applied to the given socket 371 */ 372 public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform) 373 throws IOException { 374 try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) { 375 removeTransportModeTransform(pfd, transform); 376 } 377 } 378 379 /* Call down to activate a transform */ 380 private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) { 381 try { 382 mService.removeTransportModeTransform(pfd, transform.getResourceId()); 383 } catch (RemoteException e) { 384 throw e.rethrowFromSystemServer(); 385 } 386 } 387 388 /** 389 * Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of 390 * cleanup if a tunneled Network experiences a change in default route. The Network will drop 391 * all traffic that cannot be routed to the Tunnel's outbound interface. If that interface is 392 * lost, all traffic will drop. 393 * 394 * @param net a network that currently has transform applied to it. 395 * @param transform a Tunnel Mode IPsec Transform that has been previously applied to the given 396 * network 397 * @hide 398 */ 399 public void removeTunnelModeTransform(Network net, IpSecTransform transform) {} 400 401 /** 402 * Class providing access to a system-provided UDP Encapsulation Socket, which may be used for 403 * IKE signalling as well as for inbound and outbound UDP encapsulated IPsec traffic. 404 * 405 * <p>The socket provided by this class cannot be re-bound or closed via the inner 406 * FileDescriptor. Instead, disposing of this socket requires a call to close(). 407 */ 408 public static final class UdpEncapsulationSocket implements AutoCloseable { 409 private final ParcelFileDescriptor mPfd; 410 private final IIpSecService mService; 411 private final int mResourceId; 412 private final int mPort; 413 private final CloseGuard mCloseGuard = CloseGuard.get(); 414 415 private UdpEncapsulationSocket(@NonNull IIpSecService service, int port) 416 throws ResourceUnavailableException, IOException { 417 mService = service; 418 try { 419 IpSecUdpEncapResponse result = 420 mService.openUdpEncapsulationSocket(port, new Binder()); 421 switch (result.status) { 422 case Status.OK: 423 break; 424 case Status.RESOURCE_UNAVAILABLE: 425 throw new ResourceUnavailableException( 426 "No more Sockets may be allocated by this requester."); 427 default: 428 throw new RuntimeException( 429 "Unknown status returned by IpSecService: " + result.status); 430 } 431 mResourceId = result.resourceId; 432 mPort = result.port; 433 mPfd = result.fileDescriptor; 434 } catch (RemoteException e) { 435 throw e.rethrowFromSystemServer(); 436 } 437 mCloseGuard.open("constructor"); 438 } 439 440 /** Access the inner UDP Encapsulation Socket */ 441 public FileDescriptor getSocket() { 442 if (mPfd == null) { 443 return null; 444 } 445 return mPfd.getFileDescriptor(); 446 } 447 448 /** Retrieve the port number of the inner encapsulation socket */ 449 public int getPort() { 450 return mPort; 451 } 452 453 @Override 454 /** 455 * Release the resources that have been reserved for this Socket. 456 * 457 * <p>This method closes the underlying socket, reducing a user's allocated sockets in the 458 * system. This must be done as part of cleanup following use of a socket. Failure to do so 459 * will cause the socket to count against a total allocation limit for IpSec and eventually 460 * fail due to resource limits. 461 * 462 * @param fd a file descriptor previously returned as a UDP Encapsulation socket. 463 */ 464 public void close() throws IOException { 465 try { 466 mService.closeUdpEncapsulationSocket(mResourceId); 467 } catch (RemoteException e) { 468 throw e.rethrowFromSystemServer(); 469 } 470 471 try { 472 mPfd.close(); 473 } catch (IOException e) { 474 Log.e(TAG, "Failed to close UDP Encapsulation Socket with Port= " + mPort); 475 throw e; 476 } 477 mCloseGuard.close(); 478 } 479 480 @Override 481 protected void finalize() throws Throwable { 482 if (mCloseGuard != null) { 483 mCloseGuard.warnIfOpen(); 484 } 485 close(); 486 } 487 488 /** @hide */ 489 int getResourceId() { 490 return mResourceId; 491 } 492 }; 493 494 /** 495 * Open a socket that is bound to a free UDP port on the system. 496 * 497 * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by 498 * the caller. This provides safe access to a socket on a port that can later be used as a UDP 499 * Encapsulation port. 500 * 501 * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the 502 * socket port. Explicitly opening this port is only necessary if communication is desired on 503 * that port. 504 * 505 * @param port a local UDP port to be reserved for UDP Encapsulation. is provided, then this 506 * method will bind to the specified port or fail. To retrieve the port number, call {@link 507 * android.system.Os#getsockname(FileDescriptor)}. 508 * @return a {@link UdpEncapsulationSocket} that is bound to the requested port for the lifetime 509 * of the object. 510 */ 511 // Returning a socket in this fashion that has been created and bound by the system 512 // is the only safe way to ensure that a socket is both accessible to the user and 513 // safely usable for Encapsulation without allowing a user to possibly unbind from/close 514 // the port, which could potentially impact the traffic of the next user who binds to that 515 // socket. 516 public UdpEncapsulationSocket openUdpEncapsulationSocket(int port) 517 throws IOException, ResourceUnavailableException { 518 /* 519 * Most range checking is done in the service, but this version of the constructor expects 520 * a valid port number, and zero cannot be checked after being passed to the service. 521 */ 522 if (port == 0) { 523 throw new IllegalArgumentException("Specified port must be a valid port number!"); 524 } 525 return new UdpEncapsulationSocket(mService, port); 526 } 527 528 /** 529 * Open a socket that is bound to a port selected by the system. 530 * 531 * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by 532 * the caller. This provides safe access to a socket on a port that can later be used as a UDP 533 * Encapsulation port. 534 * 535 * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the 536 * socket port. Explicitly opening this port is only necessary if communication is desired on 537 * that port. 538 * 539 * @return a {@link UdpEncapsulationSocket} that is bound to an arbitrarily selected port 540 */ 541 // Returning a socket in this fashion that has been created and bound by the system 542 // is the only safe way to ensure that a socket is both accessible to the user and 543 // safely usable for Encapsulation without allowing a user to possibly unbind from/close 544 // the port, which could potentially impact the traffic of the next user who binds to that 545 // socket. 546 public UdpEncapsulationSocket openUdpEncapsulationSocket() 547 throws IOException, ResourceUnavailableException { 548 return new UdpEncapsulationSocket(mService, 0); 549 } 550 551 /** 552 * Retrieve an instance of an IpSecManager within you application context 553 * 554 * @param context the application context for this manager 555 * @hide 556 */ 557 public IpSecManager(IIpSecService service) { 558 mService = checkNotNull(service, "missing service"); 559 } 560} 561