Merge changes from topic "ipsec-svc-cleanup" am: 9778c7884f

am: ae61e0c6e8

Change-Id: If8803e2fc98adb2f08a5bf2944fc60d0f0e71acd
This commit is contained in:
nharold
2017-10-04 20:35:20 +00:00
committed by android-build-merger
8 changed files with 733 additions and 411 deletions

View File

@@ -24,6 +24,7 @@ import com.android.internal.util.HexDump;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
/** /**
* IpSecAlgorithm specifies a single algorithm that can be applied to an IpSec Transform. Refer to * IpSecAlgorithm specifies a single algorithm that can be applied to an IpSec Transform. Refer to
@@ -75,13 +76,7 @@ public final class IpSecAlgorithm implements Parcelable {
public static final String AUTH_HMAC_SHA512 = "hmac(sha512)"; public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
/** @hide */ /** @hide */
@StringDef({ @StringDef({CRYPT_AES_CBC, AUTH_HMAC_MD5, AUTH_HMAC_SHA1, AUTH_HMAC_SHA256, AUTH_HMAC_SHA512})
CRYPT_AES_CBC,
AUTH_HMAC_MD5,
AUTH_HMAC_SHA1,
AUTH_HMAC_SHA256,
AUTH_HMAC_SHA512
})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface AlgorithmName {} public @interface AlgorithmName {}
@@ -197,4 +192,12 @@ public final class IpSecAlgorithm implements Parcelable {
.append("}") .append("}")
.toString(); .toString();
} }
/** package */
static boolean equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs) {
if (lhs == null || rhs == null) return (lhs == rhs);
return (lhs.mName.equals(rhs.mName)
&& Arrays.equals(lhs.mKey, rhs.mKey)
&& lhs.mTruncLenBits == rhs.mTruncLenBits);
}
}; };

View File

