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 android.net.IpSecManager.INVALID_RESOURCE_ID;
     19 
     20 import android.annotation.IntDef;
     21 import android.annotation.NonNull;
     22 import android.annotation.SystemApi;
     23 import android.content.Context;
     24 import android.os.Binder;
     25 import android.os.IBinder;
     26 import android.os.RemoteException;
     27 import android.os.ServiceManager;
     28 import android.util.Log;
     29 import com.android.internal.util.Preconditions;
     30 import dalvik.system.CloseGuard;
     31 import java.io.IOException;
     32 import java.lang.annotation.Retention;
     33 import java.lang.annotation.RetentionPolicy;
     34 import java.net.InetAddress;
     35 
     36 /**
     37  * This class represents an IpSecTransform, which encapsulates both properties and state of IPsec.
     38  *
     39  * <p>IpSecTransforms must be built from an IpSecTransform.Builder, and they must persist throughout
     40  * the lifetime of the underlying transform. If a transform object leaves scope, the underlying
     41  * transform may be disabled automatically, with likely undesirable results.
     42  *
     43  * <p>An IpSecTransform may either represent a tunnel mode transform that operates on a wide array
     44  * of traffic or may represent a transport mode transform operating on a Socket or Sockets.
     45  *
     46  * @hide
     47  */
     48 public final class IpSecTransform implements AutoCloseable {
     49     private static final String TAG = "IpSecTransform";
     50 
     51     /**
     52      * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies
     53      * to traffic towards the host.
     54      */
     55     public static final int DIRECTION_IN = 0;
     56 
     57     /**
     58      * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies
     59      * to traffic from the host.
     60      */
     61     public static final int DIRECTION_OUT = 1;
     62 
     63     /** @hide */
     64     @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
     65     @Retention(RetentionPolicy.SOURCE)
     66     public @interface TransformDirection {}
     67 
     68     /** @hide */
     69     private static final int MODE_TUNNEL = 0;
     70 
     71     /** @hide */
     72     private static final int MODE_TRANSPORT = 1;
     73 
     74     /** @hide */
     75     public static final int ENCAP_NONE = 0;
     76 
     77     /**
     78      * IpSec traffic will be encapsulated within a UDP header with an additional 8-byte header pad
     79      * (of '0'-value bytes) that prevents traffic from being interpreted as IKE or as ESP over UDP.
     80      *
     81      * @hide
     82      */
     83     public static final int ENCAP_ESPINUDP_NON_IKE = 1;
     84 
     85     /**
     86      * IpSec traffic will be encapsulated within UDP as per <a
     87      * href="https://tools.ietf.org/html/rfc3948">RFC3498</a>.
     88      *
     89      * @hide
     90      */
     91     public static final int ENCAP_ESPINUDP = 2;
     92 
     93     /** @hide */
     94     @IntDef(value = {ENCAP_NONE, ENCAP_ESPINUDP, ENCAP_ESPINUDP_NON_IKE})
     95     @Retention(RetentionPolicy.SOURCE)
     96     public @interface EncapType {}
     97 
     98     private IpSecTransform(Context context, IpSecConfig config) {
     99         mContext = context;
    100         mConfig = config;
    101         mResourceId = INVALID_RESOURCE_ID;
    102     }
    103 
    104     private IIpSecService getIpSecService() {
    105         IBinder b = ServiceManager.getService(android.content.Context.IPSEC_SERVICE);
    106         if (b == null) {
    107             throw new RemoteException("Failed to connect to IpSecService")
    108                     .rethrowAsRuntimeException();
    109         }
    110 
    111         return IIpSecService.Stub.asInterface(b);
    112     }
    113 
    114     private void checkResultStatusAndThrow(int status)
    115             throws IOException, IpSecManager.ResourceUnavailableException,
    116                     IpSecManager.SpiUnavailableException {
    117         switch (status) {
    118             case IpSecManager.Status.OK:
    119                 return;
    120                 // TODO: Pass Error string back from bundle so that errors can be more specific
    121             case IpSecManager.Status.RESOURCE_UNAVAILABLE:
    122                 throw new IpSecManager.ResourceUnavailableException(
    123                         "Failed to allocate a new IpSecTransform");
    124             case IpSecManager.Status.SPI_UNAVAILABLE:
    125                 Log.wtf(TAG, "Attempting to use an SPI that was somehow not reserved");
    126                 // Fall through
    127             default:
    128                 throw new IllegalStateException(
    129                         "Failed to Create a Transform with status code " + status);
    130         }
    131     }
    132 
    133     private IpSecTransform activate()
    134             throws IOException, IpSecManager.ResourceUnavailableException,
    135                     IpSecManager.SpiUnavailableException {
    136         synchronized (this) {
    137             try {
    138                 IIpSecService svc = getIpSecService();
    139                 IpSecTransformResponse result =
    140                         svc.createTransportModeTransform(mConfig, new Binder());
    141                 int status = result.status;
    142                 checkResultStatusAndThrow(status);
    143                 mResourceId = result.resourceId;
    144 
    145                 /* Keepalive will silently fail if not needed by the config; but, if needed and
    146                  * it fails to start, we need to bail because a transform will not be reliable
    147                  * to use if keepalive is expected to offload and fails.
    148                  */
    149                 // FIXME: if keepalive fails, we need to fail spectacularly
    150                 startKeepalive(mContext);
    151                 Log.d(TAG, "Added Transform with Id " + mResourceId);
    152                 mCloseGuard.open("build");
    153             } catch (RemoteException e) {
    154                 throw e.rethrowAsRuntimeException();
    155             }
    156         }
    157 
    158         return this;
    159     }
    160 
    161     /**
    162      * Deactivate an IpSecTransform and free all resources for that transform that are managed by
    163      * the system for this Transform.
    164      *
    165      * <p>Deactivating a transform while it is still applied to any Socket will result in sockets
    166      * refusing to send or receive data. This method will silently succeed if the specified
    167      * transform has already been removed; thus, it is always safe to attempt cleanup when a
    168      * transform is no longer needed.
    169      */
    170     public void close() {
    171         Log.d(TAG, "Removing Transform with Id " + mResourceId);
    172 
    173         // Always safe to attempt cleanup
    174         if (mResourceId == INVALID_RESOURCE_ID) {
    175             mCloseGuard.close();
    176             return;
    177         }
    178         try {
    179             /* Order matters here because the keepalive is best-effort but could fail in some
    180              * horrible way to be removed if the wifi (or cell) subsystem has crashed, and we
    181              * still want to clear out the transform.
    182              */
    183             IIpSecService svc = getIpSecService();
    184             svc.deleteTransportModeTransform(mResourceId);
    185             stopKeepalive();
    186         } catch (RemoteException e) {
    187             throw e.rethrowAsRuntimeException();
    188         } finally {
    189             mResourceId = INVALID_RESOURCE_ID;
    190             mCloseGuard.close();
    191         }
    192     }
    193 
    194     @Override
    195     protected void finalize() throws Throwable {
    196         if (mCloseGuard != null) {
    197             mCloseGuard.warnIfOpen();
    198         }
    199         close();
    200     }
    201 
    202     /* Package */
    203     IpSecConfig getConfig() {
    204         return mConfig;
    205     }
    206 
    207     private final IpSecConfig mConfig;
    208     private int mResourceId;
    209     private final Context mContext;
    210     private final CloseGuard mCloseGuard = CloseGuard.get();
    211     private ConnectivityManager.PacketKeepalive mKeepalive;
    212     private int mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
    213     private Object mKeepaliveSyncLock = new Object();
    214     private ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
    215             new ConnectivityManager.PacketKeepaliveCallback() {
    216 
    217                 @Override
    218                 public void onStarted() {
    219                     synchronized (mKeepaliveSyncLock) {
    220                         mKeepaliveStatus = ConnectivityManager.PacketKeepalive.SUCCESS;
    221                         mKeepaliveSyncLock.notifyAll();
    222                     }
    223                 }
    224 
    225                 @Override
    226                 public void onStopped() {
    227                     synchronized (mKeepaliveSyncLock) {
    228                         mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
    229                         mKeepaliveSyncLock.notifyAll();
    230                     }
    231                 }
    232 
    233                 @Override
    234                 public void onError(int error) {
    235                     synchronized (mKeepaliveSyncLock) {
    236                         mKeepaliveStatus = error;
    237                         mKeepaliveSyncLock.notifyAll();
    238                     }
    239                 }
    240             };
    241 
    242     /* Package */
    243     void startKeepalive(Context c) {
    244         // FIXME: NO_KEEPALIVE needs to be a constant
    245         if (mConfig.getNattKeepaliveInterval() == 0) {
    246             return;
    247         }
    248 
    249         ConnectivityManager cm =
    250                 (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
    251 
    252         if (mKeepalive != null) {
    253             Log.wtf(TAG, "Keepalive already started for this IpSecTransform.");
    254             return;
    255         }
    256 
    257         synchronized (mKeepaliveSyncLock) {
    258             mKeepalive =
    259                     cm.startNattKeepalive(
    260                             mConfig.getNetwork(),
    261                             mConfig.getNattKeepaliveInterval(),
    262                             mKeepaliveCallback,
    263                             mConfig.getLocalAddress(),
    264                             0x1234, /* FIXME: get the real port number again,
    265                                     which we need to retrieve from the provided
    266                                     EncapsulationSocket, and which isn't currently
    267                                     stashed in IpSecConfig */
    268                             mConfig.getRemoteAddress());
    269             try {
    270                 // FIXME: this is still a horrible way to fudge the synchronous callback
    271                 mKeepaliveSyncLock.wait(2000);
    272             } catch (InterruptedException e) {
    273             }
    274         }
    275         if (mKeepaliveStatus != ConnectivityManager.PacketKeepalive.SUCCESS) {
    276             throw new UnsupportedOperationException("Packet Keepalive cannot be started");
    277         }
    278     }
    279 
    280     /* Package */
    281     int getResourceId() {
    282         return mResourceId;
    283     }
    284 
    285     /* Package */
    286     void stopKeepalive() {
    287         if (mKeepalive == null) {
    288             return;
    289         }
    290         mKeepalive.stop();
    291         synchronized (mKeepaliveSyncLock) {
    292             if (mKeepaliveStatus == ConnectivityManager.PacketKeepalive.SUCCESS) {
    293                 try {
    294                     mKeepaliveSyncLock.wait(2000);
    295                 } catch (InterruptedException e) {
    296                 }
    297             }
    298         }
    299     }
    300 
    301     /**
    302      * Builder object to facilitate the creation of IpSecTransform objects.
    303      *
    304      * <p>Apply additional properties to the transform and then call a build() method to return an
    305      * IpSecTransform object.
    306      *
    307      * @see Builder#buildTransportModeTransform(InetAddress)
    308      */
    309     public static class Builder {
    310         private Context mContext;
    311         private IpSecConfig mConfig;
    312 
    313         /**
    314          * Add an encryption algorithm to the transform for the given direction.
    315          *
    316          * <p>If encryption is set for a given direction without also providing an SPI for that
    317          * direction, creation of an IpSecTransform will fail upon calling a build() method.
    318          *
    319          * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
    320          * @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
    321          */
    322         public IpSecTransform.Builder setEncryption(
    323                 @TransformDirection int direction, IpSecAlgorithm algo) {
    324             mConfig.flow[direction].encryption = algo;
    325             return this;
    326         }
    327 
    328         /**
    329          * Add an authentication/integrity algorithm to the transform.
    330          *
    331          * <p>If authentication is set for a given direction without also providing an SPI for that
    332          * direction, creation of an IpSecTransform will fail upon calling a build() method.
    333          *
    334          * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
    335          * @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
    336          */
    337         public IpSecTransform.Builder setAuthentication(
    338                 @TransformDirection int direction, IpSecAlgorithm algo) {
    339             mConfig.flow[direction].authentication = algo;
    340             return this;
    341         }
    342 
    343         /**
    344          * Set the SPI, which uniquely identifies a particular IPsec session from others. Because
    345          * IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a
    346          * given destination address.
    347          *
    348          * <p>Care should be chosen when selecting an SPI to ensure that is is as unique as
    349          * possible. To reserve a value call {@link IpSecManager#reserveSecurityParameterIndex(int,
    350          * InetAddress, int)}. Otherwise, SPI collisions would prevent a transform from being
    351          * activated. IpSecManager#reserveSecurityParameterIndex(int, InetAddres$s, int)}.
    352          *
    353          * <p>Unless an SPI is set for a given direction, traffic in that direction will be
    354          * sent/received without any IPsec applied.
    355          *
    356          * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
    357          * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
    358          *     traffic
    359          */
    360         public IpSecTransform.Builder setSpi(
    361                 @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
    362             // TODO: convert to using the resource Id of the SPI. Then build() can validate
    363             // the owner in the IpSecService
    364             mConfig.flow[direction].spiResourceId = spi.getResourceId();
    365             return this;
    366         }
    367 
    368         /**
    369          * Specify the network on which this transform will emit its traffic; (otherwise it will
    370          * emit on the default network).
    371          *
    372          * <p>Restricts the transformed traffic to a particular {@link Network}. This is required in
    373          * tunnel mode.
    374          *
    375          * @hide
    376          */
    377         @SystemApi
    378         public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
    379             mConfig.network = net;
    380             return this;
    381         }
    382 
    383         /**
    384          * Add UDP encapsulation to an IPv4 transform
    385          *
    386          * <p>This option allows IPsec traffic to pass through NAT. Refer to RFC 3947 and 3948 for
    387          * details on how UDP should be applied to IPsec.
    388          *
    389          * @param localSocket a {@link IpSecManager.UdpEncapsulationSocket} for sending and
    390          *     receiving encapsulating traffic.
    391          * @param remotePort the UDP port number of the remote that will send and receive
    392          *     encapsulated traffic. In the case of IKE, this is likely port 4500.
    393          */
    394         public IpSecTransform.Builder setIpv4Encapsulation(
    395                 IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
    396             // TODO: check encap type is valid.
    397             mConfig.encapType = ENCAP_ESPINUDP;
    398             mConfig.encapLocalPortResourceId = localSocket.getResourceId();
    399             mConfig.encapRemotePort = remotePort;
    400             return this;
    401         }
    402 
    403         // TODO: Decrease the minimum keepalive to maybe 10?
    404         // TODO: Probably a better exception to throw for NATTKeepalive failure
    405         // TODO: Specify the needed NATT keepalive permission.
    406         /**
    407          * Send a NATT Keepalive packet with a given maximum interval. This will create an offloaded
    408          * request to do power-efficient NATT Keepalive. If NATT keepalive is requested but cannot
    409          * be activated, then the transform will fail to activate and throw an IOException.
    410          *
    411          * @param intervalSeconds the maximum number of seconds between keepalive packets, no less
    412          *     than 20s and no more than 3600s.
    413          * @hide
    414          */
    415         @SystemApi
    416         public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
    417             mConfig.nattKeepaliveInterval = intervalSeconds;
    418             return this;
    419         }
    420 
    421         /**
    422          * Build and return an active {@link IpSecTransform} object as a Transport Mode Transform.
    423          * Some parameters have interdependencies that are checked at build time. If a well-formed
    424          * transform cannot be created from the supplied parameters, this method will throw an
    425          * Exception.
    426          *
    427          * <p>Upon a successful return from this call, the provided IpSecTransform will be active
    428          * and may be applied to sockets. If too many IpSecTransform objects are active for a given
    429          * user this operation will fail and throw ResourceUnavailableException. To avoid these
    430          * exceptions, unused Transform objects must be cleaned up by calling {@link
    431          * IpSecTransform#close()} when they are no longer needed.
    432          *
    433          * @param remoteAddress the {@link InetAddress} that, when matched on traffic to/from this
    434          *     socket will cause the transform to be applied.
    435          *     <p>Note that an active transform will not impact any network traffic until it has
    436          *     been applied to one or more Sockets. Calling this method is a necessary precondition
    437          *     for applying it to a socket, but is not sufficient to actually apply IPsec.
    438          * @throws IllegalArgumentException indicating that a particular combination of transform
    439          *     properties is invalid.
    440          * @throws IpSecManager.ResourceUnavailableException in the event that no more Transforms
    441          *     may be allocated
    442          * @throws SpiUnavailableException if the SPI collides with an existing transform
    443          *     (unlikely).
    444          * @throws ResourceUnavailableException if the current user currently has exceeded the
    445          *     number of allowed active transforms.
    446          */
    447         public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
    448                 throws IpSecManager.ResourceUnavailableException,
    449                         IpSecManager.SpiUnavailableException, IOException {
    450             //FIXME: argument validation here
    451             //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
    452             mConfig.mode = MODE_TRANSPORT;
    453             mConfig.remoteAddress = remoteAddress;
    454             return new IpSecTransform(mContext, mConfig).activate();
    455         }
    456 
    457         /**
    458          * Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
    459          * parameters have interdependencies that are checked at build time.
    460          *
    461          * @param localAddress the {@link InetAddress} that provides the local endpoint for this
    462          *     IPsec tunnel. This is almost certainly an address belonging to the {@link Network}
    463          *     that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}.
    464          * @param remoteAddress the {@link InetAddress} representing the remote endpoint of this
    465          *     IPsec tunnel.
    466          * @throws IllegalArgumentException indicating that a particular combination of transform
    467          *     properties is invalid.
    468          * @hide
    469          */
    470         public IpSecTransform buildTunnelModeTransform(
    471                 InetAddress localAddress, InetAddress remoteAddress) {
    472             //FIXME: argument validation here
    473             //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
    474             mConfig.localAddress = localAddress;
    475             mConfig.remoteAddress = remoteAddress;
    476             mConfig.mode = MODE_TUNNEL;
    477             return new IpSecTransform(mContext, mConfig);
    478         }
    479 
    480         /**
    481          * Create a new IpSecTransform.Builder to construct an IpSecTransform
    482          *
    483          * @param context current Context
    484          */
    485         public Builder(@NonNull Context context) {
    486             Preconditions.checkNotNull(context);
    487             mContext = context;
    488             mConfig = new IpSecConfig();
    489         }
    490     }
    491 }
    492