IpSecManager.java revision 1afbef40c68373f3871eed087c546cfe1911ee36
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 */
16package android.net;
17
18import static com.android.internal.util.Preconditions.checkNotNull;
19
20import android.annotation.SystemApi;
21import android.os.ParcelFileDescriptor;
22import android.util.AndroidException;
23import dalvik.system.CloseGuard;
24import java.io.FileDescriptor;
25import java.io.IOException;
26import java.net.DatagramSocket;
27import java.net.InetAddress;
28import java.net.Socket;
29
30/**
31 * This class contains methods for managing IPsec sessions, which will perform kernel-space
32 * encryption and decryption of socket or Network traffic.
33 *
34 * <p>An IpSecManager may be obtained by calling {@link
35 * android.content.Context#getSystemService(String) Context#getSystemService(String)} with {@link
36 * android.content.Context#IPSEC_SERVICE Context#IPSEC_SERVICE}
37 */
38public final class IpSecManager {
39    private static final String TAG = "IpSecManager";
40
41    /**
42     * Indicates that the combination of remote InetAddress and SPI was non-unique for a given
43     * request. If encountered, selection of a new SPI is required before a transform may be
44     * created. Note, this should happen very rarely if the SPI is chosen to be sufficiently random
45     * or reserved using reserveSecurityParameterIndex.
46     */
47    public static final class SpiUnavailableException extends AndroidException {
48        private final int mSpi;
49
50        /**
51         * Construct an exception indicating that a transform with the given SPI is already in use
52         * or otherwise unavailable.
53         *
54         * @param msg Description indicating the colliding SPI
55         * @param spi the SPI that could not be used due to a collision
56         */
57        SpiUnavailableException(String msg, int spi) {
58            super(msg + "(spi: " + spi + ")");
59            mSpi = spi;
60        }
61
62        /** Retrieve the SPI that caused a collision */
63        public int getSpi() {
64            return mSpi;
65        }
66    }
67
68    /**
69     * Indicates that the requested system resource for IPsec, such as a socket or other system
70     * resource is unavailable. If this exception is thrown, try releasing allocated objects of the
71     * type requested.
72     */
73    public static final class ResourceUnavailableException extends AndroidException {
74
75        ResourceUnavailableException(String msg) {
76            super(msg);
77        }
78    }
79
80    private final IIpSecService mService;
81
82    public static final class SecurityParameterIndex implements AutoCloseable {
83        private final IIpSecService mService;
84        private final InetAddress mDestinationAddress;
85        private final CloseGuard mCloseGuard = CloseGuard.get();
86        private int mSpi;
87
88        /** Return the underlying SPI held by this object */
89        public int getSpi() {
90            return mSpi;
91        }
92
93        private SecurityParameterIndex(
94                IIpSecService service, InetAddress destinationAddress, int spi)
95                throws ResourceUnavailableException, SpiUnavailableException {
96            mService = service;
97            mDestinationAddress = destinationAddress;
98            mSpi = spi;
99            mCloseGuard.open("open");
100        }
101
102        /**
103         * Release an SPI that was previously reserved.
104         *
105         * <p>Release an SPI for use by other users in the system. This will fail if the SPI is
106         * currently in use by an IpSecTransform.
107         *
108         * @param destinationAddress SPIs must be unique for each combination of SPI and destination
109         *     address. Thus, the destinationAddress to which the SPI will communicate must be
110         *     supplied.
111         * @param spi the previously reserved SPI to be freed.
112         */
113        @Override
114        public void close() {
115            mSpi = INVALID_SECURITY_PARAMETER_INDEX; // TODO: Invalid SPI
116            mCloseGuard.close();
117        }
118
119        @Override
120        protected void finalize() {
121            if (mCloseGuard != null) {
122                mCloseGuard.warnIfOpen();
123            }
124
125            close();
126        }
127    }
128
129    /**
130     * The Security Parameter Index, SPI, 0 indicates an unknown or invalid index.
131     *
132     * <p>No IPsec packet may contain an SPI of 0.
133     */
134    public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
135
136    /**
137     * Reserve an SPI for traffic bound towards the specified destination address.
138     *
139     * <p>If successful, this SPI is guaranteed available until released by a call to {@link
140     * SecurityParameterIndex#close()}.
141     *
142     * @param destinationAddress SPIs must be unique for each combination of SPI and destination
143     *     address.
144     * @param requestedSpi the requested SPI, or '0' to allocate a random SPI.
145     * @return the reserved SecurityParameterIndex
146     * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
147     *     for this user
148     * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
149     */
150    public SecurityParameterIndex reserveSecurityParameterIndex(
151            InetAddress destinationAddress, int requestedSpi)
152            throws SpiUnavailableException, ResourceUnavailableException {
153        return new SecurityParameterIndex(mService, destinationAddress, requestedSpi);
154    }
155
156    /**
157     * Apply an active Transport Mode IPsec Transform to a stream socket to perform IPsec
158     * encapsulation of the traffic flowing between the socket and the remote InetAddress of that
159     * transform. For security reasons, attempts to send traffic to any IP address other than the
160     * address associated with that transform will throw an IOException. In addition, if the
161     * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to
162     * send() or receive() until the transform is removed from the socket by calling {@link
163     * #removeTransportModeTransform(Socket, IpSecTransform)};
164     *
165     * @param socket a stream socket
166     * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
167     */
168    public void applyTransportModeTransform(Socket socket, IpSecTransform transform)
169            throws IOException {
170        applyTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform);
171    }
172
173    /**
174     * Apply an active Transport Mode IPsec Transform to a datagram socket to perform IPsec
175     * encapsulation of the traffic flowing between the socket and the remote InetAddress of that
176     * transform. For security reasons, attempts to send traffic to any IP address other than the
177     * address associated with that transform will throw an IOException. In addition, if the
178     * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to
179     * send() or receive() until the transform is removed from the socket by calling {@link
180     * #removeTransportModeTransform(DatagramSocket, IpSecTransform)};
181     *
182     * @param socket a datagram socket
183     * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
184     */
185    public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
186            throws IOException {
187        applyTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform);
188    }
189
190    /* Call down to activate a transform */
191    private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
192
193    /**
194     * Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to
195     * and from that network's interface with IPsec (applies an outer IP header and IPsec Header to
196     * all traffic, and expects an additional IP header and IPsec Header on all inbound traffic).
197     * Applications should probably not use this API directly. Instead, they should use {@link
198     * VpnService} to provide VPN capability in a more generic fashion.
199     *
200     * @param net a {@link Network} that will be tunneled via IP Sec.
201     * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform.
202     * @hide
203     */
204    @SystemApi
205    public void applyTunnelModeTransform(Network net, IpSecTransform transform) {}
206
207    /**
208     * Remove a transform from a given stream socket. Once removed, traffic on the socket will not
209     * be encypted. This allows sockets that have been used for IPsec to be reclaimed for
210     * communication in the clear in the event socket reuse is desired. This operation will succeed
211     * regardless of the underlying state of a transform. If a transform is removed, communication
212     * on all sockets to which that transform was applied will fail until this method is called.
213     *
214     * @param socket a socket that previously had a transform applied to it.
215     * @param transform the IPsec Transform that was previously applied to the given socket
216     */
217    public void removeTransportModeTransform(Socket socket, IpSecTransform transform) {
218        removeTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform);
219    }
220
221    /**
222     * Remove a transform from a given datagram socket. Once removed, traffic on the socket will not
223     * be encypted. This allows sockets that have been used for IPsec to be reclaimed for
224     * communication in the clear in the event socket reuse is desired. This operation will succeed
225     * regardless of the underlying state of a transform. If a transform is removed, communication
226     * on all sockets to which that transform was applied will fail until this method is called.
227     *
228     * @param socket a socket that previously had a transform applied to it.
229     * @param transform the IPsec Transform that was previously applied to the given socket
230     */
231    public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform) {
232        removeTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform);
233    }
234
235    /* Call down to activate a transform */
236    private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
237
238    /**
239     * Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of
240     * cleanup if a tunneled Network experiences a change in default route. The Network will drop
241     * all traffic that cannot be routed to the Tunnel's outbound interface. If that interface is
242     * lost, all traffic will drop.
243     *
244     * @param net a network that currently has transform applied to it.
245     * @param transform a Tunnel Mode IPsec Transform that has been previously applied to the given
246     *     network
247     * @hide
248     */
249    @SystemApi
250    public void removeTunnelModeTransform(Network net, IpSecTransform transform) {}
251
252    /**
253     * Class providing access to a system-provided UDP Encapsulation Socket, which may be used for
254     * IKE signalling as well as for inbound and outbound UDP encapsulated IPsec traffic.
255     *
256     * <p>The socket provided by this class cannot be re-bound or closed via the inner
257     * FileDescriptor. Instead, disposing of this socket requires a call to close().
258     */
259    public static final class UdpEncapsulationSocket implements AutoCloseable {
260        private final FileDescriptor mFd;
261        private final IIpSecService mService;
262        private final CloseGuard mCloseGuard = CloseGuard.get();
263
264        private UdpEncapsulationSocket(IIpSecService service, int port)
265                throws ResourceUnavailableException {
266            mService = service;
267            mCloseGuard.open("constructor");
268            // TODO: go down to the kernel and get a socket on the specified
269            mFd = new FileDescriptor();
270        }
271
272        private UdpEncapsulationSocket(IIpSecService service) throws ResourceUnavailableException {
273            mService = service;
274            mCloseGuard.open("constructor");
275            // TODO: go get a random socket on a random port
276            mFd = new FileDescriptor();
277        }
278
279        /** Access the inner UDP Encapsulation Socket */
280        public FileDescriptor getSocket() {
281            return mFd;
282        }
283
284        /** Retrieve the port number of the inner encapsulation socket */
285        public int getPort() {
286            return 0; // TODO get the port number from the Socket;
287        }
288
289        @Override
290        /**
291         * Release the resources that have been reserved for this Socket.
292         *
293         * <p>This method closes the underlying socket, reducing a user's allocated sockets in the
294         * system. This must be done as part of cleanup following use of a socket. Failure to do so
295         * will cause the socket to count against a total allocation limit for IpSec and eventually
296         * fail due to resource limits.
297         *
298         * @param fd a file descriptor previously returned as a UDP Encapsulation socket.
299         */
300        public void close() {
301            // TODO: Go close the socket
302            mCloseGuard.close();
303        }
304
305        @Override
306        protected void finalize() throws Throwable {
307            if (mCloseGuard != null) {
308                mCloseGuard.warnIfOpen();
309            }
310
311            close();
312        }
313    };
314
315    /**
316     * Open a socket that is bound to a free UDP port on the system.
317     *
318     * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by
319     * the caller. This provides safe access to a socket on a port that can later be used as a UDP
320     * Encapsulation port.
321     *
322     * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the
323     * socket port. Explicitly opening this port is only necessary if communication is desired on
324     * that port.
325     *
326     * @param port a local UDP port to be reserved for UDP Encapsulation. is provided, then this
327     *     method will bind to the specified port or fail. To retrieve the port number, call {@link
328     *     android.system.Os#getsockname(FileDescriptor)}.
329     * @return a {@link UdpEncapsulationSocket} that is bound to the requested port for the lifetime
330     *     of the object.
331     */
332    // Returning a socket in this fashion that has been created and bound by the system
333    // is the only safe way to ensure that a socket is both accessible to the user and
334    // safely usable for Encapsulation without allowing a user to possibly unbind from/close
335    // the port, which could potentially impact the traffic of the next user who binds to that
336    // socket.
337    public UdpEncapsulationSocket openUdpEncapsulationSocket(int port)
338            throws IOException, ResourceUnavailableException {
339        // Temporary code
340        return new UdpEncapsulationSocket(mService, port);
341    }
342
343    /**
344     * Open a socket that is bound to a port selected by the system.
345     *
346     * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by
347     * the caller. This provides safe access to a socket on a port that can later be used as a UDP
348     * Encapsulation port.
349     *
350     * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the
351     * socket port. Explicitly opening this port is only necessary if communication is desired on
352     * that port.
353     *
354     * @return a {@link UdpEncapsulationSocket} that is bound to an arbitrarily selected port
355     */
356    // Returning a socket in this fashion that has been created and bound by the system
357    // is the only safe way to ensure that a socket is both accessible to the user and
358    // safely usable for Encapsulation without allowing a user to possibly unbind from/close
359    // the port, which could potentially impact the traffic of the next user who binds to that
360    // socket.
361    public UdpEncapsulationSocket openUdpEncapsulationSocket()
362            throws IOException, ResourceUnavailableException {
363        // Temporary code
364        return new UdpEncapsulationSocket(mService);
365    }
366
367    /**
368     * Retrieve an instance of an IpSecManager within you application context
369     *
370     * @param context the application context for this manager
371     * @hide
372     */
373    public IpSecManager(IIpSecService service) {
374        mService = checkNotNull(service, "missing service");
375    }
376}
377