@@ -17,105 +17,170 @@ package android.net;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.util.Log;
import java.net.InetAddress; import com.android.internal.annotations.VisibleForTesting;
import java.net.UnknownHostException;
/** @hide */ /** @hide */
public final class IpSecConfig implements Parcelable { public final class IpSecConfig implements Parcelable {
private static final String TAG = "IpSecConfig"; private static final String TAG = "IpSecConfig";
//MODE_TRANSPORT or MODE_TUNNEL // MODE_TRANSPORT or MODE_TUNNEL
int mode; private int mMode = IpSecTransform.MODE_TRANSPORT;
// For tunnel mode // Needs to be valid only for tunnel mode
InetAddress localAddress; // Preventing this from being null simplifies Java->Native binder
private String mLocalAddress = "";
InetAddress remoteAddress; // Preventing this from being null simplifies Java->Native binder
private String mRemoteAddress = "";
// Limit selection by network interface // The underlying network interface that represents the "gateway" Network
Network network; // for outbound packets. It may also be used to select packets.
private Network mNetwork;
public static class Flow { public static class Flow {
// Minimum requirements for identifying a transform // Minimum requirements for identifying a transform
// SPI identifying the IPsec flow in packet processing // SPI identifying the IPsec flow in packet processing
// and a remote IP address // and a remote IP address
int spiResourceId; private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
// Encryption Algorithm // Encryption Algorithm
IpSecAlgorithm encryption; private IpSecAlgorithm mEncryption;
// Authentication Algorithm // Authentication Algorithm
IpSecAlgorithm authentication; private IpSecAlgorithm mAuthentication;
@Override @Override
public String toString() { public String toString() {
return new StringBuilder() return new StringBuilder()
.append("{spiResourceId=") .append("{mSpiResourceId=")
.append(spiResourceId) .append(mSpiResourceId)
.append(", encryption=") .append(", mEncryption=")
.append(encryption) .append(mEncryption)
.append(", authentication=") .append(", mAuthentication=")
.append(authentication) .append(mAuthentication)
.append("}") .append("}")
.toString(); .toString();
} }
static boolean equals(IpSecConfig.Flow lhs, IpSecConfig.Flow rhs) {
if (lhs == null || rhs == null) return (lhs == rhs);
return (lhs.mSpiResourceId == rhs.mSpiResourceId
&& IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
&& IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication));
}
} }
final Flow[] flow = new Flow[] {new Flow(), new Flow()}; private final Flow[] mFlow = 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
int encapType; private int mEncapType = IpSecTransform.ENCAP_NONE;
int encapLocalPortResourceId; private int mEncapSocketResourceId = IpSecManager.INVALID_RESOURCE_ID;
int encapRemotePort; private int mEncapRemotePort;
// An interval, in seconds between the NattKeepalive packets // An interval, in seconds between the NattKeepalive packets
int nattKeepaliveInterval; private int mNattKeepaliveInterval;
/** Set the mode for this IPsec transform */
public void setMode(int mode) {
mMode = mode;
}
/** Set the local IP address for Tunnel mode */
public void setLocalAddress(String localAddress) {
if (localAddress == null) {
throw new IllegalArgumentException("localAddress may not be null!");
}
mLocalAddress = localAddress;
}
/** Set the remote IP address for this IPsec transform */
public void setRemoteAddress(String remoteAddress) {
if (remoteAddress == null) {
throw new IllegalArgumentException("remoteAddress may not be null!");
}
mRemoteAddress = remoteAddress;
}
/** Set the SPI for a given direction by resource ID */
public void setSpiResourceId(int direction, int resourceId) {
mFlow[direction].mSpiResourceId = resourceId;
}
/** Set the encryption algorithm for a given direction */
public void setEncryption(int direction, IpSecAlgorithm encryption) {
mFlow[direction].mEncryption = encryption;
}
/** Set the authentication algorithm for a given direction */
public void setAuthentication(int direction, IpSecAlgorithm authentication) {
mFlow[direction].mAuthentication = authentication;
}
public void setNetwork(Network network) {
mNetwork = network;
}
public void setEncapType(int encapType) {
mEncapType = encapType;
}
public void setEncapSocketResourceId(int resourceId) {
mEncapSocketResourceId = resourceId;
}
public void setEncapRemotePort(int port) {
mEncapRemotePort = port;
}
public void setNattKeepaliveInterval(int interval) {
mNattKeepaliveInterval = interval;
}
// Transport or Tunnel // Transport or Tunnel
public int getMode() { public int getMode() {
return mode; return mMode;
} }
public InetAddress getLocalAddress() { public String getLocalAddress() {
return localAddress; return mLocalAddress;
} }
public int getSpiResourceId(int direction) { public int getSpiResourceId(int direction) {
return flow[direction].spiResourceId; return mFlow[direction].mSpiResourceId;
} }
public InetAddress getRemoteAddress() { public String getRemoteAddress() {
return remoteAddress; return mRemoteAddress;
} }
public IpSecAlgorithm getEncryption(int direction) { public IpSecAlgorithm getEncryption(int direction) {
return flow[direction].encryption; return mFlow[direction].mEncryption;
} }
public IpSecAlgorithm getAuthentication(int direction) { public IpSecAlgorithm getAuthentication(int direction) {
return flow[direction].authentication; return mFlow[direction].mAuthentication;
} }
public Network getNetwork() { public Network getNetwork() {
return network; return mNetwork;
} }
public int getEncapType() { public int getEncapType() {
return encapType; return mEncapType;
} }
public int getEncapLocalResourceId() { public int getEncapSocketResourceId() {
return encapLocalPortResourceId; return mEncapSocketResourceId;
} }
public int getEncapRemotePort() { public int getEncapRemotePort() {
return encapRemotePort; return mEncapRemotePort;
} }
public int getNattKeepaliveInterval() { public int getNattKeepaliveInterval() {
return nattKeepaliveInterval; return mNattKeepaliveInterval;
} }
// Parcelable Methods // Parcelable Methods
@@ -127,82 +192,70 @@ public final class IpSecConfig implements Parcelable {
@Override @Override
public void writeToParcel(Parcel out, int flags) { public void writeToParcel(Parcel out, int flags) {
// TODO: Use a byte array or other better method for storing IPs that can also include scope out.writeInt(mMode);
out.writeString((localAddress != null) ? localAddress.getHostAddress() : null); out.writeString(mLocalAddress);
// TODO: Use a byte array or other better method for storing IPs that can also include scope out.writeString(mRemoteAddress);
out.writeString((remoteAddress != null) ? remoteAddress.getHostAddress() : null); out.writeParcelable(mNetwork, flags);
out.writeParcelable(network, flags); out.writeInt(mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId);
out.writeInt(flow[IpSecTransform.DIRECTION_IN].spiResourceId); out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mEncryption, flags);
out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryption, flags); out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthentication, flags);
out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authentication, flags); out.writeInt(mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId);
out.writeInt(flow[IpSecTransform.DIRECTION_OUT].spiResourceId); out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mEncryption, flags);
out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryption, flags); out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication, flags);
out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authentication, flags); out.writeInt(mEncapType);
out.writeInt(encapType); out.writeInt(mEncapSocketResourceId);
out.writeInt(encapLocalPortResourceId); out.writeInt(mEncapRemotePort);
out.writeInt(encapRemotePort); out.writeInt(mNattKeepaliveInterval);
} }
// Package Private: Used by the IpSecTransform.Builder; @VisibleForTesting
// there should be no public constructor for this object public IpSecConfig() {}
IpSecConfig() {}
private static InetAddress readInetAddressFromParcel(Parcel in) {
String addrString = in.readString();
if (addrString == null) {
return null;
}
try {
return InetAddress.getByName(addrString);
} catch (UnknownHostException e) {
Log.wtf(TAG, "Invalid IpAddress " + addrString);
return null;
}
}
private IpSecConfig(Parcel in) { private IpSecConfig(Parcel in) {
localAddress = readInetAddressFromParcel(in); mMode = in.readInt();
remoteAddress = readInetAddressFromParcel(in); mLocalAddress = in.readString();
network = (Network) in.readParcelable(Network.class.getClassLoader()); mRemoteAddress = in.readString();
flow[IpSecTransform.DIRECTION_IN].spiResourceId = in.readInt(); mNetwork = (Network) in.readParcelable(Network.class.getClassLoader());
flow[IpSecTransform.DIRECTION_IN].encryption = mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId = in.readInt();
mFlow[IpSecTransform.DIRECTION_IN].mEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
flow[IpSecTransform.DIRECTION_IN].authentication = mFlow[IpSecTransform.DIRECTION_IN].mAuthentication =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
flow[IpSecTransform.DIRECTION_OUT].spiResourceId = in.readInt(); mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId = in.readInt();
flow[IpSecTransform.DIRECTION_OUT].encryption = mFlow[IpSecTransform.DIRECTION_OUT].mEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
flow[IpSecTransform.DIRECTION_OUT].authentication = mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
encapType = in.readInt(); mEncapType = in.readInt();
encapLocalPortResourceId = in.readInt(); mEncapSocketResourceId = in.readInt();
encapRemotePort = in.readInt(); mEncapRemotePort = in.readInt();
mNattKeepaliveInterval = in.readInt();
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder strBuilder = new StringBuilder(); StringBuilder strBuilder = new StringBuilder();
strBuilder strBuilder
.append("{mode=") .append("{mMode=")
.append(mode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT") .append(mMode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
.append(", localAddress=") .append(", mLocalAddress=")
.append(localAddress) .append(mLocalAddress)
.append(", remoteAddress=") .append(", mRemoteAddress=")
.append(remoteAddress) .append(mRemoteAddress)
.append(", network=") .append(", mNetwork=")
.append(network) .append(mNetwork)
.append(", encapType=") .append(", mEncapType=")
.append(encapType) .append(mEncapType)
.append(", encapLocalPortResourceId=") .append(", mEncapSocketResourceId=")
.append(encapLocalPortResourceId) .append(mEncapSocketResourceId)
.append(", encapRemotePort=") .append(", mEncapRemotePort=")
.append(encapRemotePort) .append(mEncapRemotePort)
.append(", nattKeepaliveInterval=") .append(", mNattKeepaliveInterval=")
.append(nattKeepaliveInterval) .append(mNattKeepaliveInterval)
.append(", flow[OUT]=") .append(", mFlow[OUT]=")
.append(flow[IpSecTransform.DIRECTION_OUT]) .append(mFlow[IpSecTransform.DIRECTION_OUT])
.append(", flow[IN]=") .append(", mFlow[IN]=")
.append(flow[IpSecTransform.DIRECTION_IN]) .append(mFlow[IpSecTransform.DIRECTION_IN])
.append("}"); .append("}");
return strBuilder.toString(); return strBuilder.toString();
@@ -218,4 +271,22 @@ public final class IpSecConfig implements Parcelable {
return new IpSecConfig[size]; return new IpSecConfig[size];
} }
}; };
@VisibleForTesting
public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) {
if (lhs == null || rhs == null) return (lhs == rhs);
return (lhs.mMode == rhs.mMode
&& lhs.mLocalAddress.equals(rhs.mLocalAddress)
&& lhs.mRemoteAddress.equals(rhs.mRemoteAddress)
&& ((lhs.mNetwork != null && lhs.mNetwork.equals(rhs.mNetwork))
|| (lhs.mNetwork == rhs.mNetwork))
&& lhs.mEncapType == rhs.mEncapType
&& lhs.mEncapSocketResourceId == rhs.mEncapSocketResourceId
&& lhs.mEncapRemotePort == rhs.mEncapRemotePort
&& lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
&& IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_OUT],
rhs.mFlow[IpSecTransform.DIRECTION_OUT])
&& IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_IN],
rhs.mFlow[IpSecTransform.DIRECTION_IN]));
}
} }

