Merge "Use Vpn rules (not firewall) for always-on VPN"

am: e4f56a7e44

Change-Id: I80b3607fd1c61875be8354439e09db56a0bd391d
This commit is contained in:
Robin Lee
2017-04-21 22:05:33 +00:00
committed by android-build-merger
5 changed files with 110 additions and 93 deletions

View File

@@ -3681,17 +3681,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
existing.shutdown();
}
try {
if (tracker != null) {
mNetd.setFirewallEnabled(true);
mNetd.setFirewallInterfaceRule("lo", true);
mLockdownTracker = tracker;
mLockdownTracker.init();
} else {
mNetd.setFirewallEnabled(false);
}
} catch (RemoteException e) {
// ignored; NMS lives inside system_server
if (tracker != null) {
mLockdownTracker = tracker;
mLockdownTracker.init();
}
}

View File

@@ -95,7 +95,6 @@ import com.android.internal.util.HexDump;
import com.android.internal.util.Preconditions;
import com.android.server.NativeDaemonConnector.Command;
import com.android.server.NativeDaemonConnector.SensitiveArg;
import com.android.server.net.LockdownVpnTracker;
import com.google.android.collect.Maps;
import java.io.BufferedReader;
@@ -628,7 +627,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
}
setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
setFirewallEnabled(mFirewallEnabled);
syncFirewallChainLocked(FIREWALL_CHAIN_NONE, mUidFirewallRules, "");
syncFirewallChainLocked(FIREWALL_CHAIN_STANDBY, mUidFirewallStandbyRules, "standby ");

View File

