Merge changes I0baf582f,I4bba01ba

* changes:
  Block unpriviledged apps which create keepalives with null fd
  Fix keepalive don't get removed when lower layer error
This commit is contained in:
Junyu Lai
2019-03-28 00:53:20 +00:00
committed by Gerrit Code Review
3 changed files with 28 additions and 11 deletions

View File

@@ -1934,6 +1934,8 @@ public class ConnectivityManager {
@NonNull Callback callback) { @NonNull Callback callback) {
ParcelFileDescriptor dup; ParcelFileDescriptor dup;
try { try {
// Dup is needed here as the pfd inside the socket is owned by the IpSecService,
// which cannot be obtained by the app process.
dup = ParcelFileDescriptor.dup(socket.getFileDescriptor()); dup = ParcelFileDescriptor.dup(socket.getFileDescriptor());
} catch (IOException ignored) { } catch (IOException ignored) {
// Construct an invalid fd, so that if the user later calls start(), it will fail with // Construct an invalid fd, so that if the user later calls start(), it will fail with
@@ -1975,6 +1977,7 @@ public class ConnectivityManager {
@NonNull Callback callback) { @NonNull Callback callback) {
ParcelFileDescriptor dup; ParcelFileDescriptor dup;
try { try {
// TODO: Consider remove unnecessary dup.
dup = pfd.dup(); dup = pfd.dup();
} catch (IOException ignored) { } catch (IOException ignored) {
// Construct an invalid fd, so that if the user later calls start(), it will fail with // Construct an invalid fd, so that if the user later calls start(), it will fail with

View File

@@ -154,12 +154,19 @@ public class KeepaliveTracker {
// keepalives are sent cannot be reused by another app even if the fd gets closed by // keepalives are sent cannot be reused by another app even if the fd gets closed by
// the user. A null is acceptable here for backward compatibility of PacketKeepalive // the user. A null is acceptable here for backward compatibility of PacketKeepalive
// API. // API.
// TODO: don't accept null fd after legacy packetKeepalive API is removed.
try { try {
if (fd != null) { if (fd != null) {
mFd = Os.dup(fd); mFd = Os.dup(fd);
} else { } else {
Log.d(TAG, "uid/pid " + mUid + "/" + mPid + " calls with null fd"); Log.d(TAG, toString() + " calls with null fd");
if (!mPrivileged) {
throw new SecurityException(
"null fd is not allowed for unprivileged access.");
}
if (mType == TYPE_TCP) {
throw new IllegalArgumentException(
"null fd is not allowed for tcp socket keepalives.");
}
mFd = null; mFd = null;
} }
} catch (ErrnoException e) { } catch (ErrnoException e) {
@@ -480,7 +487,6 @@ public class KeepaliveTracker {
} }
} else { } else {
// Keepalive successfully stopped, or error. // Keepalive successfully stopped, or error.
ki.mStartedState = KeepaliveInfo.NOT_STARTED;
if (reason == SUCCESS) { if (reason == SUCCESS) {
// The message indicated success stopping : don't call handleStopKeepalive. // The message indicated success stopping : don't call handleStopKeepalive.
if (DBG) Log.d(TAG, "Successfully stopped keepalive " + slot + " on " + nai.name()); if (DBG) Log.d(TAG, "Successfully stopped keepalive " + slot + " on " + nai.name());
@@ -490,6 +496,7 @@ public class KeepaliveTracker {
handleStopKeepalive(nai, slot, reason); handleStopKeepalive(nai, slot, reason);
if (DBG) Log.d(TAG, "Keepalive " + slot + " on " + nai.name() + " error " + reason); if (DBG) Log.d(TAG, "Keepalive " + slot + " on " + nai.name() + " error " + reason);
} }
ki.mStartedState = KeepaliveInfo.NOT_STARTED;
} }
} }
@@ -531,7 +538,8 @@ public class KeepaliveTracker {
try { try {
ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds, ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
KeepaliveInfo.TYPE_NATT, fd); KeepaliveInfo.TYPE_NATT, fd);
} catch (InvalidSocketException e) { } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
Log.e(TAG, "Fail to construct keepalive", e);
notifyErrorCallback(cb, ERROR_INVALID_SOCKET); notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
return; return;
} }
@@ -570,7 +578,8 @@ public class KeepaliveTracker {
try { try {
ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds, ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
KeepaliveInfo.TYPE_TCP, fd); KeepaliveInfo.TYPE_TCP, fd);
} catch (InvalidSocketException e) { } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
Log.e(TAG, "Fail to construct keepalive e=" + e);
notifyErrorCallback(cb, ERROR_INVALID_SOCKET); notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
return; return;
} }

View File

@@ -4066,8 +4066,6 @@ public class ConnectivityServiceTest {
// TODO: 1. Move this outside of ConnectivityServiceTest. // TODO: 1. Move this outside of ConnectivityServiceTest.
// 2. Make test to verify that Nat-T keepalive socket is created by IpSecService. // 2. Make test to verify that Nat-T keepalive socket is created by IpSecService.
// 3. Mock ipsec service. // 3. Mock ipsec service.
// 4. Find a free port instead of a fixed port.
final int srcPort = 12345;
final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129"); final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
final InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35"); final InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
final InetAddress myIPv6 = InetAddress.getByName("2001:db8::1"); final InetAddress myIPv6 = InetAddress.getByName("2001:db8::1");
@@ -4078,7 +4076,8 @@ public class ConnectivityServiceTest {
final int invalidKaInterval = 9; final int invalidKaInterval = 9;
final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE); final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(srcPort); final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket();
final int srcPort = testSocket.getPort();
LinkProperties lp = new LinkProperties(); LinkProperties lp = new LinkProperties();
lp.setInterfaceName("wlan12"); lp.setInterfaceName("wlan12");
@@ -4198,6 +4197,7 @@ public class ConnectivityServiceTest {
// Check that keepalive slots start from 1 and increment. The first one gets slot 1. // Check that keepalive slots start from 1 and increment. The first one gets slot 1.
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1); mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
int srcPort2 = 0;
try (SocketKeepalive ka = mCm.createSocketKeepalive( try (SocketKeepalive ka = mCm.createSocketKeepalive(
myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
ka.start(validKaInterval); ka.start(validKaInterval);
@@ -4205,7 +4205,8 @@ public class ConnectivityServiceTest {
// The second one gets slot 2. // The second one gets slot 2.
mWiFiNetworkAgent.setExpectedKeepaliveSlot(2); mWiFiNetworkAgent.setExpectedKeepaliveSlot(2);
final UdpEncapsulationSocket testSocket2 = mIpSec.openUdpEncapsulationSocket(6789); final UdpEncapsulationSocket testSocket2 = mIpSec.openUdpEncapsulationSocket();
srcPort2 = testSocket2.getPort();
TestSocketKeepaliveCallback callback2 = new TestSocketKeepaliveCallback(executor); TestSocketKeepaliveCallback callback2 = new TestSocketKeepaliveCallback(executor);
try (SocketKeepalive ka2 = mCm.createSocketKeepalive( try (SocketKeepalive ka2 = mCm.createSocketKeepalive(
myNet, testSocket2, myIPv4, dstIPv4, executor, callback2)) { myNet, testSocket2, myIPv4, dstIPv4, executor, callback2)) {
@@ -4223,6 +4224,10 @@ public class ConnectivityServiceTest {
} }
} }
// Check that there is no port leaked after all keepalives and sockets are closed.
assertFalse(isUdpPortInUse(srcPort));
assertFalse(isUdpPortInUse(srcPort2));
mWiFiNetworkAgent.disconnect(); mWiFiNetworkAgent.disconnect();
waitFor(mWiFiNetworkAgent.getDisconnectedCV()); waitFor(mWiFiNetworkAgent.getDisconnectedCV());
mWiFiNetworkAgent = null; mWiFiNetworkAgent = null;
@@ -4305,7 +4310,6 @@ public class ConnectivityServiceTest {
} }
private void doTestNattSocketKeepalivesFdWithExecutor(Executor executor) throws Exception { private void doTestNattSocketKeepalivesFdWithExecutor(Executor executor) throws Exception {
final int srcPort = 12345;
final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129"); final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
final InetAddress anyIPv4 = InetAddress.getByName("0.0.0.0"); final InetAddress anyIPv4 = InetAddress.getByName("0.0.0.0");
final InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8"); final InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8");
@@ -4324,7 +4328,8 @@ public class ConnectivityServiceTest {
// Prepare the target file descriptor, keep only one instance. // Prepare the target file descriptor, keep only one instance.
final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE); final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(srcPort); final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket();
final int srcPort = testSocket.getPort();
final ParcelFileDescriptor testPfd = final ParcelFileDescriptor testPfd =
ParcelFileDescriptor.dup(testSocket.getFileDescriptor()); ParcelFileDescriptor.dup(testSocket.getFileDescriptor());
testSocket.close(); testSocket.close();