View File

@@ -26,6 +26,8 @@ import android.os.RemoteException;
import android.util.AndroidException; import android.util.AndroidException;
import android.util.Log; import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import dalvik.system.CloseGuard; import dalvik.system.CloseGuard;
import java.io.FileDescriptor; import java.io.FileDescriptor;
@@ -188,7 +190,8 @@ public final class IpSecManager {
} }
/** @hide */ /** @hide */
int getResourceId() { @VisibleForTesting
public int getResourceId() {
return mResourceId; return mResourceId;
} }
} }
@@ -489,7 +492,8 @@ public final class IpSecManager {
} }
/** @hide */ /** @hide */
int getResourceId() { @VisibleForTesting
public int getResourceId() {
return mResourceId; return mResourceId;
} }
}; };

View File

@@ -68,10 +68,10 @@ public final class IpSecTransform implements AutoCloseable {
public @interface TransformDirection {} public @interface TransformDirection {}
/** @hide */ /** @hide */
public static final int MODE_TUNNEL = 0; public static final int MODE_TRANSPORT = 0;
/** @hide */ /** @hide */
public static final int MODE_TRANSPORT = 1; public static final int MODE_TUNNEL = 1;
/** @hide */ /** @hide */
public static final int ENCAP_NONE = 0; public static final int ENCAP_NONE = 0;
@@ -113,7 +113,11 @@ public final class IpSecTransform implements AutoCloseable {
return IIpSecService.Stub.asInterface(b); return IIpSecService.Stub.asInterface(b);
} }
private void checkResultStatusAndThrow(int status) /**
* Checks the result status and throws an appropriate exception if
* the status is not Status.OK.
*/
private void checkResultStatus(int status)
throws IOException, IpSecManager.ResourceUnavailableException, throws IOException, IpSecManager.ResourceUnavailableException,
IpSecManager.SpiUnavailableException { IpSecManager.SpiUnavailableException {
switch (status) { switch (status) {
@@ -141,7 +145,7 @@ public final class IpSecTransform implements AutoCloseable {
IpSecTransformResponse result = IpSecTransformResponse result =
svc.createTransportModeTransform(mConfig, new Binder()); svc.createTransportModeTransform(mConfig, new Binder());
int status = result.status; int status = result.status;
checkResultStatusAndThrow(status); checkResultStatus(status);
mResourceId = result.resourceId; mResourceId = result.resourceId;
/* Keepalive will silently fail if not needed by the config; but, if needed and /* Keepalive will silently fail if not needed by the config; but, if needed and
@@ -243,61 +247,20 @@ 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) { Log.wtf(TAG, "Keepalive not yet supported.");
return;
}
ConnectivityManager cm =
(ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
if (mKeepalive != null) {
Log.wtf(TAG, "Keepalive already started for this IpSecTransform.");
return;
}
synchronized (mKeepaliveSyncLock) {
mKeepalive =
cm.startNattKeepalive(
mConfig.getNetwork(),
mConfig.getNattKeepaliveInterval(),
mKeepaliveCallback,
mConfig.getLocalAddress(),
0x1234, /* FIXME: get the real port number again,
which we need to retrieve from the provided
EncapsulationSocket, and which isn't currently
stashed in IpSecConfig */
mConfig.getRemoteAddress());
try {
// FIXME: this is still a horrible way to fudge the synchronous callback
mKeepaliveSyncLock.wait(2000);
} catch (InterruptedException e) {
}
}
if (mKeepaliveStatus != ConnectivityManager.PacketKeepalive.SUCCESS) {
throw new UnsupportedOperationException("Packet Keepalive cannot be started");
} }
} }
/* Package */ /** @hide */
int getResourceId() { @VisibleForTesting
public int getResourceId() {
return mResourceId; return mResourceId;
} }
/* Package */ /* Package */
void stopKeepalive() { void stopKeepalive() {
if (mKeepalive == null) { return;
return;
}
mKeepalive.stop();
synchronized (mKeepaliveSyncLock) {
if (mKeepaliveStatus == ConnectivityManager.PacketKeepalive.SUCCESS) {
try {
mKeepaliveSyncLock.wait(2000);
} catch (InterruptedException e) {
}
}
}
} }
/** /**
@@ -323,7 +286,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].encryption = algo; mConfig.setEncryption(direction, algo);
return this; return this;
} }
@@ -338,7 +301,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].authentication = algo; mConfig.setAuthentication(direction, algo);
return this; return this;
} }
@@ -361,9 +324,7 @@ 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 mConfig.setSpiResourceId(direction, spi.getResourceId());
// the owner in the IpSecService
mConfig.flow[direction].spiResourceId = spi.getResourceId();
return this; return this;
} }
@@ -378,7 +339,7 @@ public final class IpSecTransform implements AutoCloseable {
*/ */
@SystemApi @SystemApi
public IpSecTransform.Builder setUnderlyingNetwork(Network net) { public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
mConfig.network = net; mConfig.setNetwork(net);
return this; return this;
} }
@@ -395,10 +356,9 @@ public final class IpSecTransform implements AutoCloseable {
*/ */
public IpSecTransform.Builder setIpv4Encapsulation( public IpSecTransform.Builder setIpv4Encapsulation(
IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) { IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
// TODO: check encap type is valid. mConfig.setEncapType(ENCAP_ESPINUDP);
mConfig.encapType = ENCAP_ESPINUDP; mConfig.setEncapSocketResourceId(localSocket.getResourceId());
mConfig.encapLocalPortResourceId = localSocket.getResourceId(); mConfig.setEncapRemotePort(remotePort);
mConfig.encapRemotePort = remotePort;
return this; return this;
} }
@@ -416,7 +376,7 @@ public final class IpSecTransform implements AutoCloseable {
*/ */
@SystemApi @SystemApi
public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) { public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
mConfig.nattKeepaliveInterval = intervalSeconds; mConfig.setNattKeepaliveInterval(intervalSeconds);
return this; return this;
} }
@@ -451,8 +411,8 @@ public final class IpSecTransform implements AutoCloseable {
IpSecManager.SpiUnavailableException, IOException { IpSecManager.SpiUnavailableException, IOException {
//FIXME: argument validation here //FIXME: argument validation here
//throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation"); //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
mConfig.mode = MODE_TRANSPORT; mConfig.setMode(MODE_TRANSPORT);
mConfig.remoteAddress = remoteAddress; mConfig.setRemoteAddress(remoteAddress.getHostAddress());
return new IpSecTransform(mContext, mConfig).activate(); return new IpSecTransform(mContext, mConfig).activate();
} }
@@ -473,9 +433,9 @@ public final class IpSecTransform implements AutoCloseable {
InetAddress localAddress, InetAddress remoteAddress) { InetAddress localAddress, InetAddress remoteAddress) {
//FIXME: argument validation here //FIXME: argument validation here
//throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation"); //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
mConfig.localAddress = localAddress; mConfig.setLocalAddress(localAddress.getHostAddress());
mConfig.remoteAddress = remoteAddress; mConfig.setRemoteAddress(remoteAddress.getHostAddress());
mConfig.mode = MODE_TUNNEL; mConfig.setMode(MODE_TUNNEL);
return new IpSecTransform(mContext, mConfig); return new IpSecTransform(mContext, mConfig);
} }
@@ -489,14 +449,5 @@ public final class IpSecTransform implements AutoCloseable {
mContext = context; mContext = context;
mConfig = new IpSecConfig(); mConfig = new IpSecConfig();
} }
/**
* Return an {@link IpSecConfig} object for testing purposes.
* @hide
*/
@VisibleForTesting
public IpSecConfig getIpSecConfig() {
return mConfig;
}
} }
} }

