Add UserQuotaTracker to IpSecService
Add a small tracking object to enforce maximum quotas for SPIs, Transforms, and Encap sockets. The current quota limits are intentionally conservative with the expectation that we can relax them more easily than we can shrink them. Bug: 37688603 Test: tbd Change-Id: Iee59ac59ef9f4a7ab75a2e04f9ca72da82fc3229
This commit is contained in:
@@ -115,6 +115,67 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
|
|
||||||
private final IpSecServiceConfiguration mSrvConfig;
|
private final IpSecServiceConfiguration mSrvConfig;
|
||||||
|
|
||||||
|
/* Very simple counting class that looks much like a counting semaphore */
|
||||||
|
public static class ResourceTracker {
|
||||||
|
private final int mMax;
|
||||||
|
int mCurrent;
|
||||||
|
|
||||||
|
ResourceTracker(int max) {
|
||||||
|
mMax = max;
|
||||||
|
mCurrent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized boolean isAvailable() {
|
||||||
|
return (mCurrent < mMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void take() {
|
||||||
|
if (!isAvailable()) {
|
||||||
|
Log.wtf(TAG, "Too many resources allocated!");
|
||||||
|
}
|
||||||
|
mCurrent++;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void give() {
|
||||||
|
if (mCurrent <= 0) {
|
||||||
|
Log.wtf(TAG, "We've released this resource too many times");
|
||||||
|
}
|
||||||
|
mCurrent--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class UserQuotaTracker {
|
||||||
|
/* Maximum number of UDP Encap Sockets that a single UID may possess */
|
||||||
|
public static final int MAX_NUM_ENCAP_SOCKETS = 2;
|
||||||
|
|
||||||
|
/* Maximum number of IPsec Transforms that a single UID may possess */
|
||||||
|
public static final int MAX_NUM_TRANSFORMS = 4;
|
||||||
|
|
||||||
|
/* Maximum number of IPsec Transforms that a single UID may possess */
|
||||||
|
public static final int MAX_NUM_SPIS = 8;
|
||||||
|
|
||||||
|
/* Record for one users's IpSecService-managed objects */
|
||||||
|
public static class UserRecord {
|
||||||
|
public final ResourceTracker socket = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
|
||||||
|
public final ResourceTracker transform = new ResourceTracker(MAX_NUM_TRANSFORMS);
|
||||||
|
public final ResourceTracker spi = new ResourceTracker(MAX_NUM_SPIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
|
||||||
|
|
||||||
|
/* a never-fail getter so that we can populate the list of UIDs as-needed */
|
||||||
|
public synchronized UserRecord getUserRecord(int uid) {
|
||||||
|
UserRecord r = mUserRecords.get(uid);
|
||||||
|
if (r == null) {
|
||||||
|
r = new UserRecord();
|
||||||
|
mUserRecords.put(uid, r);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final UserQuotaTracker mUserQuotaTracker = new UserQuotaTracker();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ManagedResource class provides a facility to cleanly and reliably release system
|
* The ManagedResource class provides a facility to cleanly and reliably release system
|
||||||
* resources. It relies on two things: an IBinder that allows ManagedResource to automatically
|
* resources. It relies on two things: an IBinder that allows ManagedResource to automatically
|
||||||
@@ -132,11 +193,15 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
|
|
||||||
ManagedResource(int resourceId, IBinder binder) {
|
ManagedResource(int resourceId, IBinder binder) {
|
||||||
super();
|
super();
|
||||||
|
if (resourceId == INVALID_RESOURCE_ID) {
|
||||||
|
throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
|
||||||
|
}
|
||||||
mBinder = binder;
|
mBinder = binder;
|
||||||
mResourceId = resourceId;
|
mResourceId = resourceId;
|
||||||
pid = Binder.getCallingPid();
|
pid = Binder.getCallingPid();
|
||||||
uid = Binder.getCallingUid();
|
uid = Binder.getCallingUid();
|
||||||
|
|
||||||
|
getResourceTracker().take();
|
||||||
try {
|
try {
|
||||||
mBinder.linkToDeath(this, 0);
|
mBinder.linkToDeath(this, 0);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -184,6 +249,7 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
releaseResources();
|
releaseResources();
|
||||||
|
getResourceTracker().give();
|
||||||
if (mBinder != null) {
|
if (mBinder != null) {
|
||||||
mBinder.unlinkToDeath(this, 0);
|
mBinder.unlinkToDeath(this, 0);
|
||||||
}
|
}
|
||||||
@@ -215,6 +281,9 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
*/
|
*/
|
||||||
protected abstract void releaseResources() throws RemoteException;
|
protected abstract void releaseResources() throws RemoteException;
|
||||||
|
|
||||||
|
/** Get the resource tracker for this resource */
|
||||||
|
protected abstract ResourceTracker getResourceTracker();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder()
|
return new StringBuilder()
|
||||||
@@ -330,6 +399,10 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ResourceTracker getResourceTracker() {
|
||||||
|
return mUserQuotaTracker.getUserRecord(this.uid).transform;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder strBuilder = new StringBuilder();
|
StringBuilder strBuilder = new StringBuilder();
|
||||||
@@ -398,6 +471,11 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
|
mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResourceTracker getResourceTracker() {
|
||||||
|
return mUserQuotaTracker.getUserRecord(this.uid).spi;
|
||||||
|
}
|
||||||
|
|
||||||
public int getSpi() {
|
public int getSpi() {
|
||||||
return mSpi;
|
return mSpi;
|
||||||
}
|
}
|
||||||
@@ -450,6 +528,11 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
mSocket = null;
|
mSocket = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResourceTracker getResourceTracker() {
|
||||||
|
return mUserQuotaTracker.getUserRecord(this.uid).socket;
|
||||||
|
}
|
||||||
|
|
||||||
public int getPort() {
|
public int getPort() {
|
||||||
return mPort;
|
return mPort;
|
||||||
}
|
}
|
||||||
@@ -535,7 +618,14 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
|
|
||||||
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
|
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
|
||||||
String localAddress = "";
|
String localAddress = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).spi.isAvailable()) {
|
||||||
|
return new IpSecSpiResponse(
|
||||||
|
IpSecManager.Status.RESOURCE_UNAVAILABLE,
|
||||||
|
INVALID_RESOURCE_ID,
|
||||||
|
spi);
|
||||||
|
}
|
||||||
spi =
|
spi =
|
||||||
mSrvConfig
|
mSrvConfig
|
||||||
.getNetdInstance()
|
.getNetdInstance()
|
||||||
@@ -552,7 +642,7 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
} catch (ServiceSpecificException e) {
|
} catch (ServiceSpecificException e) {
|
||||||
// TODO: Add appropriate checks when other ServiceSpecificException types are supported
|
// TODO: Add appropriate checks when other ServiceSpecificException types are supported
|
||||||
return new IpSecSpiResponse(
|
return new IpSecSpiResponse(
|
||||||
IpSecManager.Status.SPI_UNAVAILABLE, IpSecManager.INVALID_RESOURCE_ID, spi);
|
IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
throw e.rethrowFromSystemServer();
|
throw e.rethrowFromSystemServer();
|
||||||
}
|
}
|
||||||
@@ -635,6 +725,10 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
int resourceId = mNextResourceId.getAndIncrement();
|
int resourceId = mNextResourceId.getAndIncrement();
|
||||||
FileDescriptor sockFd = null;
|
FileDescriptor sockFd = null;
|
||||||
try {
|
try {
|
||||||
|
if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).socket.isAvailable()) {
|
||||||
|
return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
|
||||||
if (port != 0) {
|
if (port != 0) {
|
||||||
@@ -679,6 +773,9 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
public synchronized IpSecTransformResponse createTransportModeTransform(
|
public synchronized IpSecTransformResponse createTransportModeTransform(
|
||||||
IpSecConfig c, IBinder binder) throws RemoteException {
|
IpSecConfig c, IBinder binder) throws RemoteException {
|
||||||
int resourceId = mNextResourceId.getAndIncrement();
|
int resourceId = mNextResourceId.getAndIncrement();
|
||||||
|
if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).transform.isAvailable()) {
|
||||||
|
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
|
||||||
|
}
|
||||||
SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
|
SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
|
||||||
// TODO: Basic input validation here since it's coming over the Binder
|
// TODO: Basic input validation here since it's coming over the Binder
|
||||||
int encapType, encapLocalPort = 0, encapRemotePort = 0;
|
int encapType, encapLocalPort = 0, encapRemotePort = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user