@@ -263,6 +263,30 @@ public class Vpn {
updateAlwaysOnNotification(detailedState);
}
/**
* Chooses whether to force all connections to go though VPN.
*
* Used to enable/disable legacy VPN lockdown.
*
* This uses the same ip rule mechanism as {@link #setAlwaysOnPackage(String, boolean)};
* previous settings from calling that function will be replaced and saved with the
* always-on state.
*
* @param lockdown whether to prevent all traffic outside of a VPN.
*/
public synchronized void setLockdown(boolean lockdown) {
enforceControlPermissionOrInternalCaller();
setVpnForcedLocked(lockdown);
mLockdown = lockdown;
// Update app lockdown setting if it changed. Legacy VPN lockdown status is controlled by
// LockdownVpnTracker.isEnabled() which keeps track of its own state.
if (mAlwaysOn) {
saveAlwaysOnPackage();
}
}
/**
* Configures an always-on VPN connection through a specific application.
* This connection is automatically granted and persisted after a reboot.
@@ -376,7 +400,7 @@ public class Vpn {
mSystemServices.settingsSecurePutStringForUser(Settings.Secure.ALWAYS_ON_VPN_APP,
getAlwaysOnPackage(), mUserHandle);
mSystemServices.settingsSecurePutIntForUser(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
(mLockdown ? 1 : 0), mUserHandle);
(mAlwaysOn && mLockdown ? 1 : 0), mUserHandle);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -557,6 +581,7 @@ public class Vpn {
mConfig = null;
updateState(DetailedState.IDLE, "prepare");
setVpnForcedLocked(mLockdown);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1003,9 +1028,7 @@ public class Vpn {
Log.wtf(TAG, "Failed to add restricted user to owner", e);
}
}
if (mAlwaysOn) {
setVpnForcedLocked(mLockdown);
}
setVpnForcedLocked(mLockdown);
}
}
}
@@ -1022,9 +1045,7 @@ public class Vpn {
Log.wtf(TAG, "Failed to remove restricted user to owner", e);
}
}
if (mAlwaysOn) {
setVpnForcedLocked(mLockdown);
}
setVpnForcedLocked(mLockdown);
}
}
}
@@ -1034,7 +1055,7 @@ public class Vpn {
*/
public synchronized void onUserStopped() {
// Switch off networking lockdown (if it was enabled)
setVpnForcedLocked(false);
setLockdown(false);
mAlwaysOn = false;
unregisterPackageChangeReceiverLocked();
@@ -1061,20 +1082,31 @@ public class Vpn {
*/
@GuardedBy("this")
private void setVpnForcedLocked(boolean enforce) {
final List<String> exemptedPackages =
isNullOrLegacyVpn(mPackage) ? null : Collections.singletonList(mPackage);
setVpnForcedWithExemptionsLocked(enforce, exemptedPackages);
}
/**
* @see #setVpnForcedLocked
*/
@GuardedBy("this")
private void setVpnForcedWithExemptionsLocked(boolean enforce,
@Nullable List<String> exemptedPackages) {
final Set<UidRange> removedRanges = new ArraySet<>(mBlockedUsers);
Set<UidRange> addedRanges = Collections.emptySet();
if (enforce) {
final Set<UidRange> addedRanges = createUserAndRestrictedProfilesRanges(mUserHandle,
addedRanges = createUserAndRestrictedProfilesRanges(mUserHandle,
/* allowedApplications */ null,
/* disallowedApplications */ Collections.singletonList(mPackage));
/* disallowedApplications */ exemptedPackages);
removedRanges.removeAll(addedRanges);
addedRanges.removeAll(mBlockedUsers);
setAllowOnlyVpnForUids(false, removedRanges);
setAllowOnlyVpnForUids(true, addedRanges);
} else {
setAllowOnlyVpnForUids(false, removedRanges);
}
setAllowOnlyVpnForUids(false, removedRanges);
setAllowOnlyVpnForUids(true, addedRanges);
}
/**

View File

@@ -139,7 +139,6 @@ public class LockdownVpnTracker {
" " + mAcceptedEgressIface + "->" + egressIface);
if (egressDisconnected || egressChanged) {
clearSourceRulesLocked();
mAcceptedEgressIface = null;
mVpn.stopLegacyVpnPrivileged();
}
@@ -191,24 +190,6 @@ public class LockdownVpnTracker {
EventLogTags.writeLockdownVpnConnected(egressType);
showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
try {
clearSourceRulesLocked();
mNetService.setFirewallInterfaceRule(iface, true);
for (LinkAddress addr : sourceAddrs) {
setFirewallEgressSourceRule(addr, true);
}
mNetService.setFirewallUidRule(FIREWALL_CHAIN_NONE, ROOT_UID, FIREWALL_RULE_ALLOW);
mNetService.setFirewallUidRule(FIREWALL_CHAIN_NONE, Os.getuid(), FIREWALL_RULE_ALLOW);
mErrorCount = 0;
mAcceptedIface = iface;
mAcceptedSourceAddr = sourceAddrs;
} catch (RemoteException e) {
throw new RuntimeException("Problem setting firewall rules", e);
}
final NetworkInfo clone = new NetworkInfo(egressInfo);
augmentNetworkInfo(clone);
mConnService.sendConnectedBroadcast(clone);
@@ -225,19 +206,11 @@ public class LockdownVpnTracker {
Slog.d(TAG, "initLocked()");
mVpn.setEnableTeardown(false);
mVpn.setLockdown(true);
final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET);
mContext.registerReceiver(mResetReceiver, resetFilter, CONNECTIVITY_INTERNAL, null);
try {
// TODO: support non-standard port numbers
mNetService.setFirewallEgressDestRule(mProfile.server, 500, true);
mNetService.setFirewallEgressDestRule(mProfile.server, 4500, true);
mNetService.setFirewallEgressDestRule(mProfile.server, 1701, true);
} catch (RemoteException e) {
throw new RuntimeException("Problem setting firewall rules", e);
}
handleStateChangedLocked();
}
@@ -254,14 +227,7 @@ public class LockdownVpnTracker {
mErrorCount = 0;
mVpn.stopLegacyVpnPrivileged();
try {
mNetService.setFirewallEgressDestRule(mProfile.server, 500, false);
mNetService.setFirewallEgressDestRule(mProfile.server, 4500, false);
mNetService.setFirewallEgressDestRule(mProfile.server, 1701, false);
} catch (RemoteException e) {
throw new RuntimeException("Problem setting firewall rules", e);
}
clearSourceRulesLocked();
mVpn.setLockdown(false);
hideNotification();
mContext.unregisterReceiver(mResetReceiver);
@@ -278,35 +244,6 @@ public class LockdownVpnTracker {
}
}
private void clearSourceRulesLocked() {
try {
if (mAcceptedIface != null) {
mNetService.setFirewallInterfaceRule(mAcceptedIface, false);
mAcceptedIface = null;
}
if (mAcceptedSourceAddr != null) {
for (LinkAddress addr : mAcceptedSourceAddr) {
setFirewallEgressSourceRule(addr, false);
}
mNetService.setFirewallUidRule(FIREWALL_CHAIN_NONE, ROOT_UID, FIREWALL_RULE_DEFAULT);
mNetService.setFirewallUidRule(FIREWALL_CHAIN_NONE,Os.getuid(), FIREWALL_RULE_DEFAULT);
mAcceptedSourceAddr = null;
}
} catch (RemoteException e) {
throw new RuntimeException("Problem setting firewall rules", e);
}
}
private void setFirewallEgressSourceRule(
LinkAddress address, boolean allow) throws RemoteException {
// Our source address based firewall rules must only cover our own source address, not the
// whole subnet
final String addrString = address.getAddress().getHostAddress();
mNetService.setFirewallEgressSourceRule(addrString, allow);
}
public void onNetworkInfoChanged() {
synchronized (mStateLock) {
handleStateChangedLocked();

View File

@@ -27,6 +27,7 @@ import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -42,6 +43,8 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.net.VpnConfig;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
@@ -101,8 +104,10 @@ public class VpnTest extends AndroidTestCase {
@Override
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
setMockedPackages(mPackages);
when(mContext.getPackageName()).thenReturn(Vpn.class.getPackage().getName());
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
@@ -257,6 +262,58 @@ public class VpnTest extends AndroidTestCase {
}));
}
@SmallTest
public void testLockdownRuleRepeatability() throws Exception {
final Vpn vpn = createVpn(primaryUser.id);
// Given legacy lockdown is already enabled,
vpn.setLockdown(true);
verify(mNetService, times(1)).setAllowOnlyVpnForUids(
eq(true), aryEq(new UidRange[] {UidRange.createForUser(primaryUser.id)}));
// Enabling legacy lockdown twice should do nothing.
vpn.setLockdown(true);
verify(mNetService, times(1)).setAllowOnlyVpnForUids(anyBoolean(), any(UidRange[].class));
// And disabling should remove the rules exactly once.
vpn.setLockdown(false);
verify(mNetService, times(1)).setAllowOnlyVpnForUids(
eq(false), aryEq(new UidRange[] {UidRange.createForUser(primaryUser.id)}));
// Removing the lockdown again should have no effect.
vpn.setLockdown(false);
verify(mNetService, times(2)).setAllowOnlyVpnForUids(anyBoolean(), any(UidRange[].class));
}
@SmallTest
public void testLockdownRuleReversibility() throws Exception {
final Vpn vpn = createVpn(primaryUser.id);
final UidRange[] entireUser = {
UidRange.createForUser(primaryUser.id)
};
final UidRange[] exceptPkg0 = {
new UidRange(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1),
new UidRange(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop)
};
final InOrder order = inOrder(mNetService);
// Given lockdown is enabled with no package (legacy VPN),
vpn.setLockdown(true);
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
// When a new VPN package is set the rules should change to cover that package.
vpn.prepare(null, PKGS[0]);
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
// When that VPN package is unset, everything should be undone again in reverse.
vpn.prepare(null, VpnConfig.LEGACY_VPN);
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
}
@SmallTest
public void testNotificationShownForAlwaysOnApp() {
final UserHandle userHandle = UserHandle.of(primaryUser.id);