View File

@@ -33,6 +33,7 @@ import android.net.IpSecSpiResponse;
import android.net.IpSecTransform; import android.net.IpSecTransform;
import android.net.IpSecTransformResponse; import android.net.IpSecTransformResponse;
import android.net.IpSecUdpEncapResponse; import android.net.IpSecUdpEncapResponse;
import android.net.NetworkUtils;
import android.net.util.NetdService; import android.net.util.NetdService;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
@@ -42,11 +43,14 @@ import android.os.ServiceSpecificException;
import android.system.ErrnoException; import android.system.ErrnoException;
import android.system.Os; import android.system.Os;
import android.system.OsConstants; import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
@@ -54,6 +58,7 @@ import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import libcore.io.IoUtils; import libcore.io.IoUtils;
/** @hide */ /** @hide */
@@ -252,7 +257,11 @@ public class IpSecService extends IIpSecService.Stub {
return (mReferenceCount.get() > 0); return (mReferenceCount.get() > 0);
} }
public void checkOwnerOrSystemAndThrow() { /**
* Ensures that the caller is either the owner of this resource or has the system UID and
* throws a SecurityException otherwise.
*/
public void checkOwnerOrSystem() {
if (uid != Binder.getCallingUid() if (uid != Binder.getCallingUid()
&& android.os.Process.SYSTEM_UID != Binder.getCallingUid()) { && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
throw new SecurityException("Only the owner may access managed resources!"); throw new SecurityException("Only the owner may access managed resources!");
@@ -340,7 +349,7 @@ public class IpSecService extends IIpSecService.Stub {
// The value should never be null unless the resource doesn't exist // The value should never be null unless the resource doesn't exist
// (since we do not allow null resources to be added). // (since we do not allow null resources to be added).
if (val != null) { if (val != null) {
val.checkOwnerOrSystemAndThrow(); val.checkOwnerOrSystem();
} }
return val; return val;
} }
@@ -405,12 +414,8 @@ public class IpSecService extends IIpSecService.Stub {
.ipSecDeleteSecurityAssociation( .ipSecDeleteSecurityAssociation(
mResourceId, mResourceId,
direction, direction,
(mConfig.getLocalAddress() != null) mConfig.getLocalAddress(),
? mConfig.getLocalAddress().getHostAddress() mConfig.getRemoteAddress(),
: "",
(mConfig.getRemoteAddress() != null)
? mConfig.getRemoteAddress().getHostAddress()
: "",
spi); spi);
} catch (ServiceSpecificException e) { } catch (ServiceSpecificException e) {
// FIXME: get the error code and throw is at an IOException from Errno Exception // FIXME: get the error code and throw is at an IOException from Errno Exception
@@ -638,11 +643,45 @@ public class IpSecService extends IIpSecService.Stub {
} }
} }
/**
* Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
* a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
*/
private static void checkInetAddress(String inetAddress) {
if (TextUtils.isEmpty(inetAddress)) {
throw new IllegalArgumentException("Unspecified address");
}
InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
if (checkAddr.isAnyLocalAddress()) {
throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
}
}
/**
* Checks the user-provided direction field and throws an IllegalArgumentException if it is not
* DIRECTION_IN or DIRECTION_OUT
*/
private static void checkDirection(int direction) {
switch (direction) {
case IpSecTransform.DIRECTION_OUT:
case IpSecTransform.DIRECTION_IN:
return;
}
throw new IllegalArgumentException("Invalid Direction: " + direction);
}
@Override @Override
/** Get a new SPI and maintain the reservation in the system server */ /** Get a new SPI and maintain the reservation in the system server */
public synchronized IpSecSpiResponse reserveSecurityParameterIndex( public synchronized IpSecSpiResponse reserveSecurityParameterIndex(
int direction, String remoteAddress, int requestedSpi, IBinder binder) int direction, String remoteAddress, int requestedSpi, IBinder binder)
throws RemoteException { throws RemoteException {
checkDirection(direction);
checkInetAddress(remoteAddress);
/* requestedSpi can be anything in the int range, so no check is needed. */
checkNotNull(binder, "Null Binder passed to reserveSecurityParameterIndex");
int resourceId = mNextResourceId.getAndIncrement(); int resourceId = mNextResourceId.getAndIncrement();
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
@@ -651,9 +690,7 @@ public class IpSecService extends IIpSecService.Stub {
try { try {
if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).spi.isAvailable()) { if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).spi.isAvailable()) {
return new IpSecSpiResponse( return new IpSecSpiResponse(
IpSecManager.Status.RESOURCE_UNAVAILABLE, IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
INVALID_RESOURCE_ID,
spi);
} }
spi = spi =
mSrvConfig mSrvConfig
@@ -751,6 +788,8 @@ public class IpSecService extends IIpSecService.Stub {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Specified port number must be a valid non-reserved UDP port"); "Specified port number must be a valid non-reserved UDP port");
} }
checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
int resourceId = mNextResourceId.getAndIncrement(); int resourceId = mNextResourceId.getAndIncrement();
FileDescriptor sockFd = null; FileDescriptor sockFd = null;
try { try {
@@ -791,6 +830,67 @@ public class IpSecService extends IIpSecService.Stub {
releaseManagedResource(mUdpSocketRecords, resourceId, "UdpEncapsulationSocket"); releaseManagedResource(mUdpSocketRecords, resourceId, "UdpEncapsulationSocket");
} }
/**
* Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
* IllegalArgumentException if they are not.
*/
private void checkIpSecConfig(IpSecConfig config) {
if (config.getLocalAddress() == null) {
throw new IllegalArgumentException("Invalid null Local InetAddress");
}
if (config.getRemoteAddress() == null) {
throw new IllegalArgumentException("Invalid null Remote InetAddress");
}
switch (config.getMode()) {
case IpSecTransform.MODE_TRANSPORT:
if (!config.getLocalAddress().isEmpty()) {
throw new IllegalArgumentException("Non-empty Local Address");
}
// Must be valid, and not a wildcard
checkInetAddress(config.getRemoteAddress());
break;
case IpSecTransform.MODE_TUNNEL:
break;
default:
throw new IllegalArgumentException(
"Invalid IpSecTransform.mode: " + config.getMode());
}
switch (config.getEncapType()) {
case IpSecTransform.ENCAP_NONE:
break;
case IpSecTransform.ENCAP_ESPINUDP:
case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
if (mUdpSocketRecords.get(config.getEncapSocketResourceId()) == null) {
throw new IllegalStateException(
"No Encapsulation socket for Resource Id: "
+ config.getEncapSocketResourceId());
}
int port = config.getEncapRemotePort();
if (port <= 0 || port > 0xFFFF) {
throw new IllegalArgumentException("Invalid remote UDP port: " + port);
}
break;
default:
throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
}
for (int direction : DIRECTIONS) {
IpSecAlgorithm crypt = config.getEncryption(direction);
IpSecAlgorithm auth = config.getAuthentication(direction);
if (crypt == null && auth == null) {
throw new IllegalArgumentException("Encryption and Authentication are both null");
}
if (mSpiRecords.get(config.getSpiResourceId(direction)) == null) {
throw new IllegalStateException("No SPI for specified Resource Id");
}
}
}
/** /**
* Create a transport mode transform, which represent two security associations (one in each * 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 * direction) in the kernel. The transform will be cached by the system server and must be freed
@@ -801,17 +901,19 @@ public class IpSecService extends IIpSecService.Stub {
@Override @Override
public synchronized IpSecTransformResponse createTransportModeTransform( public synchronized IpSecTransformResponse createTransportModeTransform(
IpSecConfig c, IBinder binder) throws RemoteException { IpSecConfig c, IBinder binder) throws RemoteException {
checkIpSecConfig(c);
checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
int resourceId = mNextResourceId.getAndIncrement(); int resourceId = mNextResourceId.getAndIncrement();
if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).transform.isAvailable()) { if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).transform.isAvailable()) {
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE); 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
int encapType, encapLocalPort = 0, encapRemotePort = 0; int encapType, encapLocalPort = 0, encapRemotePort = 0;
UdpSocketRecord socketRecord = null; UdpSocketRecord socketRecord = null;
encapType = c.getEncapType(); encapType = c.getEncapType();
if (encapType != IpSecTransform.ENCAP_NONE) { if (encapType != IpSecTransform.ENCAP_NONE) {
socketRecord = mUdpSocketRecords.get(c.getEncapLocalResourceId()); socketRecord = mUdpSocketRecords.get(c.getEncapSocketResourceId());
encapLocalPort = socketRecord.getPort(); encapLocalPort = socketRecord.getPort();
encapRemotePort = c.getEncapRemotePort(); encapRemotePort = c.getEncapRemotePort();
} }
@@ -823,20 +925,15 @@ public class IpSecService extends IIpSecService.Stub {
spis[direction] = mSpiRecords.get(c.getSpiResourceId(direction)); spis[direction] = mSpiRecords.get(c.getSpiResourceId(direction));
int spi = spis[direction].getSpi(); int spi = spis[direction].getSpi();
try { try {
mSrvConfig.getNetdInstance() mSrvConfig
.getNetdInstance()
.ipSecAddSecurityAssociation( .ipSecAddSecurityAssociation(
resourceId, resourceId,
c.getMode(), c.getMode(),
direction, direction,
(c.getLocalAddress() != null) c.getLocalAddress(),
? c.getLocalAddress().getHostAddress() c.getRemoteAddress(),
: "", (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0,
(c.getRemoteAddress() != null)
? c.getRemoteAddress().getHostAddress()
: "",
(c.getNetwork() != null)
? c.getNetwork().getNetworkHandle()
: 0,
spi, spi,
(auth != null) ? auth.getName() : "", (auth != null) ? auth.getName() : "",
(auth != null) ? auth.getKey() : null, (auth != null) ? auth.getKey() : null,
@@ -899,12 +996,8 @@ public class IpSecService extends IIpSecService.Stub {
socket.getFileDescriptor(), socket.getFileDescriptor(),
resourceId, resourceId,
direction, direction,
(c.getLocalAddress() != null) c.getLocalAddress(),
? c.getLocalAddress().getHostAddress() c.getRemoteAddress(),
: "",
(c.getRemoteAddress() != null)
? c.getRemoteAddress().getHostAddress()
: "",
info.getSpiRecord(direction).getSpi()); info.getSpiRecord(direction).getSpi());
} }
} catch (ServiceSpecificException e) { } catch (ServiceSpecificException e) {

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link IpSecConfig}. */
@SmallTest
@RunWith(JUnit4.class)
public class IpSecConfigTest {
@Test
public void testDefaults() throws Exception {
IpSecConfig c = new IpSecConfig();
assertEquals(IpSecTransform.MODE_TRANSPORT, c.getMode());
assertEquals("", c.getLocalAddress());
assertEquals("", c.getRemoteAddress());
assertNull(c.getNetwork());
assertEquals(IpSecTransform.ENCAP_NONE, c.getEncapType());
assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getEncapSocketResourceId());
assertEquals(0, c.getEncapRemotePort());
assertEquals(0, c.getNattKeepaliveInterval());
for (int direction :
new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}) {
assertNull(c.getEncryption(direction));
assertNull(c.getAuthentication(direction));
assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId(direction));
}
}
@Test
public void testParcelUnparcel() throws Exception {
assertParcelingIsLossless(new IpSecConfig());
IpSecConfig c = new IpSecConfig();
c.setMode(IpSecTransform.MODE_TUNNEL);
c.setLocalAddress("0.0.0.0");
c.setRemoteAddress("1.2.3.4");
c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
c.setEncapSocketResourceId(7);
c.setEncapRemotePort(22);
c.setNattKeepaliveInterval(42);
c.setEncryption(
IpSecTransform.DIRECTION_OUT,
new IpSecAlgorithm(
IpSecAlgorithm.CRYPT_AES_CBC,
new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
c.setAuthentication(
IpSecTransform.DIRECTION_OUT,
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_HMAC_SHA1,
new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
c.setSpiResourceId(IpSecTransform.DIRECTION_OUT, 1984);
c.setEncryption(
IpSecTransform.DIRECTION_IN,
new IpSecAlgorithm(
IpSecAlgorithm.CRYPT_AES_CBC,
new byte[] {2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
c.setAuthentication(
IpSecTransform.DIRECTION_IN,
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_HMAC_SHA1,
new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 1}));
c.setSpiResourceId(IpSecTransform.DIRECTION_IN, 99);
assertParcelingIsLossless(c);
}
private void assertParcelingIsLossless(IpSecConfig ci) throws Exception {
Parcel p = Parcel.obtain();
ci.writeToParcel(p, 0);
p.setDataPosition(0);
IpSecConfig co = IpSecConfig.CREATOR.createFromParcel(p);
assertTrue(IpSecConfig.equals(co, ci));
}
}

View File

@@ -0,0 +1,276 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.INetd;
import android.net.IpSecAlgorithm;
import android.net.IpSecConfig;
import android.net.IpSecManager;
import android.net.IpSecSpiResponse;
import android.net.IpSecTransform;
import android.net.IpSecTransformResponse;
import android.net.NetworkUtils;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.support.test.filters.SmallTest;
import android.system.OsConstants;
import java.net.Socket;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
/** Unit tests for {@link IpSecService}. */
@SmallTest
@RunWith(Parameterized.class)
public class IpSecServiceParameterizedTest {
private static final int DROID_SPI = 0xD1201D;
private static final int DROID_SPI2 = DROID_SPI + 1;
private final String mRemoteAddr;
@Parameterized.Parameters
public static Collection ipSecConfigs() {
return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}});
}
private static final byte[] CRYPT_KEY = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
private static final byte[] AUTH_KEY = {
0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
};
Context mMockContext;
INetd mMockNetd;
IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
IpSecService mIpSecService;
public IpSecServiceParameterizedTest(String remoteAddr) {
mRemoteAddr = remoteAddr;
}
@Before
public void setUp() throws Exception {
mMockContext = mock(Context.class);
mMockNetd = mock(INetd.class);
mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
// Injecting mock netd
when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
}
@Test
public void testIpSecServiceReserveSpi() throws Exception {
when(mMockNetd.ipSecAllocateSpi(
anyInt(),
eq(IpSecTransform.DIRECTION_OUT),
anyString(),
eq(mRemoteAddr),
eq(DROID_SPI)))
.thenReturn(DROID_SPI);
IpSecSpiResponse spiResp =
mIpSecService.reserveSecurityParameterIndex(
IpSecTransform.DIRECTION_OUT, mRemoteAddr, DROID_SPI, new Binder());
assertEquals(IpSecManager.Status.OK, spiResp.status);
assertEquals(DROID_SPI, spiResp.spi);
}
@Test
public void testReleaseSecurityParameterIndex() throws Exception {
when(mMockNetd.ipSecAllocateSpi(
anyInt(),
eq(IpSecTransform.DIRECTION_OUT),
anyString(),
eq(mRemoteAddr),
eq(DROID_SPI)))
.thenReturn(DROID_SPI);
IpSecSpiResponse spiResp =
mIpSecService.reserveSecurityParameterIndex(
IpSecTransform.DIRECTION_OUT, mRemoteAddr, DROID_SPI, new Binder());
mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
eq(spiResp.resourceId), anyInt(), anyString(), anyString(), eq(DROID_SPI));
}
IpSecConfig buildIpSecConfig() throws Exception {
IpSecManager ipSecManager = new IpSecManager(mIpSecService);
// Mocking the netd to allocate SPI
when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt()))
.thenReturn(DROID_SPI)
.thenReturn(DROID_SPI2);
IpSecAlgorithm encryptAlgo = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
IpSecAlgorithm authAlgo =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 8);
/** Allocate and add SPI records in the IpSecService through IpSecManager interface. */
IpSecManager.SecurityParameterIndex outSpi =
ipSecManager.reserveSecurityParameterIndex(
IpSecTransform.DIRECTION_OUT,
NetworkUtils.numericToInetAddress(mRemoteAddr));
IpSecManager.SecurityParameterIndex inSpi =
ipSecManager.reserveSecurityParameterIndex(
IpSecTransform.DIRECTION_IN,
NetworkUtils.numericToInetAddress(mRemoteAddr));
IpSecConfig config = new IpSecConfig();
config.setSpiResourceId(IpSecTransform.DIRECTION_IN, inSpi.getResourceId());
config.setSpiResourceId(IpSecTransform.DIRECTION_OUT, outSpi.getResourceId());
config.setEncryption(IpSecTransform.DIRECTION_OUT, encryptAlgo);
config.setAuthentication(IpSecTransform.DIRECTION_OUT, authAlgo);
config.setEncryption(IpSecTransform.DIRECTION_IN, encryptAlgo);
config.setAuthentication(IpSecTransform.DIRECTION_IN, authAlgo);
config.setRemoteAddress(mRemoteAddr);
return config;
}
@Test
public void testCreateTransportModeTransform() throws Exception {
IpSecConfig ipSecConfig = buildIpSecConfig();
IpSecTransformResponse createTransformResp =
mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
verify(mMockNetd)
.ipSecAddSecurityAssociation(
eq(createTransformResp.resourceId),
anyInt(),
eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
anyLong(),
eq(DROID_SPI),
eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
eq(AUTH_KEY),
anyInt(),
eq(IpSecAlgorithm.CRYPT_AES_CBC),
eq(CRYPT_KEY),
anyInt(),
anyInt(),
anyInt(),
anyInt());
verify(mMockNetd)
.ipSecAddSecurityAssociation(
eq(createTransformResp.resourceId),
anyInt(),
eq(IpSecTransform.DIRECTION_IN),
anyString(),
anyString(),
anyLong(),
eq(DROID_SPI2),
eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
eq(AUTH_KEY),
anyInt(),
eq(IpSecAlgorithm.CRYPT_AES_CBC),
eq(CRYPT_KEY),
anyInt(),
anyInt(),
anyInt(),
anyInt());
}
@Test
public void testDeleteTransportModeTransform() throws Exception {
IpSecConfig ipSecConfig = buildIpSecConfig();
IpSecTransformResponse createTransformResp =
mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId);
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
eq(createTransformResp.resourceId),
eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
eq(DROID_SPI));
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
eq(createTransformResp.resourceId),
eq(IpSecTransform.DIRECTION_IN),
anyString(),
anyString(),
eq(DROID_SPI2));
}
@Test
public void testApplyTransportModeTransform() throws Exception {
IpSecConfig ipSecConfig = buildIpSecConfig();
IpSecTransformResponse createTransformResp =
mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
int resourceId = createTransformResp.resourceId;
mIpSecService.applyTransportModeTransform(pfd, resourceId);
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
eq(pfd.getFileDescriptor()),
eq(resourceId),
eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
eq(DROID_SPI));
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
eq(pfd.getFileDescriptor()),
eq(resourceId),
eq(IpSecTransform.DIRECTION_IN),
anyString(),
anyString(),
eq(DROID_SPI2));
}
@Test
public void testRemoveTransportModeTransform() throws Exception {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
mIpSecService.removeTransportModeTransform(pfd, 1);
verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
}
}

