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.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          */
     84         SpiUnavailableException(String msg, int spi) {
     85             super(msg + "(spi: " + spi + ")");
     86             mSpi = spi;
     87         }
     88 
     89         /** Retrieve the SPI that caused a collision */
     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 
    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 */
    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
    129         public void close() {
    130             mSpi = INVALID_SECURITY_PARAMETER_INDEX;
    131             mCloseGuard.close();
    132         }
    133 
    134         @Override
    135         protected void finalize() {
    136             if (mCloseGuard != null) {
    137                 mCloseGuard.warnIfOpen();
    138             }
    139 
    140             close();
    141         }
    142 
    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      */
    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      */
    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      */
    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      */
    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 */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 */
    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      */
    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 
    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 
    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 */
    410         public FileDescriptor getSocket() {
    411             return mFd;
    412         }
    413 
    414         /** Retrieve the port number of the inner encapsulation socket */
    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          */
    430         public void close() throws IOException {
    431             // TODO: Go close the socket
    432             mCloseGuard.close();
    433         }
    434 
    435         @Override
    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.
    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.
    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      */
    503     public IpSecManager(IIpSecService service) {
    504         mService = checkNotNull(service, "missing service");
    505     }
    506 }
    507