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