View File

@@ -23,34 +23,28 @@ import static android.system.OsConstants.SOCK_DGRAM;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.net.INetd; import android.net.INetd;
import android.net.IpSecAlgorithm;
import android.net.IpSecConfig;
import android.net.IpSecManager; import android.net.IpSecManager;
import android.net.IpSecSpiResponse; import android.net.IpSecSpiResponse;
import android.net.IpSecTransform; import android.net.IpSecTransform;
import android.net.IpSecTransformResponse;
import android.net.IpSecUdpEncapResponse; import android.net.IpSecUdpEncapResponse;
import android.os.Binder; import android.os.Binder;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import android.system.ErrnoException; import android.system.ErrnoException;
import android.system.Os; import android.system.Os;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -62,13 +56,8 @@ import org.junit.runners.JUnit4;
public class IpSecServiceTest { public class IpSecServiceTest {
private static final int DROID_SPI = 0xD1201D; private static final int DROID_SPI = 0xD1201D;
private static final int DROID_SPI2 = DROID_SPI + 1;
private static final int TEST_UDP_ENCAP_INVALID_PORT = 100; private static final int TEST_UDP_ENCAP_INVALID_PORT = 100;
private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000; private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000;
private static final int TEST_UDP_ENCAP_PORT = 34567;
private static final String IPV4_LOOPBACK = "127.0.0.1";
private static final String IPV4_ADDR = "192.168.0.2";
private static final InetAddress INADDR_ANY; private static final InetAddress INADDR_ANY;
@@ -80,21 +69,6 @@ public class IpSecServiceTest {
} }
} }
private static final int[] DIRECTIONS =
new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
private static final byte[] CRYPT_KEY = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
private static final byte[] AUTH_KEY = {
0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
};
Context mMockContext; Context mMockContext;
INetd mMockNetd; INetd mMockNetd;
IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
@@ -117,44 +91,6 @@ public class IpSecServiceTest {
assertNotNull(ipSecSrv); assertNotNull(ipSecSrv);
} }
@Test
public void testIpSecServiceReserveSpi() throws Exception {
when(mMockNetd.ipSecAllocateSpi(
anyInt(),
eq(IpSecTransform.DIRECTION_OUT),
anyString(),
eq(IPV4_LOOPBACK),
eq(DROID_SPI)))
.thenReturn(DROID_SPI);
IpSecSpiResponse spiResp =
mIpSecService.reserveSecurityParameterIndex(
IpSecTransform.DIRECTION_OUT, IPV4_LOOPBACK, DROID_SPI, new Binder());
assertEquals(IpSecManager.Status.OK, spiResp.status);
assertEquals(DROID_SPI, spiResp.spi);
}
@Test
public void testReleaseSecurityParameterIndex() throws Exception {
when(mMockNetd.ipSecAllocateSpi(
anyInt(),
eq(IpSecTransform.DIRECTION_OUT),
anyString(),
eq(IPV4_LOOPBACK),
eq(DROID_SPI)))
.thenReturn(DROID_SPI);
IpSecSpiResponse spiResp =
mIpSecService.reserveSecurityParameterIndex(
IpSecTransform.DIRECTION_OUT, IPV4_LOOPBACK, DROID_SPI, new Binder());
mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
eq(spiResp.resourceId), anyInt(), anyString(), anyString(), eq(DROID_SPI));
}
@Test @Test
public void testReleaseInvalidSecurityParameterIndex() throws Exception { public void testReleaseInvalidSecurityParameterIndex() throws Exception {
try { try {
@@ -285,108 +221,6 @@ public class IpSecServiceTest {
} }
} }
IpSecConfig buildIpSecConfig() throws Exception {
IpSecManager ipSecManager = new IpSecManager(mIpSecService);
// Mocking the netd to allocate SPI
when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt()))
.thenReturn(DROID_SPI)
.thenReturn(DROID_SPI2);
IpSecAlgorithm encryptAlgo = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
IpSecAlgorithm authAlgo =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 8);
InetAddress localAddr = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
/** Allocate and add SPI records in the IpSecService through IpSecManager interface. */
IpSecManager.SecurityParameterIndex outSpi =
ipSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, localAddr);
IpSecManager.SecurityParameterIndex inSpi =
ipSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_IN, localAddr);
IpSecConfig ipSecConfig =
new IpSecTransform.Builder(mMockContext)
.setSpi(IpSecTransform.DIRECTION_OUT, outSpi)
.setSpi(IpSecTransform.DIRECTION_IN, inSpi)
.setEncryption(IpSecTransform.DIRECTION_OUT, encryptAlgo)
.setAuthentication(IpSecTransform.DIRECTION_OUT, authAlgo)
.setEncryption(IpSecTransform.DIRECTION_IN, encryptAlgo)
.setAuthentication(IpSecTransform.DIRECTION_IN, authAlgo)
.getIpSecConfig();
return ipSecConfig;
}
@Test
public void testCreateTransportModeTransform() throws Exception {
IpSecConfig ipSecConfig = buildIpSecConfig();
IpSecTransformResponse createTransformResp =
mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
verify(mMockNetd)
.ipSecAddSecurityAssociation(
eq(createTransformResp.resourceId),
anyInt(),
eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
anyLong(),
eq(DROID_SPI),
eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
eq(AUTH_KEY),
anyInt(),
eq(IpSecAlgorithm.CRYPT_AES_CBC),
eq(CRYPT_KEY),
anyInt(),
anyInt(),
anyInt(),
anyInt());
verify(mMockNetd)
.ipSecAddSecurityAssociation(
eq(createTransformResp.resourceId),
anyInt(),
eq(IpSecTransform.DIRECTION_IN),
anyString(),
anyString(),
anyLong(),
eq(DROID_SPI2),
eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
eq(AUTH_KEY),
anyInt(),
eq(IpSecAlgorithm.CRYPT_AES_CBC),
eq(CRYPT_KEY),
anyInt(),
anyInt(),
anyInt(),
anyInt());
}
@Test
public void testDeleteTransportModeTransform() throws Exception {
IpSecConfig ipSecConfig = buildIpSecConfig();
IpSecTransformResponse createTransformResp =
mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId);
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
eq(createTransformResp.resourceId),
eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
eq(DROID_SPI));
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
eq(createTransformResp.resourceId),
eq(IpSecTransform.DIRECTION_IN),
anyString(),
anyString(),
eq(DROID_SPI2));
}
@Test @Test
public void testDeleteInvalidTransportModeTransform() throws Exception { public void testDeleteInvalidTransportModeTransform() throws Exception {
try { try {
@@ -396,35 +230,6 @@ public class IpSecServiceTest {
} }
} }
@Test
public void testApplyTransportModeTransform() throws Exception {
IpSecConfig ipSecConfig = buildIpSecConfig();
IpSecTransformResponse createTransformResp =
mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
int resourceId = createTransformResp.resourceId;
mIpSecService.applyTransportModeTransform(pfd, resourceId);
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
eq(pfd.getFileDescriptor()),
eq(resourceId),
eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
eq(DROID_SPI));
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
eq(pfd.getFileDescriptor()),
eq(resourceId),
eq(IpSecTransform.DIRECTION_IN),
anyString(),
anyString(),
eq(DROID_SPI2));
}
@Test @Test
public void testRemoveTransportModeTransform() throws Exception { public void testRemoveTransportModeTransform() throws Exception {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
@@ -432,4 +237,25 @@ public class IpSecServiceTest {
verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor()); verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
} }
@Test
public void testValidateIpAddresses() throws Exception {
String[] invalidAddresses =
new String[] {"www.google.com", "::", "2001::/64", "0.0.0.0", ""};
for (String address : invalidAddresses) {
try {
IpSecSpiResponse spiResp =
mIpSecService.reserveSecurityParameterIndex(
IpSecTransform.DIRECTION_OUT, address, DROID_SPI, new Binder());
fail("Invalid address was passed through IpSecService validation: " + address);
} catch (IllegalArgumentException e) {
} catch (Exception e) {
fail(
"Invalid InetAddress was not caught in validation: "
+ address
+ ", Exception: "
+ e);
}
}
}
} }