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