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