Home | History | Annotate | Download | only in net
      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          */
     78         SpiUnavailableException(String msg, int spi) {
     79             super(msg + "(spi: " + spi + ")");
     80             mSpi = spi;
     81         }
     82 
     83         /** Retrieve the SPI that caused a collision */
     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 
     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 */
    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
    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
    133         protected void finalize() {
    134             if (mCloseGuard != null) {
    135                 mCloseGuard.warnIfOpen();
    136             }
    137 
    138             close();
    139         }
    140 
    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 */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 */
    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      */
    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      */
    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      */
    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      */
    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 */
    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      */
    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 
    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 */
    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 */
    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          */
    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
    480         protected void finalize() throws Throwable {
    481             if (mCloseGuard != null) {
    482                 mCloseGuard.warnIfOpen();
    483             }
    484             close();
    485         }
    486 
    487         /** @hide */
    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.
    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.
    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      */
    556     public IpSecManager(IIpSecService service) {
    557         mService = checkNotNull(service, "missing service");
    558     }
    559 }
    560