Add Initial IPsec APIs to IpSecService
-Plumb IpSecManager APIs to NetD -Add Resource Management to IpSecService Bug: 30984788 Test: b/34812052, b/34811227 Change-Id: Ic43965c6158f28cac53810adbf5cf50d2c54f920 (cherry picked from commit 93962f34ce21f5aac825afbcebf2f3e8c7a30910)
This commit is contained in:
@@ -16,9 +16,31 @@
|
|||||||
|
|
||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
|
import android.net.Network;
|
||||||
|
import android.net.IpSecConfig;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
interface IIpSecService
|
interface IIpSecService
|
||||||
{
|
{
|
||||||
|
Bundle reserveSecurityParameterIndex(
|
||||||
|
int direction, in String remoteAddress, int requestedSpi, in IBinder binder);
|
||||||
|
|
||||||
|
void releaseSecurityParameterIndex(int resourceId);
|
||||||
|
|
||||||
|
Bundle openUdpEncapsulationSocket(int port, in IBinder binder);
|
||||||
|
|
||||||
|
void closeUdpEncapsulationSocket(in ParcelFileDescriptor socket);
|
||||||
|
|
||||||
|
Bundle createTransportModeTransform(in IpSecConfig c, in IBinder binder);
|
||||||
|
|
||||||
|
void deleteTransportModeTransform(int transformId);
|
||||||
|
|
||||||
|
void applyTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
|
||||||
|
|
||||||
|
void removeTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ public final class IpSecAlgorithm implements Parcelable {
|
|||||||
|
|
||||||
private static boolean isTruncationLengthValid(String algo, int truncLenBits) {
|
private static boolean isTruncationLengthValid(String algo, int truncLenBits) {
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
|
case ALGO_CRYPT_AES_CBC:
|
||||||
|
return (truncLenBits == 128 || truncLenBits == 192 || truncLenBits == 256);
|
||||||
case ALGO_AUTH_HMAC_MD5:
|
case ALGO_AUTH_HMAC_MD5:
|
||||||
return (truncLenBits >= 96 && truncLenBits <= 128);
|
return (truncLenBits >= 96 && truncLenBits <= 128);
|
||||||
case ALGO_AUTH_HMAC_SHA1:
|
case ALGO_AUTH_HMAC_SHA1:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import java.net.UnknownHostException;
|
|||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public final class IpSecConfig implements Parcelable {
|
public final class IpSecConfig implements Parcelable {
|
||||||
private static final String TAG = IpSecConfig.class.getSimpleName();
|
private static final String TAG = "IpSecConfig";
|
||||||
|
|
||||||
//MODE_TRANSPORT or MODE_TUNNEL
|
//MODE_TRANSPORT or MODE_TUNNEL
|
||||||
int mode;
|
int mode;
|
||||||
@@ -43,13 +43,13 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
int spi;
|
int spi;
|
||||||
|
|
||||||
// Encryption Algorithm
|
// Encryption Algorithm
|
||||||
IpSecAlgorithm encryptionAlgo;
|
IpSecAlgorithm encryption;
|
||||||
|
|
||||||
// Authentication Algorithm
|
// Authentication Algorithm
|
||||||
IpSecAlgorithm authenticationAlgo;
|
IpSecAlgorithm authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
Flow[] flow = new Flow[2];
|
Flow[] flow = new Flow[] {new Flow(), new Flow()};
|
||||||
|
|
||||||
// For tunnel mode IPv4 UDP Encapsulation
|
// For tunnel mode IPv4 UDP Encapsulation
|
||||||
// IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
|
// IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
|
||||||
@@ -57,17 +57,15 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
int encapLocalPort;
|
int encapLocalPort;
|
||||||
int encapRemotePort;
|
int encapRemotePort;
|
||||||
|
|
||||||
// An optional protocol to match with the selector
|
|
||||||
int selectorProto;
|
|
||||||
|
|
||||||
// A bitmask of FEATURE_* indicating which of the fields
|
|
||||||
// of this class are valid.
|
|
||||||
long features;
|
|
||||||
|
|
||||||
// An interval, in seconds between the NattKeepalive packets
|
// An interval, in seconds between the NattKeepalive packets
|
||||||
int nattKeepaliveInterval;
|
int nattKeepaliveInterval;
|
||||||
|
|
||||||
public InetAddress getLocalIp() {
|
// Transport or Tunnel
|
||||||
|
public int getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InetAddress getLocalAddress() {
|
||||||
return localAddress;
|
return localAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,19 +73,19 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
return flow[direction].spi;
|
return flow[direction].spi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InetAddress getRemoteIp() {
|
public InetAddress getRemoteAddress() {
|
||||||
return remoteAddress;
|
return remoteAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpSecAlgorithm getEncryptionAlgo(int direction) {
|
public IpSecAlgorithm getEncryption(int direction) {
|
||||||
return flow[direction].encryptionAlgo;
|
return flow[direction].encryption;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpSecAlgorithm getAuthenticationAlgo(int direction) {
|
public IpSecAlgorithm getAuthentication(int direction) {
|
||||||
return flow[direction].authenticationAlgo;
|
return flow[direction].authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
Network getNetwork() {
|
public Network getNetwork() {
|
||||||
return network;
|
return network;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,18 +101,10 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
return encapRemotePort;
|
return encapRemotePort;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSelectorProto() {
|
public int getNattKeepaliveInterval() {
|
||||||
return selectorProto;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getNattKeepaliveInterval() {
|
|
||||||
return nattKeepaliveInterval;
|
return nattKeepaliveInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasProperty(int featureBits) {
|
|
||||||
return (features & featureBits) == featureBits;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parcelable Methods
|
// Parcelable Methods
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -124,31 +114,25 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel out, int flags) {
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
out.writeLong(features);
|
|
||||||
// TODO: Use a byte array or other better method for storing IPs that can also include scope
|
// TODO: Use a byte array or other better method for storing IPs that can also include scope
|
||||||
out.writeString((localAddress != null) ? localAddress.getHostAddress() : null);
|
out.writeString((localAddress != null) ? localAddress.getHostAddress() : null);
|
||||||
// TODO: Use a byte array or other better method for storing IPs that can also include scope
|
// TODO: Use a byte array or other better method for storing IPs that can also include scope
|
||||||
out.writeString((remoteAddress != null) ? remoteAddress.getHostAddress() : null);
|
out.writeString((remoteAddress != null) ? remoteAddress.getHostAddress() : null);
|
||||||
out.writeParcelable(network, flags);
|
out.writeParcelable(network, flags);
|
||||||
out.writeInt(flow[IpSecTransform.DIRECTION_IN].spi);
|
out.writeInt(flow[IpSecTransform.DIRECTION_IN].spi);
|
||||||
out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryptionAlgo, flags);
|
out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryption, flags);
|
||||||
out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authenticationAlgo, flags);
|
out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authentication, flags);
|
||||||
out.writeInt(flow[IpSecTransform.DIRECTION_OUT].spi);
|
out.writeInt(flow[IpSecTransform.DIRECTION_OUT].spi);
|
||||||
out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo, flags);
|
out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryption, flags);
|
||||||
out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo, flags);
|
out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authentication, flags);
|
||||||
out.writeInt(encapType);
|
out.writeInt(encapType);
|
||||||
out.writeInt(encapLocalPort);
|
out.writeInt(encapLocalPort);
|
||||||
out.writeInt(encapRemotePort);
|
out.writeInt(encapRemotePort);
|
||||||
out.writeInt(selectorProto);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package Private: Used by the IpSecTransform.Builder;
|
// Package Private: Used by the IpSecTransform.Builder;
|
||||||
// there should be no public constructor for this object
|
// there should be no public constructor for this object
|
||||||
IpSecConfig() {
|
IpSecConfig() {}
|
||||||
flow[IpSecTransform.DIRECTION_IN].spi = 0;
|
|
||||||
flow[IpSecTransform.DIRECTION_OUT].spi = 0;
|
|
||||||
nattKeepaliveInterval = 0; //FIXME constant
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InetAddress readInetAddressFromParcel(Parcel in) {
|
private static InetAddress readInetAddressFromParcel(Parcel in) {
|
||||||
String addrString = in.readString();
|
String addrString = in.readString();
|
||||||
@@ -164,24 +148,22 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IpSecConfig(Parcel in) {
|
private IpSecConfig(Parcel in) {
|
||||||
features = in.readLong();
|
|
||||||
localAddress = readInetAddressFromParcel(in);
|
localAddress = readInetAddressFromParcel(in);
|
||||||
remoteAddress = readInetAddressFromParcel(in);
|
remoteAddress = readInetAddressFromParcel(in);
|
||||||
network = (Network) in.readParcelable(Network.class.getClassLoader());
|
network = (Network) in.readParcelable(Network.class.getClassLoader());
|
||||||
flow[IpSecTransform.DIRECTION_IN].spi = in.readInt();
|
flow[IpSecTransform.DIRECTION_IN].spi = in.readInt();
|
||||||
flow[IpSecTransform.DIRECTION_IN].encryptionAlgo =
|
flow[IpSecTransform.DIRECTION_IN].encryption =
|
||||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
||||||
flow[IpSecTransform.DIRECTION_IN].authenticationAlgo =
|
flow[IpSecTransform.DIRECTION_IN].authentication =
|
||||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
||||||
flow[IpSecTransform.DIRECTION_OUT].spi = in.readInt();
|
flow[IpSecTransform.DIRECTION_OUT].spi = in.readInt();
|
||||||
flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo =
|
flow[IpSecTransform.DIRECTION_OUT].encryption =
|
||||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
||||||
flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo =
|
flow[IpSecTransform.DIRECTION_OUT].authentication =
|
||||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
||||||
encapType = in.readInt();
|
encapType = in.readInt();
|
||||||
encapLocalPort = in.readInt();
|
encapLocalPort = in.readInt();
|
||||||
encapRemotePort = in.readInt();
|
encapRemotePort = in.readInt();
|
||||||
selectorProto = in.readInt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Parcelable.Creator<IpSecConfig> CREATOR =
|
public static final Parcelable.Creator<IpSecConfig> CREATOR =
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ package android.net;
|
|||||||
|
|
||||||
import static com.android.internal.util.Preconditions.checkNotNull;
|
import static com.android.internal.util.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import android.annotation.SystemApi;
|
import android.annotation.NonNull;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.util.AndroidException;
|
import android.util.AndroidException;
|
||||||
import dalvik.system.CloseGuard;
|
import dalvik.system.CloseGuard;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
@@ -38,6 +41,29 @@ import java.net.Socket;
|
|||||||
public final class IpSecManager {
|
public final class IpSecManager {
|
||||||
private static final String TAG = "IpSecManager";
|
private static final String TAG = "IpSecManager";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Security Parameter Index, SPI, 0 indicates an unknown or invalid index.
|
||||||
|
*
|
||||||
|
* <p>No IPsec packet may contain an SPI of 0.
|
||||||
|
*/
|
||||||
|
public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public interface Status {
|
||||||
|
public static final int OK = 0;
|
||||||
|
public static final int RESOURCE_UNAVAILABLE = 1;
|
||||||
|
public static final int SPI_UNAVAILABLE = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static final String KEY_STATUS = "status";
|
||||||
|
/** @hide */
|
||||||
|
public static final String KEY_RESOURCE_ID = "resourceId";
|
||||||
|
/** @hide */
|
||||||
|
public static final String KEY_SPI = "spi";
|
||||||
|
/** @hide */
|
||||||
|
public static final int INVALID_RESOURCE_ID = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that the combination of remote InetAddress and SPI was non-unique for a given
|
* Indicates that the combination of remote InetAddress and SPI was non-unique for a given
|
||||||
* request. If encountered, selection of a new SPI is required before a transform may be
|
* request. If encountered, selection of a new SPI is required before a transform may be
|
||||||
@@ -83,22 +109,14 @@ public final class IpSecManager {
|
|||||||
private final IIpSecService mService;
|
private final IIpSecService mService;
|
||||||
private final InetAddress mRemoteAddress;
|
private final InetAddress mRemoteAddress;
|
||||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||||
private int mSpi;
|
private int mSpi = INVALID_SECURITY_PARAMETER_INDEX;
|
||||||
|
private int mResourceId;
|
||||||
|
|
||||||
/** Return the underlying SPI held by this object */
|
/** Return the underlying SPI held by this object */
|
||||||
public int getSpi() {
|
public int getSpi() {
|
||||||
return mSpi;
|
return mSpi;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecurityParameterIndex(
|
|
||||||
IIpSecService service, int direction, InetAddress remoteAddress, int spi)
|
|
||||||
throws ResourceUnavailableException, SpiUnavailableException {
|
|
||||||
mService = service;
|
|
||||||
mRemoteAddress = remoteAddress;
|
|
||||||
mSpi = spi;
|
|
||||||
mCloseGuard.open("open");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release an SPI that was previously reserved.
|
* Release an SPI that was previously reserved.
|
||||||
*
|
*
|
||||||
@@ -108,7 +126,7 @@ public final class IpSecManager {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
mSpi = INVALID_SECURITY_PARAMETER_INDEX; // TODO: Invalid SPI
|
mSpi = INVALID_SECURITY_PARAMETER_INDEX;
|
||||||
mCloseGuard.close();
|
mCloseGuard.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,14 +138,52 @@ public final class IpSecManager {
|
|||||||
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SecurityParameterIndex(
|
||||||
|
@NonNull IIpSecService service, int direction, InetAddress remoteAddress, int spi)
|
||||||
|
throws ResourceUnavailableException, SpiUnavailableException {
|
||||||
|
mService = service;
|
||||||
|
mRemoteAddress = remoteAddress;
|
||||||
|
try {
|
||||||
|
Bundle result =
|
||||||
|
mService.reserveSecurityParameterIndex(
|
||||||
|
direction, remoteAddress.getHostAddress(), spi, new Binder());
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
throw new NullPointerException("Received null response from IpSecService");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
int status = result.getInt(KEY_STATUS);
|
||||||
* The Security Parameter Index, SPI, 0 indicates an unknown or invalid index.
|
switch (status) {
|
||||||
*
|
case Status.OK:
|
||||||
* <p>No IPsec packet may contain an SPI of 0.
|
break;
|
||||||
*/
|
case Status.RESOURCE_UNAVAILABLE:
|
||||||
public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
|
throw new ResourceUnavailableException(
|
||||||
|
"No more SPIs may be allocated by this requester.");
|
||||||
|
case Status.SPI_UNAVAILABLE:
|
||||||
|
throw new SpiUnavailableException("Requested SPI is unavailable", spi);
|
||||||
|
default:
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Unknown status returned by IpSecService: " + status);
|
||||||
|
}
|
||||||
|
mSpi = result.getInt(KEY_SPI);
|
||||||
|
mResourceId = result.getInt(KEY_RESOURCE_ID);
|
||||||
|
|
||||||
|
if (mSpi == INVALID_SECURITY_PARAMETER_INDEX) {
|
||||||
|
throw new RuntimeException("Invalid SPI returned by IpSecService: " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mResourceId == INVALID_RESOURCE_ID) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Invalid Resource ID returned by IpSecService: " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
mCloseGuard.open("open");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reserve an SPI for traffic bound towards the specified remote address.
|
* Reserve an SPI for traffic bound towards the specified remote address.
|
||||||
@@ -184,7 +240,13 @@ public final class IpSecManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Call down to activate a transform */
|
/* Call down to activate a transform */
|
||||||
private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
|
private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
|
||||||
|
try {
|
||||||
|
mService.applyTransportModeTransform(pfd, transform.getResourceId());
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to
|
* Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to
|
||||||
@@ -228,7 +290,13 @@ public final class IpSecManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Call down to activate a transform */
|
/* Call down to activate a transform */
|
||||||
private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
|
private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
|
||||||
|
try {
|
||||||
|
mService.removeTransportModeTransform(pfd, transform.getResourceId());
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of
|
* Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of
|
||||||
@@ -255,7 +323,7 @@ public final class IpSecManager {
|
|||||||
private final IIpSecService mService;
|
private final IIpSecService mService;
|
||||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||||
|
|
||||||
private UdpEncapsulationSocket(IIpSecService service, int port)
|
private UdpEncapsulationSocket(@NonNull IIpSecService service, int port)
|
||||||
throws ResourceUnavailableException {
|
throws ResourceUnavailableException {
|
||||||
mService = service;
|
mService = service;
|
||||||
mCloseGuard.open("constructor");
|
mCloseGuard.open("constructor");
|
||||||
|
|||||||
@@ -15,11 +15,21 @@
|
|||||||
*/
|
*/
|
||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
|
import static android.net.IpSecManager.INVALID_RESOURCE_ID;
|
||||||
|
import static android.net.IpSecManager.KEY_RESOURCE_ID;
|
||||||
|
import static android.net.IpSecManager.KEY_STATUS;
|
||||||
|
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
|
import android.annotation.NonNull;
|
||||||
import android.annotation.SystemApi;
|
import android.annotation.SystemApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.system.ErrnoException;
|
import android.os.Binder;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.ServiceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import com.android.internal.util.Preconditions;
|
||||||
import dalvik.system.CloseGuard;
|
import dalvik.system.CloseGuard;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@@ -86,39 +96,64 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface EncapType {}
|
public @interface EncapType {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sentinel for an invalid transform (means that this transform is inactive).
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public static final int INVALID_TRANSFORM_ID = -1;
|
|
||||||
|
|
||||||
private IpSecTransform(Context context, IpSecConfig config) {
|
private IpSecTransform(Context context, IpSecConfig config) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mConfig = config;
|
mConfig = config;
|
||||||
mTransformId = INVALID_TRANSFORM_ID;
|
mResourceId = INVALID_RESOURCE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IIpSecService getIpSecService() {
|
||||||
|
IBinder b = ServiceManager.getService(android.content.Context.IPSEC_SERVICE);
|
||||||
|
if (b == null) {
|
||||||
|
throw new RemoteException("Failed to connect to IpSecService")
|
||||||
|
.rethrowAsRuntimeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return IIpSecService.Stub.asInterface(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkResultStatusAndThrow(int status)
|
||||||
|
throws IOException, IpSecManager.ResourceUnavailableException,
|
||||||
|
IpSecManager.SpiUnavailableException {
|
||||||
|
switch (status) {
|
||||||
|
case IpSecManager.Status.OK:
|
||||||
|
return;
|
||||||
|
// TODO: Pass Error string back from bundle so that errors can be more specific
|
||||||
|
case IpSecManager.Status.RESOURCE_UNAVAILABLE:
|
||||||
|
throw new IpSecManager.ResourceUnavailableException(
|
||||||
|
"Failed to allocate a new IpSecTransform");
|
||||||
|
case IpSecManager.Status.SPI_UNAVAILABLE:
|
||||||
|
Log.wtf(TAG, "Attempting to use an SPI that was somehow not reserved");
|
||||||
|
// Fall through
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Failed to Create a Transform with status code " + status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IpSecTransform activate()
|
private IpSecTransform activate()
|
||||||
throws IOException, IpSecManager.ResourceUnavailableException,
|
throws IOException, IpSecManager.ResourceUnavailableException,
|
||||||
IpSecManager.SpiUnavailableException {
|
IpSecManager.SpiUnavailableException {
|
||||||
int transformId;
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
//try {
|
try {
|
||||||
transformId = INVALID_TRANSFORM_ID;
|
IIpSecService svc = getIpSecService();
|
||||||
//} catch (RemoteException e) {
|
Bundle result = svc.createTransportModeTransform(mConfig, new Binder());
|
||||||
// throw e.rethrowFromSystemServer();
|
int status = result.getInt(KEY_STATUS);
|
||||||
//}
|
checkResultStatusAndThrow(status);
|
||||||
|
mResourceId = result.getInt(KEY_RESOURCE_ID, INVALID_RESOURCE_ID);
|
||||||
|
|
||||||
if (transformId < 0) {
|
/* Keepalive will silently fail if not needed by the config; but, if needed and
|
||||||
throw new ErrnoException("addTransform", -transformId).rethrowAsIOException();
|
* it fails to start, we need to bail because a transform will not be reliable
|
||||||
}
|
* to use if keepalive is expected to offload and fails.
|
||||||
|
*/
|
||||||
startKeepalive(mContext); // Will silently fail if not required
|
// FIXME: if keepalive fails, we need to fail spectacularly
|
||||||
mTransformId = transformId;
|
startKeepalive(mContext);
|
||||||
Log.d(TAG, "Added Transform with Id " + transformId);
|
Log.d(TAG, "Added Transform with Id " + mResourceId);
|
||||||
}
|
|
||||||
mCloseGuard.open("build");
|
mCloseGuard.open("build");
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowAsRuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -133,22 +168,28 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
* transform is no longer needed.
|
* transform is no longer needed.
|
||||||
*/
|
*/
|
||||||
public void close() {
|
public void close() {
|
||||||
Log.d(TAG, "Removing Transform with Id " + mTransformId);
|
Log.d(TAG, "Removing Transform with Id " + mResourceId);
|
||||||
|
|
||||||
// Always safe to attempt cleanup
|
// Always safe to attempt cleanup
|
||||||
if (mTransformId == INVALID_TRANSFORM_ID) {
|
if (mResourceId == INVALID_RESOURCE_ID) {
|
||||||
|
mCloseGuard.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//try {
|
try {
|
||||||
|
/* Order matters here because the keepalive is best-effort but could fail in some
|
||||||
|
* horrible way to be removed if the wifi (or cell) subsystem has crashed, and we
|
||||||
|
* still want to clear out the transform.
|
||||||
|
*/
|
||||||
|
IIpSecService svc = getIpSecService();
|
||||||
|
svc.deleteTransportModeTransform(mResourceId);
|
||||||
stopKeepalive();
|
stopKeepalive();
|
||||||
//} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
// transform.setTransformId(transformId);
|
throw e.rethrowAsRuntimeException();
|
||||||
// throw e.rethrowFromSystemServer();
|
} finally {
|
||||||
//} finally {
|
mResourceId = INVALID_RESOURCE_ID;
|
||||||
mTransformId = INVALID_TRANSFORM_ID;
|
|
||||||
//}
|
|
||||||
mCloseGuard.close();
|
mCloseGuard.close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void finalize() throws Throwable {
|
protected void finalize() throws Throwable {
|
||||||
@@ -164,7 +205,7 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final IpSecConfig mConfig;
|
private final IpSecConfig mConfig;
|
||||||
private int mTransformId;
|
private int mResourceId;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||||
private ConnectivityManager.PacketKeepalive mKeepalive;
|
private ConnectivityManager.PacketKeepalive mKeepalive;
|
||||||
@@ -200,6 +241,7 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
|
|
||||||
/* Package */
|
/* Package */
|
||||||
void startKeepalive(Context c) {
|
void startKeepalive(Context c) {
|
||||||
|
// FIXME: NO_KEEPALIVE needs to be a constant
|
||||||
if (mConfig.getNattKeepaliveInterval() == 0) {
|
if (mConfig.getNattKeepaliveInterval() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -208,7 +250,7 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
(ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
|
(ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
|
||||||
if (mKeepalive != null) {
|
if (mKeepalive != null) {
|
||||||
Log.e(TAG, "Keepalive already started for this IpSecTransform.");
|
Log.wtf(TAG, "Keepalive already started for this IpSecTransform.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,10 +260,11 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
mConfig.getNetwork(),
|
mConfig.getNetwork(),
|
||||||
mConfig.getNattKeepaliveInterval(),
|
mConfig.getNattKeepaliveInterval(),
|
||||||
mKeepaliveCallback,
|
mKeepaliveCallback,
|
||||||
mConfig.getLocalIp(),
|
mConfig.getLocalAddress(),
|
||||||
mConfig.getEncapLocalPort(),
|
mConfig.getEncapLocalPort(),
|
||||||
mConfig.getRemoteIp());
|
mConfig.getRemoteAddress());
|
||||||
try {
|
try {
|
||||||
|
// FIXME: this is still a horrible way to fudge the synchronous callback
|
||||||
mKeepaliveSyncLock.wait(2000);
|
mKeepaliveSyncLock.wait(2000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
}
|
}
|
||||||
@@ -231,6 +274,11 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Package */
|
||||||
|
int getResourceId() {
|
||||||
|
return mResourceId;
|
||||||
|
}
|
||||||
|
|
||||||
/* Package */
|
/* Package */
|
||||||
void stopKeepalive() {
|
void stopKeepalive() {
|
||||||
if (mKeepalive == null) {
|
if (mKeepalive == null) {
|
||||||
@@ -247,16 +295,6 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Package */
|
|
||||||
void setTransformId(int transformId) {
|
|
||||||
mTransformId = transformId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Package */
|
|
||||||
int getTransformId() {
|
|
||||||
return mTransformId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder object to facilitate the creation of IpSecTransform objects.
|
* Builder object to facilitate the creation of IpSecTransform objects.
|
||||||
*
|
*
|
||||||
@@ -280,7 +318,7 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
*/
|
*/
|
||||||
public IpSecTransform.Builder setEncryption(
|
public IpSecTransform.Builder setEncryption(
|
||||||
@TransformDirection int direction, IpSecAlgorithm algo) {
|
@TransformDirection int direction, IpSecAlgorithm algo) {
|
||||||
mConfig.flow[direction].encryptionAlgo = algo;
|
mConfig.flow[direction].encryption = algo;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +333,7 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
*/
|
*/
|
||||||
public IpSecTransform.Builder setAuthentication(
|
public IpSecTransform.Builder setAuthentication(
|
||||||
@TransformDirection int direction, IpSecAlgorithm algo) {
|
@TransformDirection int direction, IpSecAlgorithm algo) {
|
||||||
mConfig.flow[direction].authenticationAlgo = algo;
|
mConfig.flow[direction].authentication = algo;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,6 +356,8 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
*/
|
*/
|
||||||
public IpSecTransform.Builder setSpi(
|
public IpSecTransform.Builder setSpi(
|
||||||
@TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
|
@TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
|
||||||
|
// TODO: convert to using the resource Id of the SPI. Then build() can validate
|
||||||
|
// the owner in the IpSecService
|
||||||
mConfig.flow[direction].spi = spi.getSpi();
|
mConfig.flow[direction].spi = spi.getSpi();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -439,7 +479,8 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
*
|
*
|
||||||
* @param context current Context
|
* @param context current Context
|
||||||
*/
|
*/
|
||||||
public Builder(Context context) {
|
public Builder(@NonNull Context context) {
|
||||||
|
Preconditions.checkNotNull(context);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mConfig = new IpSecConfig();
|
mConfig = new IpSecConfig();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,23 +17,40 @@
|
|||||||
package com.android.server;
|
package com.android.server;
|
||||||
|
|
||||||
import static android.Manifest.permission.DUMP;
|
import static android.Manifest.permission.DUMP;
|
||||||
|
import static android.net.IpSecManager.INVALID_RESOURCE_ID;
|
||||||
|
import static android.net.IpSecManager.KEY_RESOURCE_ID;
|
||||||
|
import static android.net.IpSecManager.KEY_SPI;
|
||||||
|
import static android.net.IpSecManager.KEY_STATUS;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.IIpSecService;
|
import android.net.IIpSecService;
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
|
import android.net.IpSecAlgorithm;
|
||||||
|
import android.net.IpSecConfig;
|
||||||
|
import android.net.IpSecManager;
|
||||||
|
import android.net.IpSecTransform;
|
||||||
import android.net.util.NetdService;
|
import android.net.util.NetdService;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.ServiceSpecificException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public class IpSecService extends IIpSecService.Stub {
|
public class IpSecService extends IIpSecService.Stub {
|
||||||
private static final String TAG = "IpSecService";
|
private static final String TAG = "IpSecService";
|
||||||
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
|
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
|
||||||
private static final String NETD_SERVICE_NAME = "netd";
|
private static final String NETD_SERVICE_NAME = "netd";
|
||||||
|
private static final int[] DIRECTIONS =
|
||||||
|
new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
|
||||||
|
|
||||||
/** Binder context for this service */
|
/** Binder context for this service */
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
@@ -42,6 +59,159 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
|
|
||||||
private static final int NETD_FETCH_TIMEOUT = 5000; //ms
|
private static final int NETD_FETCH_TIMEOUT = 5000; //ms
|
||||||
|
|
||||||
|
private AtomicInteger mNextResourceId = new AtomicInteger(0x00FADED0);
|
||||||
|
|
||||||
|
private abstract class ManagedResource implements IBinder.DeathRecipient {
|
||||||
|
final int pid;
|
||||||
|
final int uid;
|
||||||
|
private IBinder mBinder;
|
||||||
|
|
||||||
|
ManagedResource(IBinder binder) {
|
||||||
|
super();
|
||||||
|
mBinder = binder;
|
||||||
|
pid = Binder.getCallingPid();
|
||||||
|
uid = Binder.getCallingUid();
|
||||||
|
|
||||||
|
try {
|
||||||
|
mBinder.linkToDeath(this, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
binderDied();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When this record is no longer needed for managing system resources this function should
|
||||||
|
* unlink all references held by the record to allow efficient garbage collection.
|
||||||
|
*/
|
||||||
|
public final void release() {
|
||||||
|
//Release all the underlying system resources first
|
||||||
|
releaseResources();
|
||||||
|
|
||||||
|
if (mBinder != null) {
|
||||||
|
mBinder.unlinkToDeath(this, 0);
|
||||||
|
}
|
||||||
|
mBinder = null;
|
||||||
|
|
||||||
|
//remove this record so that it can be cleaned up
|
||||||
|
nullifyRecord();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the Binder object dies, this function is called to free the system resources that are
|
||||||
|
* being managed by this record and to subsequently release this record for garbage
|
||||||
|
* collection
|
||||||
|
*/
|
||||||
|
public final void binderDied() {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this method to release all object references contained in the subclass to allow
|
||||||
|
* efficient garbage collection of the record. This should remove any references to the
|
||||||
|
* record from all other locations that hold a reference as the record is no longer valid.
|
||||||
|
*/
|
||||||
|
protected abstract void nullifyRecord();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this method to release all system resources that are being protected by this
|
||||||
|
* record. Once the resources are released, the record should be invalidated and no longer
|
||||||
|
* used by calling releaseRecord()
|
||||||
|
*/
|
||||||
|
protected abstract void releaseResources();
|
||||||
|
};
|
||||||
|
|
||||||
|
private final class TransformRecord extends ManagedResource {
|
||||||
|
private IpSecConfig mConfig;
|
||||||
|
private int mResourceId;
|
||||||
|
|
||||||
|
TransformRecord(IpSecConfig config, int resourceId, IBinder binder) {
|
||||||
|
super(binder);
|
||||||
|
mConfig = config;
|
||||||
|
mResourceId = resourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IpSecConfig getConfig() {
|
||||||
|
return mConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void releaseResources() {
|
||||||
|
for (int direction : DIRECTIONS) {
|
||||||
|
try {
|
||||||
|
getNetdInstance()
|
||||||
|
.ipSecDeleteSecurityAssociation(
|
||||||
|
mResourceId,
|
||||||
|
direction,
|
||||||
|
(mConfig.getLocalAddress() != null)
|
||||||
|
? mConfig.getLocalAddress().getHostAddress()
|
||||||
|
: "",
|
||||||
|
(mConfig.getRemoteAddress() != null)
|
||||||
|
? mConfig.getRemoteAddress().getHostAddress()
|
||||||
|
: "",
|
||||||
|
mConfig.getSpi(direction));
|
||||||
|
} catch (ServiceSpecificException e) {
|
||||||
|
// FIXME: get the error code and throw is at an IOException from Errno Exception
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void nullifyRecord() {
|
||||||
|
mConfig = null;
|
||||||
|
mResourceId = INVALID_RESOURCE_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class SpiRecord extends ManagedResource {
|
||||||
|
private final int mDirection;
|
||||||
|
private final String mLocalAddress;
|
||||||
|
private final String mRemoteAddress;
|
||||||
|
private final IBinder mBinder;
|
||||||
|
private int mSpi;
|
||||||
|
private int mResourceId;
|
||||||
|
|
||||||
|
SpiRecord(
|
||||||
|
int resourceId,
|
||||||
|
int direction,
|
||||||
|
String localAddress,
|
||||||
|
String remoteAddress,
|
||||||
|
int spi,
|
||||||
|
IBinder binder) {
|
||||||
|
super(binder);
|
||||||
|
mResourceId = resourceId;
|
||||||
|
mDirection = direction;
|
||||||
|
mLocalAddress = localAddress;
|
||||||
|
mRemoteAddress = remoteAddress;
|
||||||
|
mSpi = spi;
|
||||||
|
mBinder = binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void releaseResources() {
|
||||||
|
try {
|
||||||
|
getNetdInstance()
|
||||||
|
.ipSecDeleteSecurityAssociation(
|
||||||
|
mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi);
|
||||||
|
} catch (ServiceSpecificException e) {
|
||||||
|
// FIXME: get the error code and throw is at an IOException from Errno Exception
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void nullifyRecord() {
|
||||||
|
mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
|
||||||
|
mResourceId = INVALID_RESOURCE_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mSpiRecords")
|
||||||
|
private final SparseArray<SpiRecord> mSpiRecords = new SparseArray<>();
|
||||||
|
|
||||||
|
@GuardedBy("mTransformRecords")
|
||||||
|
private final SparseArray<TransformRecord> mTransformRecords = new SparseArray<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new IpSecService instance
|
* Constructs a new IpSecService instance
|
||||||
*
|
*
|
||||||
@@ -80,22 +250,21 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
t.run();
|
t.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
INetd getNetdInstance() {
|
INetd getNetdInstance() throws RemoteException {
|
||||||
final INetd netd = NetdService.getInstance();
|
final INetd netd = NetdService.getInstance();
|
||||||
if (netd == null) {
|
if (netd == null) {
|
||||||
throw new RemoteException("Failed to Get Netd Instance").rethrowFromSystemServer();
|
throw new RemoteException("Failed to Get Netd Instance");
|
||||||
}
|
}
|
||||||
return netd;
|
return netd;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isNetdAlive() {
|
boolean isNetdAlive() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
|
try {
|
||||||
final INetd netd = getNetdInstance();
|
final INetd netd = getNetdInstance();
|
||||||
if (netd == null) {
|
if (netd == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
return netd.isAlive();
|
return netd.isAlive();
|
||||||
} catch (RemoteException re) {
|
} catch (RemoteException re) {
|
||||||
return false;
|
return false;
|
||||||
@@ -103,6 +272,217 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/** Get a new SPI and maintain the reservation in the system server */
|
||||||
|
public Bundle reserveSecurityParameterIndex(
|
||||||
|
int direction, String remoteAddress, int requestedSpi, IBinder binder)
|
||||||
|
throws RemoteException {
|
||||||
|
int resourceId = mNextResourceId.getAndIncrement();
|
||||||
|
|
||||||
|
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
|
||||||
|
String localAddress = "";
|
||||||
|
Bundle retBundle = new Bundle(3);
|
||||||
|
try {
|
||||||
|
spi =
|
||||||
|
getNetdInstance()
|
||||||
|
.ipSecAllocateSpi(
|
||||||
|
resourceId,
|
||||||
|
direction,
|
||||||
|
localAddress,
|
||||||
|
remoteAddress,
|
||||||
|
requestedSpi);
|
||||||
|
Log.d(TAG, "Allocated SPI " + spi);
|
||||||
|
retBundle.putInt(KEY_STATUS, IpSecManager.Status.OK);
|
||||||
|
retBundle.putInt(KEY_RESOURCE_ID, resourceId);
|
||||||
|
retBundle.putInt(KEY_SPI, spi);
|
||||||
|
synchronized (mSpiRecords) {
|
||||||
|
mSpiRecords.put(
|
||||||
|
resourceId,
|
||||||
|
new SpiRecord(
|
||||||
|
resourceId, direction, localAddress, remoteAddress, spi, binder));
|
||||||
|
}
|
||||||
|
} catch (ServiceSpecificException e) {
|
||||||
|
// TODO: Add appropriate checks when other ServiceSpecificException types are supported
|
||||||
|
retBundle.putInt(KEY_STATUS, IpSecManager.Status.SPI_UNAVAILABLE);
|
||||||
|
retBundle.putInt(KEY_RESOURCE_ID, resourceId);
|
||||||
|
retBundle.putInt(KEY_SPI, spi);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
return retBundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Release a previously allocated SPI that has been registered with the system server */
|
||||||
|
@Override
|
||||||
|
public void releaseSecurityParameterIndex(int resourceId) throws RemoteException {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a socket via the system server and bind it to the specified port (random if port=0).
|
||||||
|
* This will return a PFD to the user that represent a bound UDP socket. The system server will
|
||||||
|
* cache the socket and a record of its owner so that it can and must be freed when no longer
|
||||||
|
* needed.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Bundle openUdpEncapsulationSocket(int port, IBinder binder) throws RemoteException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** close a socket that has been been allocated by and registered with the system server */
|
||||||
|
@Override
|
||||||
|
public void closeUdpEncapsulationSocket(ParcelFileDescriptor socket) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a transport mode transform, which represent two security associations (one in each
|
||||||
|
* direction) in the kernel. The transform will be cached by the system server and must be freed
|
||||||
|
* when no longer needed. It is possible to free one, deleting the SA from underneath sockets
|
||||||
|
* that are using it, which will result in all of those sockets becoming unable to send or
|
||||||
|
* receive data.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Bundle createTransportModeTransform(IpSecConfig c, IBinder binder)
|
||||||
|
throws RemoteException {
|
||||||
|
// TODO: Basic input validation here since it's coming over the Binder
|
||||||
|
int resourceId = mNextResourceId.getAndIncrement();
|
||||||
|
for (int direction : DIRECTIONS) {
|
||||||
|
IpSecAlgorithm auth = c.getAuthentication(direction);
|
||||||
|
IpSecAlgorithm crypt = c.getEncryption(direction);
|
||||||
|
try {
|
||||||
|
int result =
|
||||||
|
getNetdInstance()
|
||||||
|
.ipSecAddSecurityAssociation(
|
||||||
|
resourceId,
|
||||||
|
c.getMode(),
|
||||||
|
direction,
|
||||||
|
(c.getLocalAddress() != null)
|
||||||
|
? c.getLocalAddress().getHostAddress()
|
||||||
|
: "",
|
||||||
|
(c.getRemoteAddress() != null)
|
||||||
|
? c.getRemoteAddress().getHostAddress()
|
||||||
|
: "",
|
||||||
|
(c.getNetwork() != null)
|
||||||
|
? c.getNetwork().getNetworkHandle()
|
||||||
|
: 0,
|
||||||
|
c.getSpi(direction),
|
||||||
|
(auth != null) ? auth.getName() : "",
|
||||||
|
(auth != null) ? auth.getKey() : null,
|
||||||
|
(auth != null) ? auth.getTruncationLengthBits() : 0,
|
||||||
|
(crypt != null) ? crypt.getName() : "",
|
||||||
|
(crypt != null) ? crypt.getKey() : null,
|
||||||
|
(crypt != null) ? crypt.getTruncationLengthBits() : 0,
|
||||||
|
c.getEncapType(),
|
||||||
|
c.getEncapLocalPort(),
|
||||||
|
c.getEncapRemotePort());
|
||||||
|
if (result != c.getSpi(direction)) {
|
||||||
|
// TODO: cleanup the first SA if creation of second SA fails
|
||||||
|
Bundle retBundle = new Bundle(2);
|
||||||
|
retBundle.putInt(KEY_STATUS, IpSecManager.Status.SPI_UNAVAILABLE);
|
||||||
|
retBundle.putInt(KEY_RESOURCE_ID, INVALID_RESOURCE_ID);
|
||||||
|
return retBundle;
|
||||||
|
}
|
||||||
|
} catch (ServiceSpecificException e) {
|
||||||
|
// FIXME: get the error code and throw is at an IOException from Errno Exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronized (mTransformRecords) {
|
||||||
|
mTransformRecords.put(resourceId, new TransformRecord(c, resourceId, binder));
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle retBundle = new Bundle(2);
|
||||||
|
retBundle.putInt(KEY_STATUS, IpSecManager.Status.OK);
|
||||||
|
retBundle.putInt(KEY_RESOURCE_ID, resourceId);
|
||||||
|
return retBundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a transport mode transform that was previously allocated by + registered with the
|
||||||
|
* system server. If this is called on an inactive (or non-existent) transform, it will not
|
||||||
|
* return an error. It's safe to de-allocate transforms that may have already been deleted for
|
||||||
|
* other reasons.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void deleteTransportModeTransform(int resourceId) throws RemoteException {
|
||||||
|
synchronized (mTransformRecords) {
|
||||||
|
TransformRecord record;
|
||||||
|
// We want to non-destructively get so that we can check credentials before removing
|
||||||
|
// this from the records.
|
||||||
|
record = mTransformRecords.get(resourceId);
|
||||||
|
|
||||||
|
if (record == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Transform " + resourceId + " is not available to be deleted");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.pid != Binder.getCallingPid() || record.uid != Binder.getCallingUid()) {
|
||||||
|
throw new SecurityException("Only the owner of an IpSec Transform may delete it!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: if releaseResources() throws RemoteException, we can try again to clean up on
|
||||||
|
// binder death. Need to make sure that path is actually functional.
|
||||||
|
record.releaseResources();
|
||||||
|
mTransformRecords.remove(resourceId);
|
||||||
|
record.nullifyRecord();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply an active transport mode transform to a socket, which will apply the IPsec security
|
||||||
|
* association as a correspondent policy to the provided socket
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void applyTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
|
||||||
|
throws RemoteException {
|
||||||
|
|
||||||
|
synchronized (mTransformRecords) {
|
||||||
|
TransformRecord info;
|
||||||
|
// FIXME: this code should be factored out into a security check + getter
|
||||||
|
info = mTransformRecords.get(resourceId);
|
||||||
|
|
||||||
|
if (info == null) {
|
||||||
|
throw new IllegalArgumentException("Transform " + resourceId + " is not active");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make this a function.
|
||||||
|
if (info.pid != getCallingPid() || info.uid != getCallingUid()) {
|
||||||
|
throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
|
||||||
|
}
|
||||||
|
|
||||||
|
IpSecConfig c = info.getConfig();
|
||||||
|
try {
|
||||||
|
for (int direction : DIRECTIONS) {
|
||||||
|
getNetdInstance()
|
||||||
|
.ipSecApplyTransportModeTransform(
|
||||||
|
socket.getFileDescriptor(),
|
||||||
|
resourceId,
|
||||||
|
direction,
|
||||||
|
(c.getLocalAddress() != null)
|
||||||
|
? c.getLocalAddress().getHostAddress()
|
||||||
|
: "",
|
||||||
|
(c.getRemoteAddress() != null)
|
||||||
|
? c.getRemoteAddress().getHostAddress()
|
||||||
|
: "",
|
||||||
|
c.getSpi(direction));
|
||||||
|
}
|
||||||
|
} catch (ServiceSpecificException e) {
|
||||||
|
// FIXME: get the error code and throw is at an IOException from Errno Exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Remove a transport mode transform from a socket, applying the default (empty) policy. This
|
||||||
|
* will ensure that NO IPsec policy is applied to the socket (would be the equivalent of
|
||||||
|
* applying a policy that performs no IPsec). Today the resourceId parameter is passed but not
|
||||||
|
* used: reserved for future improved input validation.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
|
||||||
|
throws RemoteException {
|
||||||
|
try {
|
||||||
|
getNetdInstance().ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
|
||||||
|
} catch (ServiceSpecificException e) {
|
||||||
|
// FIXME: get the error code and throw is at an IOException from Errno Exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||||
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
|
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
|
||||||
|
|||||||
Reference in New Issue
Block a user