• 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 
17 package com.android.server;
18 
19 import static android.Manifest.permission.DUMP;
20 import static android.net.IpSecManager.INVALID_RESOURCE_ID;
21 import static android.system.OsConstants.AF_INET;
22 import static android.system.OsConstants.AF_INET6;
23 import static android.system.OsConstants.AF_UNSPEC;
24 import static android.system.OsConstants.EINVAL;
25 import static android.system.OsConstants.IPPROTO_UDP;
26 import static android.system.OsConstants.SOCK_DGRAM;
27 
28 import static com.android.internal.util.Preconditions.checkNotNull;
29 
30 import android.annotation.NonNull;
31 import android.app.AppOpsManager;
32 import android.content.Context;
33 import android.content.pm.PackageManager;
34 import android.net.IIpSecService;
35 import android.net.INetd;
36 import android.net.IpSecAlgorithm;
37 import android.net.IpSecConfig;
38 import android.net.IpSecManager;
39 import android.net.IpSecSpiResponse;
40 import android.net.IpSecTransform;
41 import android.net.IpSecTransformResponse;
42 import android.net.IpSecTunnelInterfaceResponse;
43 import android.net.IpSecUdpEncapResponse;
44 import android.net.LinkAddress;
45 import android.net.Network;
46 import android.net.NetworkUtils;
47 import android.net.TrafficStats;
48 import android.net.util.NetdService;
49 import android.os.Binder;
50 import android.os.IBinder;
51 import android.os.ParcelFileDescriptor;
52 import android.os.RemoteException;
53 import android.os.ServiceSpecificException;
54 import android.system.ErrnoException;
55 import android.system.Os;
56 import android.system.OsConstants;
57 import android.text.TextUtils;
58 import android.util.Log;
59 import android.util.Slog;
60 import android.util.SparseArray;
61 import android.util.SparseBooleanArray;
62 
63 import com.android.internal.annotations.GuardedBy;
64 import com.android.internal.annotations.VisibleForTesting;
65 import com.android.internal.util.Preconditions;
66 
67 import libcore.io.IoUtils;
68 
69 import java.io.FileDescriptor;
70 import java.io.IOException;
71 import java.io.PrintWriter;
72 import java.net.Inet4Address;
73 import java.net.Inet6Address;
74 import java.net.InetAddress;
75 import java.net.InetSocketAddress;
76 import java.net.UnknownHostException;
77 import java.util.ArrayList;
78 import java.util.List;
79 
80 /**
81  * A service to manage multiple clients that want to access the IpSec API. The service is
82  * responsible for maintaining a list of clients and managing the resources (and related quotas)
83  * that each of them own.
84  *
85  * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
86  * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
87  * thread is ever running at a time.
88  *
89  * @hide
90  */
91 public class IpSecService extends IIpSecService.Stub {
92     private static final String TAG = "IpSecService";
93     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
94 
95     private static final String NETD_SERVICE_NAME = "netd";
96     private static final int[] ADDRESS_FAMILIES =
97             new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
98 
99     private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
100     private static final InetAddress INADDR_ANY;
101 
102     @VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
103 
104     static {
105         try {
106             INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
107         } catch (UnknownHostException e) {
108             throw new RuntimeException(e);
109         }
110     }
111 
112     static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
113     static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
114 
115     /* Binder context for this service */
116     private final Context mContext;
117 
118     /**
119      * The next non-repeating global ID for tracking resources between users, this service, and
120      * kernel data structures. Accessing this variable is not thread safe, so it is only read or
121      * modified within blocks synchronized on IpSecService.this. We want to avoid -1
122      * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
123      */
124     @GuardedBy("IpSecService.this")
125     private int mNextResourceId = 1;
126 
127     interface IpSecServiceConfiguration {
getNetdInstance()128         INetd getNetdInstance() throws RemoteException;
129 
130         static IpSecServiceConfiguration GETSRVINSTANCE =
131                 new IpSecServiceConfiguration() {
132                     @Override
133                     public INetd getNetdInstance() throws RemoteException {
134                         final INetd netd = NetdService.getInstance();
135                         if (netd == null) {
136                             throw new RemoteException("Failed to Get Netd Instance");
137                         }
138                         return netd;
139                     }
140                 };
141     }
142 
143     private final IpSecServiceConfiguration mSrvConfig;
144     final UidFdTagger mUidFdTagger;
145 
146     /**
147      * Interface for user-reference and kernel-resource cleanup.
148      *
149      * <p>This interface must be implemented for a resource to be reference counted.
150      */
151     @VisibleForTesting
152     public interface IResource {
153         /**
154          * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
155          * objects dependent on it.
156          *
157          * <p>Implementations of this method are expected to remove references to the IResource
158          * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
159          * the resource is considered invalid for user access or allocation or use in other
160          * resources.
161          *
162          * <p>References to the IResource object may be held by other RefcountedResource objects,
163          * and as such, the underlying resources and quota may not be cleaned up.
164          */
invalidate()165         void invalidate() throws RemoteException;
166 
167         /**
168          * Releases underlying resources and related quotas.
169          *
170          * <p>Implementations of this method are expected to remove all system resources that are
171          * tracked by the IResource object. Due to other RefcountedResource objects potentially
172          * having references to the IResource object, freeUnderlyingResources may not always be
173          * called from releaseIfUnreferencedRecursively().
174          */
freeUnderlyingResources()175         void freeUnderlyingResources() throws RemoteException;
176     }
177 
178     /**
179      * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
180      *
181      * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
182      * RefcountedResource object creates an explicit reference that must be freed by calling
183      * userRelease(). Additionally, adding this object as a child of another RefcountedResource
184      * object will add an implicit reference.
185      *
186      * <p>Resources are cleaned up when all references, both implicit and explicit, are released
187      * (ie, when userRelease() is called and when all parents have called releaseReference() on this
188      * object.)
189      */
190     @VisibleForTesting
191     public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
192         private final T mResource;
193         private final List<RefcountedResource> mChildren;
194         int mRefCount = 1; // starts at 1 for user's reference.
195         IBinder mBinder;
196 
RefcountedResource(T resource, IBinder binder, RefcountedResource... children)197         RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
198             synchronized (IpSecService.this) {
199                 this.mResource = resource;
200                 this.mChildren = new ArrayList<>(children.length);
201                 this.mBinder = binder;
202 
203                 for (RefcountedResource child : children) {
204                     mChildren.add(child);
205                     child.mRefCount++;
206                 }
207 
208                 try {
209                     mBinder.linkToDeath(this, 0);
210                 } catch (RemoteException e) {
211                     binderDied();
212                     e.rethrowFromSystemServer();
213                 }
214             }
215         }
216 
217         /**
218          * If the Binder object dies, this function is called to free the system resources that are
219          * being tracked by this record and to subsequently release this record for garbage
220          * collection
221          */
222         @Override
binderDied()223         public void binderDied() {
224             synchronized (IpSecService.this) {
225                 try {
226                     userRelease();
227                 } catch (Exception e) {
228                     Log.e(TAG, "Failed to release resource: " + e);
229                 }
230             }
231         }
232 
getResource()233         public T getResource() {
234             return mResource;
235         }
236 
237         /**
238          * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
239          * arrays)
240          *
241          * <p>If this method has been previously called, the RefcountedResource's binder field will
242          * be null, and the method will return without performing the cleanup a second time.
243          *
244          * <p>Note that calling this function does not imply that kernel resources will be freed at
245          * this time, or that the related quota will be returned. Such actions will only be
246          * performed upon the reference count reaching zero.
247          */
248         @GuardedBy("IpSecService.this")
userRelease()249         public void userRelease() throws RemoteException {
250             // Prevent users from putting reference counts into a bad state by calling
251             // userRelease() multiple times.
252             if (mBinder == null) {
253                 return;
254             }
255 
256             mBinder.unlinkToDeath(this, 0);
257             mBinder = null;
258 
259             mResource.invalidate();
260 
261             releaseReference();
262         }
263 
264         /**
265          * Removes a reference to this resource. If the resultant reference count is zero, the
266          * underlying resources are freed, and references to all child resources are also dropped
267          * recursively (resulting in them freeing their resources and children, etcetera)
268          *
269          * <p>This method also sets the reference count to an invalid value (-1) to signify that it
270          * has been fully released. Any subsequent calls to this method will result in an
271          * IllegalStateException being thrown due to resource already having been previously
272          * released
273          */
274         @VisibleForTesting
275         @GuardedBy("IpSecService.this")
releaseReference()276         public void releaseReference() throws RemoteException {
277             mRefCount--;
278 
279             if (mRefCount > 0) {
280                 return;
281             } else if (mRefCount < 0) {
282                 throw new IllegalStateException(
283                         "Invalid operation - resource has already been released.");
284             }
285 
286             // Cleanup own resources
287             mResource.freeUnderlyingResources();
288 
289             // Cleanup child resources as needed
290             for (RefcountedResource<? extends IResource> child : mChildren) {
291                 child.releaseReference();
292             }
293 
294             // Enforce that resource cleanup can only be called once
295             // By decrementing the refcount (from 0 to -1), the next call will throw an
296             // IllegalStateException - it has already been released fully.
297             mRefCount--;
298         }
299 
300         @Override
toString()301         public String toString() {
302             return new StringBuilder()
303                     .append("{mResource=")
304                     .append(mResource)
305                     .append(", mRefCount=")
306                     .append(mRefCount)
307                     .append(", mChildren=")
308                     .append(mChildren)
309                     .append("}")
310                     .toString();
311         }
312     }
313 
314     /**
315      * Very simple counting class that looks much like a counting semaphore
316      *
317      * <p>This class is not thread-safe, and expects that that users of this class will ensure
318      * synchronization and thread safety by holding the IpSecService.this instance lock.
319      */
320     @VisibleForTesting
321     static class ResourceTracker {
322         private final int mMax;
323         int mCurrent;
324 
ResourceTracker(int max)325         ResourceTracker(int max) {
326             mMax = max;
327             mCurrent = 0;
328         }
329 
isAvailable()330         boolean isAvailable() {
331             return (mCurrent < mMax);
332         }
333 
take()334         void take() {
335             if (!isAvailable()) {
336                 Log.wtf(TAG, "Too many resources allocated!");
337             }
338             mCurrent++;
339         }
340 
give()341         void give() {
342             if (mCurrent <= 0) {
343                 Log.wtf(TAG, "We've released this resource too many times");
344             }
345             mCurrent--;
346         }
347 
348         @Override
toString()349         public String toString() {
350             return new StringBuilder()
351                     .append("{mCurrent=")
352                     .append(mCurrent)
353                     .append(", mMax=")
354                     .append(mMax)
355                     .append("}")
356                     .toString();
357         }
358     }
359 
360     @VisibleForTesting
361     static final class UserRecord {
362         /* Maximum number of each type of resource that a single UID may possess */
363         public static final int MAX_NUM_TUNNEL_INTERFACES = 2;
364         public static final int MAX_NUM_ENCAP_SOCKETS = 2;
365         public static final int MAX_NUM_TRANSFORMS = 4;
366         public static final int MAX_NUM_SPIS = 8;
367 
368         /**
369          * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
370          * and explicit (user) reference management.
371          *
372          * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
373          *
374          * <p>Resources are removed from this array when the user releases their explicit reference
375          * by calling one of the releaseResource() methods.
376          */
377         final RefcountedResourceArray<SpiRecord> mSpiRecords =
378                 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
379         final RefcountedResourceArray<TransformRecord> mTransformRecords =
380                 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
381         final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
382                 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
383         final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
384                 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
385 
386         /**
387          * Trackers for quotas for each of the OwnedResource types.
388          *
389          * <p>These trackers are separate from the resource arrays, since they are incremented and
390          * decremented at different points in time. Specifically, quota is only returned upon final
391          * resource deallocation (after all explicit and implicit references are released). Note
392          * that it is possible that calls to releaseResource() will not return the used quota if
393          * there are other resources that depend on (are parents of) the resource being released.
394          */
395         final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
396         final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
397         final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
398         final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
399 
removeSpiRecord(int resourceId)400         void removeSpiRecord(int resourceId) {
401             mSpiRecords.remove(resourceId);
402         }
403 
removeTransformRecord(int resourceId)404         void removeTransformRecord(int resourceId) {
405             mTransformRecords.remove(resourceId);
406         }
407 
removeTunnelInterfaceRecord(int resourceId)408         void removeTunnelInterfaceRecord(int resourceId) {
409             mTunnelInterfaceRecords.remove(resourceId);
410         }
411 
removeEncapSocketRecord(int resourceId)412         void removeEncapSocketRecord(int resourceId) {
413             mEncapSocketRecords.remove(resourceId);
414         }
415 
416         @Override
toString()417         public String toString() {
418             return new StringBuilder()
419                     .append("{mSpiQuotaTracker=")
420                     .append(mSpiQuotaTracker)
421                     .append(", mTransformQuotaTracker=")
422                     .append(mTransformQuotaTracker)
423                     .append(", mSocketQuotaTracker=")
424                     .append(mSocketQuotaTracker)
425                     .append(", mTunnelQuotaTracker=")
426                     .append(mTunnelQuotaTracker)
427                     .append(", mSpiRecords=")
428                     .append(mSpiRecords)
429                     .append(", mTransformRecords=")
430                     .append(mTransformRecords)
431                     .append(", mEncapSocketRecords=")
432                     .append(mEncapSocketRecords)
433                     .append(", mTunnelInterfaceRecords=")
434                     .append(mTunnelInterfaceRecords)
435                     .append("}")
436                     .toString();
437         }
438     }
439 
440     /**
441      * This class is not thread-safe, and expects that that users of this class will ensure
442      * synchronization and thread safety by holding the IpSecService.this instance lock.
443      */
444     @VisibleForTesting
445     static final class UserResourceTracker {
446         private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
447 
448         /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
getUserRecord(int uid)449         public UserRecord getUserRecord(int uid) {
450             checkCallerUid(uid);
451 
452             UserRecord r = mUserRecords.get(uid);
453             if (r == null) {
454                 r = new UserRecord();
455                 mUserRecords.put(uid, r);
456             }
457             return r;
458         }
459 
460         /** Safety method; guards against access of other user's UserRecords */
checkCallerUid(int uid)461         private void checkCallerUid(int uid) {
462             if (uid != Binder.getCallingUid()
463                     && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
464                 throw new SecurityException("Attempted access of unowned resources");
465             }
466         }
467 
468         @Override
toString()469         public String toString() {
470             return mUserRecords.toString();
471         }
472     }
473 
474     @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
475 
476     /**
477      * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
478      * resources. It relies on a provided resourceId that should uniquely identify the kernel
479      * resource. To use this class, the user should implement the invalidate() and
480      * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
481      * tracking arrays and kernel resources, respectively.
482      *
483      * <p>This class associates kernel resources with the UID that owns and controls them.
484      */
485     private abstract class OwnedResourceRecord implements IResource {
486         final int pid;
487         final int uid;
488         protected final int mResourceId;
489 
OwnedResourceRecord(int resourceId)490         OwnedResourceRecord(int resourceId) {
491             super();
492             if (resourceId == INVALID_RESOURCE_ID) {
493                 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
494             }
495             mResourceId = resourceId;
496             pid = Binder.getCallingPid();
497             uid = Binder.getCallingUid();
498 
499             getResourceTracker().take();
500         }
501 
502         @Override
invalidate()503         public abstract void invalidate() throws RemoteException;
504 
505         /** Convenience method; retrieves the user resource record for the stored UID. */
getUserRecord()506         protected UserRecord getUserRecord() {
507             return mUserResourceTracker.getUserRecord(uid);
508         }
509 
510         @Override
freeUnderlyingResources()511         public abstract void freeUnderlyingResources() throws RemoteException;
512 
513         /** Get the resource tracker for this resource */
getResourceTracker()514         protected abstract ResourceTracker getResourceTracker();
515 
516         @Override
toString()517         public String toString() {
518             return new StringBuilder()
519                     .append("{mResourceId=")
520                     .append(mResourceId)
521                     .append(", pid=")
522                     .append(pid)
523                     .append(", uid=")
524                     .append(uid)
525                     .append("}")
526                     .toString();
527         }
528     };
529 
530     /**
531      * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
532      *
533      * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
534      * if a key is not found during a retrieval process.
535      */
536     static class RefcountedResourceArray<T extends IResource> {
537         SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
538         private final String mTypeName;
539 
RefcountedResourceArray(String typeName)540         public RefcountedResourceArray(String typeName) {
541             this.mTypeName = typeName;
542         }
543 
544         /**
545          * Accessor method to get inner resource object.
546          *
547          * @throws IllegalArgumentException if no resource with provided key is found.
548          */
getResourceOrThrow(int key)549         T getResourceOrThrow(int key) {
550             return getRefcountedResourceOrThrow(key).getResource();
551         }
552 
553         /**
554          * Accessor method to get reference counting wrapper.
555          *
556          * @throws IllegalArgumentException if no resource with provided key is found.
557          */
getRefcountedResourceOrThrow(int key)558         RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
559             RefcountedResource<T> resource = mArray.get(key);
560             if (resource == null) {
561                 throw new IllegalArgumentException(
562                         String.format("No such %s found for given id: %d", mTypeName, key));
563             }
564 
565             return resource;
566         }
567 
put(int key, RefcountedResource<T> obj)568         void put(int key, RefcountedResource<T> obj) {
569             checkNotNull(obj, "Null resources cannot be added");
570             mArray.put(key, obj);
571         }
572 
remove(int key)573         void remove(int key) {
574             mArray.remove(key);
575         }
576 
577         @Override
toString()578         public String toString() {
579             return mArray.toString();
580         }
581     }
582 
583     /**
584      * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
585      * created, the SpiRecord that originally tracked the SAs will reliquish the
586      * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
587      */
588     private final class TransformRecord extends OwnedResourceRecord {
589         private final IpSecConfig mConfig;
590         private final SpiRecord mSpi;
591         private final EncapSocketRecord mSocket;
592 
TransformRecord( int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket)593         TransformRecord(
594                 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
595             super(resourceId);
596             mConfig = config;
597             mSpi = spi;
598             mSocket = socket;
599 
600             spi.setOwnedByTransform();
601         }
602 
getConfig()603         public IpSecConfig getConfig() {
604             return mConfig;
605         }
606 
getSpiRecord()607         public SpiRecord getSpiRecord() {
608             return mSpi;
609         }
610 
getSocketRecord()611         public EncapSocketRecord getSocketRecord() {
612             return mSocket;
613         }
614 
615         /** always guarded by IpSecService#this */
616         @Override
freeUnderlyingResources()617         public void freeUnderlyingResources() {
618             int spi = mSpi.getSpi();
619             try {
620                 mSrvConfig
621                         .getNetdInstance()
622                         .ipSecDeleteSecurityAssociation(
623                                 uid,
624                                 mConfig.getSourceAddress(),
625                                 mConfig.getDestinationAddress(),
626                                 spi,
627                                 mConfig.getMarkValue(),
628                                 mConfig.getMarkMask(),
629                                 mConfig.getXfrmInterfaceId());
630             } catch (RemoteException | ServiceSpecificException e) {
631                 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
632             }
633 
634             getResourceTracker().give();
635         }
636 
637         @Override
invalidate()638         public void invalidate() throws RemoteException {
639             getUserRecord().removeTransformRecord(mResourceId);
640         }
641 
642         @Override
getResourceTracker()643         protected ResourceTracker getResourceTracker() {
644             return getUserRecord().mTransformQuotaTracker;
645         }
646 
647         @Override
toString()648         public String toString() {
649             StringBuilder strBuilder = new StringBuilder();
650             strBuilder
651                     .append("{super=")
652                     .append(super.toString())
653                     .append(", mSocket=")
654                     .append(mSocket)
655                     .append(", mSpi.mResourceId=")
656                     .append(mSpi.mResourceId)
657                     .append(", mConfig=")
658                     .append(mConfig)
659                     .append("}");
660             return strBuilder.toString();
661         }
662     }
663 
664     /**
665      * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
666      * responsibility for cleaning up underlying resources will be passed to the TransformRecord
667      * object
668      */
669     private final class SpiRecord extends OwnedResourceRecord {
670         private final String mSourceAddress;
671         private final String mDestinationAddress;
672         private int mSpi;
673 
674         private boolean mOwnedByTransform = false;
675 
SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi)676         SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
677             super(resourceId);
678             mSourceAddress = sourceAddress;
679             mDestinationAddress = destinationAddress;
680             mSpi = spi;
681         }
682 
683         /** always guarded by IpSecService#this */
684         @Override
freeUnderlyingResources()685         public void freeUnderlyingResources() {
686             try {
687                 if (!mOwnedByTransform) {
688                     mSrvConfig
689                             .getNetdInstance()
690                             .ipSecDeleteSecurityAssociation(
691                                     uid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
692                                     0 /* mask */, 0 /* if_id */);
693                 }
694             } catch (ServiceSpecificException | RemoteException e) {
695                 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
696             }
697 
698             mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
699 
700             getResourceTracker().give();
701         }
702 
getSpi()703         public int getSpi() {
704             return mSpi;
705         }
706 
getDestinationAddress()707         public String getDestinationAddress() {
708             return mDestinationAddress;
709         }
710 
setOwnedByTransform()711         public void setOwnedByTransform() {
712             if (mOwnedByTransform) {
713                 // Programming error
714                 throw new IllegalStateException("Cannot own an SPI twice!");
715             }
716 
717             mOwnedByTransform = true;
718         }
719 
getOwnedByTransform()720         public boolean getOwnedByTransform() {
721             return mOwnedByTransform;
722         }
723 
724         @Override
invalidate()725         public void invalidate() throws RemoteException {
726             getUserRecord().removeSpiRecord(mResourceId);
727         }
728 
729         @Override
getResourceTracker()730         protected ResourceTracker getResourceTracker() {
731             return getUserRecord().mSpiQuotaTracker;
732         }
733 
734         @Override
toString()735         public String toString() {
736             StringBuilder strBuilder = new StringBuilder();
737             strBuilder
738                     .append("{super=")
739                     .append(super.toString())
740                     .append(", mSpi=")
741                     .append(mSpi)
742                     .append(", mSourceAddress=")
743                     .append(mSourceAddress)
744                     .append(", mDestinationAddress=")
745                     .append(mDestinationAddress)
746                     .append(", mOwnedByTransform=")
747                     .append(mOwnedByTransform)
748                     .append("}");
749             return strBuilder.toString();
750         }
751     }
752 
753     // These values have been reserved in ConnectivityService
754     @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
755 
756     @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400;
757 
758     private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
759     private int mNextTunnelNetIdIndex = 0;
760 
761     /**
762      * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
763      *
764      * <p>This method should only be called from Binder threads. Do not call this from within the
765      * system server as it will crash the system on failure.
766      *
767      * @return an integer key within the netId range, if successful
768      * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
769      */
770     @VisibleForTesting
reserveNetId()771     int reserveNetId() {
772         synchronized (mTunnelNetIds) {
773             for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
774                 int index = mNextTunnelNetIdIndex;
775                 int netId = index + TUN_INTF_NETID_START;
776                 if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
777                 if (!mTunnelNetIds.get(netId)) {
778                     mTunnelNetIds.put(netId, true);
779                     return netId;
780                 }
781             }
782         }
783         throw new IllegalStateException("No free netIds to allocate");
784     }
785 
786     @VisibleForTesting
releaseNetId(int netId)787     void releaseNetId(int netId) {
788         synchronized (mTunnelNetIds) {
789             mTunnelNetIds.delete(netId);
790         }
791     }
792 
793     private final class TunnelInterfaceRecord extends OwnedResourceRecord {
794         private final String mInterfaceName;
795         private final Network mUnderlyingNetwork;
796 
797         // outer addresses
798         private final String mLocalAddress;
799         private final String mRemoteAddress;
800 
801         private final int mIkey;
802         private final int mOkey;
803 
804         private final int mIfId;
805 
TunnelInterfaceRecord( int resourceId, String interfaceName, Network underlyingNetwork, String localAddr, String remoteAddr, int ikey, int okey, int intfId)806         TunnelInterfaceRecord(
807                 int resourceId,
808                 String interfaceName,
809                 Network underlyingNetwork,
810                 String localAddr,
811                 String remoteAddr,
812                 int ikey,
813                 int okey,
814                 int intfId) {
815             super(resourceId);
816 
817             mInterfaceName = interfaceName;
818             mUnderlyingNetwork = underlyingNetwork;
819             mLocalAddress = localAddr;
820             mRemoteAddress = remoteAddr;
821             mIkey = ikey;
822             mOkey = okey;
823             mIfId = intfId;
824         }
825 
826         /** always guarded by IpSecService#this */
827         @Override
freeUnderlyingResources()828         public void freeUnderlyingResources() {
829             // Calls to netd
830             //       Teardown VTI
831             //       Delete global policies
832             try {
833                 final INetd netd = mSrvConfig.getNetdInstance();
834                 netd.ipSecRemoveTunnelInterface(mInterfaceName);
835 
836                 for (int selAddrFamily : ADDRESS_FAMILIES) {
837                     netd.ipSecDeleteSecurityPolicy(
838                             uid,
839                             selAddrFamily,
840                             IpSecManager.DIRECTION_OUT,
841                             mOkey,
842                             0xffffffff,
843                             mIfId);
844                     netd.ipSecDeleteSecurityPolicy(
845                             uid,
846                             selAddrFamily,
847                             IpSecManager.DIRECTION_IN,
848                             mIkey,
849                             0xffffffff,
850                             mIfId);
851                 }
852             } catch (ServiceSpecificException | RemoteException e) {
853                 Log.e(
854                         TAG,
855                         "Failed to delete VTI with interface name: "
856                                 + mInterfaceName
857                                 + " and id: "
858                                 + mResourceId, e);
859             }
860 
861             getResourceTracker().give();
862             releaseNetId(mIkey);
863             releaseNetId(mOkey);
864         }
865 
getInterfaceName()866         public String getInterfaceName() {
867             return mInterfaceName;
868         }
869 
getUnderlyingNetwork()870         public Network getUnderlyingNetwork() {
871             return mUnderlyingNetwork;
872         }
873 
874         /** Returns the local, outer address for the tunnelInterface */
getLocalAddress()875         public String getLocalAddress() {
876             return mLocalAddress;
877         }
878 
879         /** Returns the remote, outer address for the tunnelInterface */
getRemoteAddress()880         public String getRemoteAddress() {
881             return mRemoteAddress;
882         }
883 
getIkey()884         public int getIkey() {
885             return mIkey;
886         }
887 
getOkey()888         public int getOkey() {
889             return mOkey;
890         }
891 
getIfId()892         public int getIfId() {
893             return mIfId;
894         }
895 
896         @Override
getResourceTracker()897         protected ResourceTracker getResourceTracker() {
898             return getUserRecord().mTunnelQuotaTracker;
899         }
900 
901         @Override
invalidate()902         public void invalidate() {
903             getUserRecord().removeTunnelInterfaceRecord(mResourceId);
904         }
905 
906         @Override
toString()907         public String toString() {
908             return new StringBuilder()
909                     .append("{super=")
910                     .append(super.toString())
911                     .append(", mInterfaceName=")
912                     .append(mInterfaceName)
913                     .append(", mUnderlyingNetwork=")
914                     .append(mUnderlyingNetwork)
915                     .append(", mLocalAddress=")
916                     .append(mLocalAddress)
917                     .append(", mRemoteAddress=")
918                     .append(mRemoteAddress)
919                     .append(", mIkey=")
920                     .append(mIkey)
921                     .append(", mOkey=")
922                     .append(mOkey)
923                     .append("}")
924                     .toString();
925         }
926     }
927 
928     /**
929      * Tracks a UDP encap socket, and manages cleanup paths
930      *
931      * <p>While this class does not manage non-kernel resources, race conditions around socket
932      * binding require that the service creates the encap socket, binds it and applies the socket
933      * policy before handing it to a user.
934      */
935     private final class EncapSocketRecord extends OwnedResourceRecord {
936         private FileDescriptor mSocket;
937         private final int mPort;
938 
EncapSocketRecord(int resourceId, FileDescriptor socket, int port)939         EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
940             super(resourceId);
941             mSocket = socket;
942             mPort = port;
943         }
944 
945         /** always guarded by IpSecService#this */
946         @Override
freeUnderlyingResources()947         public void freeUnderlyingResources() {
948             Log.d(TAG, "Closing port " + mPort);
949             IoUtils.closeQuietly(mSocket);
950             mSocket = null;
951 
952             getResourceTracker().give();
953         }
954 
getPort()955         public int getPort() {
956             return mPort;
957         }
958 
getFileDescriptor()959         public FileDescriptor getFileDescriptor() {
960             return mSocket;
961         }
962 
963         @Override
getResourceTracker()964         protected ResourceTracker getResourceTracker() {
965             return getUserRecord().mSocketQuotaTracker;
966         }
967 
968         @Override
invalidate()969         public void invalidate() {
970             getUserRecord().removeEncapSocketRecord(mResourceId);
971         }
972 
973         @Override
toString()974         public String toString() {
975             return new StringBuilder()
976                     .append("{super=")
977                     .append(super.toString())
978                     .append(", mSocket=")
979                     .append(mSocket)
980                     .append(", mPort=")
981                     .append(mPort)
982                     .append("}")
983                     .toString();
984         }
985     }
986 
987     /**
988      * Constructs a new IpSecService instance
989      *
990      * @param context Binder context for this service
991      */
IpSecService(Context context)992     private IpSecService(Context context) {
993         this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
994     }
995 
create(Context context)996     static IpSecService create(Context context) throws InterruptedException {
997         final IpSecService service = new IpSecService(context);
998         service.connectNativeNetdService();
999         return service;
1000     }
1001 
1002     @NonNull
getAppOpsManager()1003     private AppOpsManager getAppOpsManager() {
1004         AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
1005         if(appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
1006         return appOps;
1007     }
1008 
1009     /** @hide */
1010     @VisibleForTesting
IpSecService(Context context, IpSecServiceConfiguration config)1011     public IpSecService(Context context, IpSecServiceConfiguration config) {
1012         this(
1013                 context,
1014                 config,
1015                 (fd, uid) -> {
1016                     try {
1017                         TrafficStats.setThreadStatsUid(uid);
1018                         TrafficStats.tagFileDescriptor(fd);
1019                     } finally {
1020                         TrafficStats.clearThreadStatsUid();
1021                     }
1022                 });
1023     }
1024 
1025     /** @hide */
1026     @VisibleForTesting
IpSecService( Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger)1027     public IpSecService(
1028             Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
1029         mContext = context;
1030         mSrvConfig = config;
1031         mUidFdTagger = uidFdTagger;
1032     }
1033 
systemReady()1034     public void systemReady() {
1035         if (isNetdAlive()) {
1036             Slog.d(TAG, "IpSecService is ready");
1037         } else {
1038             Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
1039         }
1040     }
1041 
connectNativeNetdService()1042     private void connectNativeNetdService() {
1043         // Avoid blocking the system server to do this
1044         new Thread() {
1045             @Override
1046             public void run() {
1047                 synchronized (IpSecService.this) {
1048                     NetdService.get(NETD_FETCH_TIMEOUT_MS);
1049                 }
1050             }
1051         }.start();
1052     }
1053 
isNetdAlive()1054     synchronized boolean isNetdAlive() {
1055         try {
1056             final INetd netd = mSrvConfig.getNetdInstance();
1057             if (netd == null) {
1058                 return false;
1059             }
1060             return netd.isAlive();
1061         } catch (RemoteException re) {
1062             return false;
1063         }
1064     }
1065 
1066     /**
1067      * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1068      * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1069      */
checkInetAddress(String inetAddress)1070     private static void checkInetAddress(String inetAddress) {
1071         if (TextUtils.isEmpty(inetAddress)) {
1072             throw new IllegalArgumentException("Unspecified address");
1073         }
1074 
1075         InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
1076 
1077         if (checkAddr.isAnyLocalAddress()) {
1078             throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1079         }
1080     }
1081 
1082     /**
1083      * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1084      * DIRECTION_IN or DIRECTION_OUT
1085      */
checkDirection(int direction)1086     private static void checkDirection(int direction) {
1087         switch (direction) {
1088             case IpSecManager.DIRECTION_OUT:
1089             case IpSecManager.DIRECTION_IN:
1090                 return;
1091         }
1092         throw new IllegalArgumentException("Invalid Direction: " + direction);
1093     }
1094 
1095     /** Get a new SPI and maintain the reservation in the system server */
1096     @Override
allocateSecurityParameterIndex( String destinationAddress, int requestedSpi, IBinder binder)1097     public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
1098             String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1099         checkInetAddress(destinationAddress);
1100         // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1101         if (requestedSpi > 0 && requestedSpi < 256) {
1102             throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1103         }
1104         checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
1105 
1106         int callingUid = Binder.getCallingUid();
1107         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
1108         final int resourceId = mNextResourceId++;
1109 
1110         int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1111         try {
1112             if (!userRecord.mSpiQuotaTracker.isAvailable()) {
1113                 return new IpSecSpiResponse(
1114                         IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1115             }
1116 
1117             spi =
1118                     mSrvConfig
1119                             .getNetdInstance()
1120                             .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
1121             Log.d(TAG, "Allocated SPI " + spi);
1122             userRecord.mSpiRecords.put(
1123                     resourceId,
1124                     new RefcountedResource<SpiRecord>(
1125                             new SpiRecord(resourceId, "", destinationAddress, spi), binder));
1126         } catch (ServiceSpecificException e) {
1127             if (e.errorCode == OsConstants.ENOENT) {
1128                 return new IpSecSpiResponse(
1129                         IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1130             }
1131             throw e;
1132         } catch (RemoteException e) {
1133             throw e.rethrowFromSystemServer();
1134         }
1135         return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1136     }
1137 
1138     /* This method should only be called from Binder threads. Do not call this from
1139      * within the system server as it will crash the system on failure.
1140      */
releaseResource(RefcountedResourceArray resArray, int resourceId)1141     private void releaseResource(RefcountedResourceArray resArray, int resourceId)
1142             throws RemoteException {
1143         resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
1144     }
1145 
1146     /** Release a previously allocated SPI that has been registered with the system server */
1147     @Override
releaseSecurityParameterIndex(int resourceId)1148     public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1149         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1150         releaseResource(userRecord.mSpiRecords, resourceId);
1151     }
1152 
1153     /**
1154      * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1155      * be unbound.
1156      *
1157      * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1158      * a random open port and then bind by number, this function creates a temp socket, binds to a
1159      * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1160      * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1161      * FileHandle.
1162      *
1163      * <p>The loop in this function handles the inherent race window between un-binding to a port
1164      * and re-binding, during which the system could *technically* hand that port out to someone
1165      * else.
1166      */
bindToRandomPort(FileDescriptor sockFd)1167     private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
1168         for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1169             try {
1170                 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1171                 Os.bind(probeSocket, INADDR_ANY, 0);
1172                 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1173                 Os.close(probeSocket);
1174                 Log.v(TAG, "Binding to port " + port);
1175                 Os.bind(sockFd, INADDR_ANY, port);
1176                 return port;
1177             } catch (ErrnoException e) {
1178                 // Someone miraculously claimed the port just after we closed probeSocket.
1179                 if (e.errno == OsConstants.EADDRINUSE) {
1180                     continue;
1181                 }
1182                 throw e.rethrowAsIOException();
1183             }
1184         }
1185         throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1186     }
1187 
1188     /**
1189      * Functional interface to do traffic tagging of given sockets to UIDs.
1190      *
1191      * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1192      * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1193      *
1194      * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1195      * methods that cannot be easily mocked/tested.
1196      */
1197     @VisibleForTesting
1198     public interface UidFdTagger {
1199         /**
1200          * Sets socket tag to assign all traffic to the provided UID.
1201          *
1202          * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1203          * should be accounted to the UID of the unprivileged application.
1204          */
tag(FileDescriptor fd, int uid)1205         public void tag(FileDescriptor fd, int uid) throws IOException;
1206     }
1207 
1208     /**
1209      * Open a socket via the system server and bind it to the specified port (random if port=0).
1210      * This will return a PFD to the user that represent a bound UDP socket. The system server will
1211      * cache the socket and a record of its owner so that it can and must be freed when no longer
1212      * needed.
1213      */
1214     @Override
openUdpEncapsulationSocket(int port, IBinder binder)1215     public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1216             throws RemoteException {
1217         if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1218             throw new IllegalArgumentException(
1219                     "Specified port number must be a valid non-reserved UDP port");
1220         }
1221         checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
1222 
1223         int callingUid = Binder.getCallingUid();
1224         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
1225         final int resourceId = mNextResourceId++;
1226         FileDescriptor sockFd = null;
1227         try {
1228             if (!userRecord.mSocketQuotaTracker.isAvailable()) {
1229                 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1230             }
1231 
1232             sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1233             mUidFdTagger.tag(sockFd, callingUid);
1234 
1235             // This code is common to both the unspecified and specified port cases
1236             Os.setsockoptInt(
1237                     sockFd,
1238                     OsConstants.IPPROTO_UDP,
1239                     OsConstants.UDP_ENCAP,
1240                     OsConstants.UDP_ENCAP_ESPINUDP);
1241 
1242             mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(
1243                         new ParcelFileDescriptor(sockFd), callingUid);
1244             if (port != 0) {
1245                 Log.v(TAG, "Binding to port " + port);
1246                 Os.bind(sockFd, INADDR_ANY, port);
1247             } else {
1248                 port = bindToRandomPort(sockFd);
1249             }
1250 
1251             userRecord.mEncapSocketRecords.put(
1252                     resourceId,
1253                     new RefcountedResource<EncapSocketRecord>(
1254                             new EncapSocketRecord(resourceId, sockFd, port), binder));
1255             return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
1256         } catch (IOException | ErrnoException e) {
1257             IoUtils.closeQuietly(sockFd);
1258         }
1259         // If we make it to here, then something has gone wrong and we couldn't open a socket.
1260         // The only reasonable condition that would cause that is resource unavailable.
1261         return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1262     }
1263 
1264     /** close a socket that has been been allocated by and registered with the system server */
1265     @Override
closeUdpEncapsulationSocket(int resourceId)1266     public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1267         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1268         releaseResource(userRecord.mEncapSocketRecords, resourceId);
1269     }
1270 
1271     /**
1272      * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1273      * tunnel interface and a record of its owner so that it can and must be freed when no longer
1274      * needed.
1275      */
1276     @Override
createTunnelInterface( String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder, String callingPackage)1277     public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
1278             String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1279             String callingPackage) {
1280         enforceTunnelFeatureAndPermissions(callingPackage);
1281         checkNotNull(binder, "Null Binder passed to createTunnelInterface");
1282         checkNotNull(underlyingNetwork, "No underlying network was specified");
1283         checkInetAddress(localAddr);
1284         checkInetAddress(remoteAddr);
1285 
1286         // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1287         //       network (b/72316676).
1288 
1289         int callerUid = Binder.getCallingUid();
1290         UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
1291         if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1292             return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1293         }
1294 
1295         final int resourceId = mNextResourceId++;
1296         final int ikey = reserveNetId();
1297         final int okey = reserveNetId();
1298         String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
1299 
1300         try {
1301             // Calls to netd:
1302             //       Create VTI
1303             //       Add inbound/outbound global policies
1304             //              (use reqid = 0)
1305             final INetd netd = mSrvConfig.getNetdInstance();
1306             netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
1307 
1308             for (int selAddrFamily : ADDRESS_FAMILIES) {
1309                 // Always send down correct local/remote addresses for template.
1310                 netd.ipSecAddSecurityPolicy(
1311                         callerUid,
1312                         selAddrFamily,
1313                         IpSecManager.DIRECTION_OUT,
1314                         localAddr,
1315                         remoteAddr,
1316                         0,
1317                         okey,
1318                         0xffffffff,
1319                         resourceId);
1320                 netd.ipSecAddSecurityPolicy(
1321                         callerUid,
1322                         selAddrFamily,
1323                         IpSecManager.DIRECTION_IN,
1324                         remoteAddr,
1325                         localAddr,
1326                         0,
1327                         ikey,
1328                         0xffffffff,
1329                         resourceId);
1330             }
1331 
1332             userRecord.mTunnelInterfaceRecords.put(
1333                     resourceId,
1334                     new RefcountedResource<TunnelInterfaceRecord>(
1335                             new TunnelInterfaceRecord(
1336                                     resourceId,
1337                                     intfName,
1338                                     underlyingNetwork,
1339                                     localAddr,
1340                                     remoteAddr,
1341                                     ikey,
1342                                     okey,
1343                                     resourceId),
1344                             binder));
1345             return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1346         } catch (RemoteException e) {
1347             // Release keys if we got an error.
1348             releaseNetId(ikey);
1349             releaseNetId(okey);
1350             throw e.rethrowFromSystemServer();
1351         } catch (Throwable t) {
1352             // Release keys if we got an error.
1353             releaseNetId(ikey);
1354             releaseNetId(okey);
1355             throw t;
1356         }
1357     }
1358 
1359     /**
1360      * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1361      * from multiple local IP addresses over the same tunnel.
1362      */
1363     @Override
addAddressToTunnelInterface( int tunnelResourceId, LinkAddress localAddr, String callingPackage)1364     public synchronized void addAddressToTunnelInterface(
1365             int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
1366         enforceTunnelFeatureAndPermissions(callingPackage);
1367         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1368 
1369         // Get tunnelInterface record; if no such interface is found, will throw
1370         // IllegalArgumentException
1371         TunnelInterfaceRecord tunnelInterfaceInfo =
1372                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1373 
1374         try {
1375             // We can assume general validity of the IP address, since we get them as a
1376             // LinkAddress, which does some validation.
1377             mSrvConfig
1378                     .getNetdInstance()
1379                     .interfaceAddAddress(
1380                             tunnelInterfaceInfo.mInterfaceName,
1381                             localAddr.getAddress().getHostAddress(),
1382                             localAddr.getPrefixLength());
1383         } catch (RemoteException e) {
1384             throw e.rethrowFromSystemServer();
1385         }
1386     }
1387 
1388     /**
1389      * Remove a new local address from the tunnel interface. After removal, the address will no
1390      * longer be available to send from, or receive on.
1391      */
1392     @Override
removeAddressFromTunnelInterface( int tunnelResourceId, LinkAddress localAddr, String callingPackage)1393     public synchronized void removeAddressFromTunnelInterface(
1394             int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
1395         enforceTunnelFeatureAndPermissions(callingPackage);
1396 
1397         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1398         // Get tunnelInterface record; if no such interface is found, will throw
1399         // IllegalArgumentException
1400         TunnelInterfaceRecord tunnelInterfaceInfo =
1401                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1402 
1403         try {
1404             // We can assume general validity of the IP address, since we get them as a
1405             // LinkAddress, which does some validation.
1406             mSrvConfig
1407                     .getNetdInstance()
1408                     .interfaceDelAddress(
1409                             tunnelInterfaceInfo.mInterfaceName,
1410                             localAddr.getAddress().getHostAddress(),
1411                             localAddr.getPrefixLength());
1412         } catch (RemoteException e) {
1413             throw e.rethrowFromSystemServer();
1414         }
1415     }
1416 
1417     /**
1418      * Delete a TunnelInterface that has been been allocated by and registered with the system
1419      * server
1420      */
1421     @Override
deleteTunnelInterface( int resourceId, String callingPackage)1422     public synchronized void deleteTunnelInterface(
1423             int resourceId, String callingPackage) throws RemoteException {
1424         enforceTunnelFeatureAndPermissions(callingPackage);
1425         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1426         releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1427     }
1428 
1429     @VisibleForTesting
validateAlgorithms(IpSecConfig config)1430     void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1431         IpSecAlgorithm auth = config.getAuthentication();
1432         IpSecAlgorithm crypt = config.getEncryption();
1433         IpSecAlgorithm aead = config.getAuthenticatedEncryption();
1434 
1435         // Validate the algorithm set
1436         Preconditions.checkArgument(
1437                 aead != null || crypt != null || auth != null,
1438                 "No Encryption or Authentication algorithms specified");
1439         Preconditions.checkArgument(
1440                 auth == null || auth.isAuthentication(),
1441                 "Unsupported algorithm for Authentication");
1442         Preconditions.checkArgument(
1443                 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
1444         Preconditions.checkArgument(
1445                 aead == null || aead.isAead(),
1446                 "Unsupported algorithm for Authenticated Encryption");
1447         Preconditions.checkArgument(
1448                 aead == null || (auth == null && crypt == null),
1449                 "Authenticated Encryption is mutually exclusive with other Authentication "
1450                         + "or Encryption algorithms");
1451     }
1452 
getFamily(String inetAddress)1453     private int getFamily(String inetAddress) {
1454         int family = AF_UNSPEC;
1455         InetAddress checkAddress = NetworkUtils.numericToInetAddress(inetAddress);
1456         if (checkAddress instanceof Inet4Address) {
1457             family = AF_INET;
1458         } else if (checkAddress instanceof Inet6Address) {
1459             family = AF_INET6;
1460         }
1461         return family;
1462     }
1463 
1464     /**
1465      * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
1466      * IllegalArgumentException if they are not.
1467      */
checkIpSecConfig(IpSecConfig config)1468     private void checkIpSecConfig(IpSecConfig config) {
1469         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1470 
1471         switch (config.getEncapType()) {
1472             case IpSecTransform.ENCAP_NONE:
1473                 break;
1474             case IpSecTransform.ENCAP_ESPINUDP:
1475             case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
1476                 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1477                 userRecord.mEncapSocketRecords.getResourceOrThrow(
1478                         config.getEncapSocketResourceId());
1479 
1480                 int port = config.getEncapRemotePort();
1481                 if (port <= 0 || port > 0xFFFF) {
1482                     throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1483                 }
1484                 break;
1485             default:
1486                 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1487         }
1488 
1489         validateAlgorithms(config);
1490 
1491         // Retrieve SPI record; will throw IllegalArgumentException if not found
1492         SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1493 
1494         // Check to ensure that SPI has not already been used.
1495         if (s.getOwnedByTransform()) {
1496             throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1497         }
1498 
1499         // If no remote address is supplied, then use one from the SPI.
1500         if (TextUtils.isEmpty(config.getDestinationAddress())) {
1501             config.setDestinationAddress(s.getDestinationAddress());
1502         }
1503 
1504         // All remote addresses must match
1505         if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1506             throw new IllegalArgumentException("Mismatched remote addresseses.");
1507         }
1508 
1509         // This check is technically redundant due to the chain of custody between the SPI and
1510         // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1511         // the transform, this will prevent us from messing up.
1512         checkInetAddress(config.getDestinationAddress());
1513 
1514         // Require a valid source address for all transforms.
1515         checkInetAddress(config.getSourceAddress());
1516 
1517         // Check to ensure source and destination have the same address family.
1518         String sourceAddress = config.getSourceAddress();
1519         String destinationAddress = config.getDestinationAddress();
1520         int sourceFamily = getFamily(sourceAddress);
1521         int destinationFamily = getFamily(destinationAddress);
1522         if (sourceFamily != destinationFamily) {
1523             throw new IllegalArgumentException(
1524                     "Source address ("
1525                             + sourceAddress
1526                             + ") and destination address ("
1527                             + destinationAddress
1528                             + ") have different address families.");
1529         }
1530 
1531         // Throw an error if UDP Encapsulation is not used in IPv4.
1532         if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
1533             throw new IllegalArgumentException(
1534                     "UDP Encapsulation is not supported for this address family");
1535         }
1536 
1537         switch (config.getMode()) {
1538             case IpSecTransform.MODE_TRANSPORT:
1539                 break;
1540             case IpSecTransform.MODE_TUNNEL:
1541                 break;
1542             default:
1543                 throw new IllegalArgumentException(
1544                         "Invalid IpSecTransform.mode: " + config.getMode());
1545         }
1546 
1547         config.setMarkValue(0);
1548         config.setMarkMask(0);
1549     }
1550 
1551     private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
1552 
enforceTunnelFeatureAndPermissions(String callingPackage)1553     private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1554         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1555             throw new UnsupportedOperationException(
1556                     "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1557         }
1558 
1559         checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
1560         switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1561             case AppOpsManager.MODE_DEFAULT:
1562                 mContext.enforceCallingOrSelfPermission(
1563                         android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
1564                 break;
1565             case AppOpsManager.MODE_ALLOWED:
1566                 return;
1567             default:
1568                 throw new SecurityException("Request to ignore AppOps for non-legacy API");
1569         }
1570     }
1571 
createOrUpdateTransform( IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)1572     private void createOrUpdateTransform(
1573             IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1574             throws RemoteException {
1575 
1576         int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1577         if (encapType != IpSecTransform.ENCAP_NONE) {
1578             encapLocalPort = socketRecord.getPort();
1579             encapRemotePort = c.getEncapRemotePort();
1580         }
1581 
1582         IpSecAlgorithm auth = c.getAuthentication();
1583         IpSecAlgorithm crypt = c.getEncryption();
1584         IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1585 
1586         String cryptName;
1587         if (crypt == null) {
1588             cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1589         } else {
1590             cryptName = crypt.getName();
1591         }
1592 
1593         mSrvConfig
1594                 .getNetdInstance()
1595                 .ipSecAddSecurityAssociation(
1596                         Binder.getCallingUid(),
1597                         c.getMode(),
1598                         c.getSourceAddress(),
1599                         c.getDestinationAddress(),
1600                         (c.getNetwork() != null) ? c.getNetwork().netId : 0,
1601                         spiRecord.getSpi(),
1602                         c.getMarkValue(),
1603                         c.getMarkMask(),
1604                         (auth != null) ? auth.getName() : "",
1605                         (auth != null) ? auth.getKey() : new byte[] {},
1606                         (auth != null) ? auth.getTruncationLengthBits() : 0,
1607                         cryptName,
1608                         (crypt != null) ? crypt.getKey() : new byte[] {},
1609                         (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1610                         (authCrypt != null) ? authCrypt.getName() : "",
1611                         (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1612                         (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1613                         encapType,
1614                         encapLocalPort,
1615                         encapRemotePort,
1616                         c.getXfrmInterfaceId());
1617     }
1618 
1619     /**
1620      * Create a IPsec transform, which represents a single security association in the kernel. The
1621      * transform will be cached by the system server and must be freed when no longer needed. It is
1622      * possible to free one, deleting the SA from underneath sockets that are using it, which will
1623      * result in all of those sockets becoming unable to send or receive data.
1624      */
1625     @Override
createTransform( IpSecConfig c, IBinder binder, String callingPackage)1626     public synchronized IpSecTransformResponse createTransform(
1627             IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
1628         checkNotNull(c);
1629         if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
1630             enforceTunnelFeatureAndPermissions(callingPackage);
1631         }
1632         checkIpSecConfig(c);
1633         checkNotNull(binder, "Null Binder passed to createTransform");
1634         final int resourceId = mNextResourceId++;
1635 
1636         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1637         List<RefcountedResource> dependencies = new ArrayList<>();
1638 
1639         if (!userRecord.mTransformQuotaTracker.isAvailable()) {
1640             return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1641         }
1642 
1643         EncapSocketRecord socketRecord = null;
1644         if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1645             RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1646                     userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1647                             c.getEncapSocketResourceId());
1648             dependencies.add(refcountedSocketRecord);
1649             socketRecord = refcountedSocketRecord.getResource();
1650         }
1651 
1652         RefcountedResource<SpiRecord> refcountedSpiRecord =
1653                 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1654         dependencies.add(refcountedSpiRecord);
1655         SpiRecord spiRecord = refcountedSpiRecord.getResource();
1656 
1657         createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
1658 
1659         // SA was created successfully, time to construct a record and lock it away
1660         userRecord.mTransformRecords.put(
1661                 resourceId,
1662                 new RefcountedResource<TransformRecord>(
1663                         new TransformRecord(resourceId, c, spiRecord, socketRecord),
1664                         binder,
1665                         dependencies.toArray(new RefcountedResource[dependencies.size()])));
1666         return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
1667     }
1668 
1669     /**
1670      * Delete a transport mode transform that was previously allocated by + registered with the
1671      * system server. If this is called on an inactive (or non-existent) transform, it will not
1672      * return an error. It's safe to de-allocate transforms that may have already been deleted for
1673      * other reasons.
1674      */
1675     @Override
deleteTransform(int resourceId)1676     public synchronized void deleteTransform(int resourceId) throws RemoteException {
1677         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1678         releaseResource(userRecord.mTransformRecords, resourceId);
1679     }
1680 
1681     /**
1682      * Apply an active transport mode transform to a socket, which will apply the IPsec security
1683      * association as a correspondent policy to the provided socket
1684      */
1685     @Override
applyTransportModeTransform( ParcelFileDescriptor socket, int direction, int resourceId)1686     public synchronized void applyTransportModeTransform(
1687             ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
1688         int callingUid = Binder.getCallingUid();
1689         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
1690         checkDirection(direction);
1691         // Get transform record; if no transform is found, will throw IllegalArgumentException
1692         TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
1693 
1694         // TODO: make this a function.
1695         if (info.pid != getCallingPid() || info.uid != callingUid) {
1696             throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1697         }
1698 
1699         // Get config and check that to-be-applied transform has the correct mode
1700         IpSecConfig c = info.getConfig();
1701         Preconditions.checkArgument(
1702                 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1703                 "Transform mode was not Transport mode; cannot be applied to a socket");
1704 
1705         mSrvConfig
1706                 .getNetdInstance()
1707                 .ipSecApplyTransportModeTransform(
1708                         socket,
1709                         callingUid,
1710                         direction,
1711                         c.getSourceAddress(),
1712                         c.getDestinationAddress(),
1713                         info.getSpiRecord().getSpi());
1714     }
1715 
1716     /**
1717      * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1718      * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1719      * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1720      * reserved for future improved input validation.
1721      */
1722     @Override
removeTransportModeTransforms(ParcelFileDescriptor socket)1723     public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1724             throws RemoteException {
1725         mSrvConfig
1726                 .getNetdInstance()
1727                 .ipSecRemoveTransportModeTransform(socket);
1728     }
1729 
1730     /**
1731      * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1732      * security association as a correspondent policy to the provided interface
1733      */
1734     @Override
applyTunnelModeTransform( int tunnelResourceId, int direction, int transformResourceId, String callingPackage)1735     public synchronized void applyTunnelModeTransform(
1736             int tunnelResourceId, int direction,
1737             int transformResourceId, String callingPackage) throws RemoteException {
1738         enforceTunnelFeatureAndPermissions(callingPackage);
1739         checkDirection(direction);
1740 
1741         int callingUid = Binder.getCallingUid();
1742         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
1743 
1744         // Get transform record; if no transform is found, will throw IllegalArgumentException
1745         TransformRecord transformInfo =
1746                 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1747 
1748         // Get tunnelInterface record; if no such interface is found, will throw
1749         // IllegalArgumentException
1750         TunnelInterfaceRecord tunnelInterfaceInfo =
1751                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1752 
1753         // Get config and check that to-be-applied transform has the correct mode
1754         IpSecConfig c = transformInfo.getConfig();
1755         Preconditions.checkArgument(
1756                 c.getMode() == IpSecTransform.MODE_TUNNEL,
1757                 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1758 
1759         EncapSocketRecord socketRecord = null;
1760         if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1761             socketRecord =
1762                     userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1763         }
1764         SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId());
1765 
1766         int mark =
1767                 (direction == IpSecManager.DIRECTION_OUT)
1768                         ? tunnelInterfaceInfo.getOkey()
1769                         : tunnelInterfaceInfo.getIkey();
1770 
1771         try {
1772             // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
1773             // SPI matching as part of the template resolution.
1774             int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1775             c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
1776 
1777             // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
1778             //     (and backporting) would allow us to narrow the mark space, and ensure that the SA
1779             //     and SPs have matching marks (as VTI are meant to be built).
1780             // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
1781             //     config matches the actual allocated resources in the kernel.
1782             // All SAs will have zero marks (from creation time), and any policy that matches the
1783             //     same src/dst could match these SAs. Non-IpSecService governed processes that
1784             //     establish floating policies with the same src/dst may result in undefined
1785             //     behavior. This is generally limited to vendor code due to the permissions
1786             //     (CAP_NET_ADMIN) required.
1787             //
1788             // c.setMarkValue(mark);
1789             // c.setMarkMask(0xffffffff);
1790 
1791             if (direction == IpSecManager.DIRECTION_OUT) {
1792                 // Set output mark via underlying network (output only)
1793                 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1794 
1795                 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
1796                 // but want to guarantee outbound packets are sent over the new SA.
1797                 spi = transformInfo.getSpiRecord().getSpi();
1798             }
1799 
1800             // Always update the policy with the relevant XFRM_IF_ID
1801             for (int selAddrFamily : ADDRESS_FAMILIES) {
1802                 mSrvConfig
1803                         .getNetdInstance()
1804                         .ipSecUpdateSecurityPolicy(
1805                                 callingUid,
1806                                 selAddrFamily,
1807                                 direction,
1808                                 transformInfo.getConfig().getSourceAddress(),
1809                                 transformInfo.getConfig().getDestinationAddress(),
1810                                 spi, // If outbound, also add SPI to the policy.
1811                                 mark, // Must always set policy mark; ikey/okey for VTIs
1812                                 0xffffffff,
1813                                 c.getXfrmInterfaceId());
1814             }
1815 
1816             // Update SA with tunnel mark (ikey or okey based on direction)
1817             createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1818         } catch (ServiceSpecificException e) {
1819             if (e.errorCode == EINVAL) {
1820                 throw new IllegalArgumentException(e.toString());
1821             } else {
1822                 throw e;
1823             }
1824         }
1825     }
1826 
1827     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)1828     protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1829         mContext.enforceCallingOrSelfPermission(DUMP, TAG);
1830 
1831         pw.println("IpSecService dump:");
1832         pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
1833         pw.println();
1834 
1835         pw.println("mUserResourceTracker:");
1836         pw.println(mUserResourceTracker);
1837     }
1838 }
1839