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