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