• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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          */
SpiUnavailableException(String msg, int spi)78         SpiUnavailableException(String msg, int spi) {
79             super(msg + "(spi: " + spi + ")");
80             mSpi = spi;
81         }
82 
83         /** Retrieve the SPI that caused a collision */
getSpi()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 
ResourceUnavailableException(String msg)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 */
getSpi()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
close()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
finalize()133         protected void finalize() {
134             if (mCloseGuard != null) {
135                 mCloseGuard.warnIfOpen();
136             }
137 
138             close();
139         }
140 
SecurityParameterIndex( @onNull IIpSecService service, int direction, InetAddress remoteAddress, int spi)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 */
getResourceId()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      */
reserveSecurityParameterIndex( int direction, InetAddress remoteAddress)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      */
reserveSecurityParameterIndex( int direction, InetAddress remoteAddress, int requestedSpi)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      */
applyTransportModeTransform(Socket socket, IpSecTransform transform)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      */
applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)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      */
applyTransportModeTransform(FileDescriptor socket, IpSecTransform transform)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 */
applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform 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      */
applyTunnelModeTransform(Network net, IpSecTransform transform)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      */
removeTransportModeTransform(Socket socket, IpSecTransform transform)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      */
removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform)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      */
removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform)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 */
removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform 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      */
removeTunnelModeTransform(Network net, IpSecTransform transform)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 
UdpEncapsulationSocket(@onNull IIpSecService service, int port)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 */
getSocket()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 */
getPort()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          */
close()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
finalize()480         protected void finalize() throws Throwable {
481             if (mCloseGuard != null) {
482                 mCloseGuard.warnIfOpen();
483             }
484             close();
485         }
486 
487         /** @hide */
getResourceId()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.
openUdpEncapsulationSocket(int port)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.
openUdpEncapsulationSocket()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      */
IpSecManager(IIpSecService service)556     public IpSecManager(IIpSecService service) {
557         mService = checkNotNull(service, "missing service");
558     }
559 }
560