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