Keystore 2.0: Make Legacy VPN settings ready for Keystore 2.0

Keystore 2.0 no longer stores vpn profiles. It still offers a
Legacy VPN profile store, to access existing profiles.

Test: N/A
Bug: 171305607
Bug: 171305388
Merged-In: I40dea0b9c3824b56814ae4c2fb6c7663c7d97af5
Change-Id: I40dea0b9c3824b56814ae4c2fb6c7663c7d97af5
This commit is contained in:
Janis Danisevskis
2021-01-25 14:59:38 -08:00
parent e92bcb3287
commit 146a0dab01
5 changed files with 44 additions and 39 deletions

View File

@@ -28,6 +28,7 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.UnrecoverableKeyException; import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Enumeration; import java.util.Enumeration;
@@ -88,6 +89,10 @@ public class AndroidKeystoreAliasLoader {
if (key != null) { if (key != null) {
if (key instanceof PrivateKey) { if (key instanceof PrivateKey) {
mKeyCertAliases.add(alias); mKeyCertAliases.add(alias);
final Certificate[] cert = keyStore.getCertificateChain(alias);
if (cert != null && cert.length >= 2) {
mCaCertAliases.add(alias);
}
} }
} else { } else {
if (keyStore.getCertificate(alias) != null) { if (keyStore.getCertificate(alias) != null) {

View File

@@ -24,8 +24,6 @@ import android.content.pm.PackageManager;
import android.net.ProxyInfo; import android.net.ProxyInfo;
import android.os.Bundle; import android.os.Bundle;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.security.Credentials;
import android.security.KeyStore;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.View; import android.view.View;
@@ -42,10 +40,12 @@ import androidx.appcompat.app.AlertDialog;
import com.android.internal.net.VpnProfile; import com.android.internal.net.VpnProfile;
import com.android.net.module.util.ProxyUtils; import com.android.net.module.util.ProxyUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.utils.AndroidKeystoreAliasLoader;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
/** /**
@@ -58,7 +58,7 @@ import java.util.List;
class ConfigDialog extends AlertDialog implements TextWatcher, class ConfigDialog extends AlertDialog implements TextWatcher,
View.OnClickListener, AdapterView.OnItemSelectedListener, View.OnClickListener, AdapterView.OnItemSelectedListener,
CompoundButton.OnCheckedChangeListener { CompoundButton.OnCheckedChangeListener {
private final KeyStore mKeyStore = KeyStore.getInstance(); private static final String TAG = "ConfigDialog";
private final DialogInterface.OnClickListener mListener; private final DialogInterface.OnClickListener mListener;
private final VpnProfile mProfile; private final VpnProfile mProfile;
@@ -153,10 +153,13 @@ class ConfigDialog extends AlertDialog implements TextWatcher,
mL2tpSecret.setTextAppearance(android.R.style.TextAppearance_DeviceDefault_Medium); mL2tpSecret.setTextAppearance(android.R.style.TextAppearance_DeviceDefault_Medium);
mIpsecIdentifier.setText(mProfile.ipsecIdentifier); mIpsecIdentifier.setText(mProfile.ipsecIdentifier);
mIpsecSecret.setText(mProfile.ipsecSecret); mIpsecSecret.setText(mProfile.ipsecSecret);
loadCertificates(mIpsecUserCert, Credentials.USER_PRIVATE_KEY, 0, mProfile.ipsecUserCert); AndroidKeystoreAliasLoader androidKeystoreAliasLoader =
loadCertificates(mIpsecCaCert, Credentials.CA_CERTIFICATE, new AndroidKeystoreAliasLoader(null);
loadCertificates(mIpsecUserCert, androidKeystoreAliasLoader.getKeyCertAliases(), 0,
mProfile.ipsecUserCert);
loadCertificates(mIpsecCaCert, androidKeystoreAliasLoader.getCaCertAliases(),
R.string.vpn_no_ca_cert, mProfile.ipsecCaCert); R.string.vpn_no_ca_cert, mProfile.ipsecCaCert);
loadCertificates(mIpsecServerCert, Credentials.USER_CERTIFICATE, loadCertificates(mIpsecServerCert, androidKeystoreAliasLoader.getKeyCertAliases(),
R.string.vpn_no_server_cert, mProfile.ipsecServerCert); R.string.vpn_no_server_cert, mProfile.ipsecServerCert);
mSaveLogin.setChecked(mProfile.saveLogin); mSaveLogin.setChecked(mProfile.saveLogin);
mAlwaysOnVpn.setChecked(mProfile.key.equals(VpnUtils.getLockdownVpn())); mAlwaysOnVpn.setChecked(mProfile.key.equals(VpnUtils.getLockdownVpn()));
@@ -511,27 +514,30 @@ class ConfigDialog extends AlertDialog implements TextWatcher,
typeSpinner.setAdapter(adapter); typeSpinner.setAdapter(adapter);
} }
private void loadCertificates(Spinner spinner, String prefix, int firstId, String selected) { private void loadCertificates(Spinner spinner, Collection<String> choices, int firstId,
String selected) {
Context context = getContext(); Context context = getContext();
String first = (firstId == 0) ? "" : context.getString(firstId); String first = (firstId == 0) ? "" : context.getString(firstId);
String[] certificates = mKeyStore.list(prefix); String[] myChoices;
if (certificates == null || certificates.length == 0) { if (choices == null || choices.size() == 0) {
certificates = new String[] {first}; myChoices = new String[] {first};
} else { } else {
String[] array = new String[certificates.length + 1]; myChoices = new String[choices.size() + 1];
array[0] = first; myChoices[0] = first;
System.arraycopy(certificates, 0, array, 1, certificates.length); int i = 1;
certificates = array; for (String c : choices) {
myChoices[i++] = c;
}
} }
ArrayAdapter<String> adapter = new ArrayAdapter<String>( ArrayAdapter<String> adapter = new ArrayAdapter<String>(
context, android.R.layout.simple_spinner_item, certificates); context, android.R.layout.simple_spinner_item, myChoices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter); spinner.setAdapter(adapter);
for (int i = 1; i < certificates.length; ++i) { for (int i = 1; i < myChoices.length; ++i) {
if (certificates[i].equals(selected)) { if (myChoices[i].equals(selected)) {
spinner.setSelection(i); spinner.setSelection(i);
break; break;
} }

View File

@@ -25,7 +25,7 @@ import android.os.Bundle;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserHandle; import android.os.UserHandle;
import android.security.Credentials; import android.security.Credentials;
import android.security.KeyStore; import android.security.LegacyVpnProfileStore;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.Toast; import android.widget.Toast;
@@ -151,9 +151,8 @@ public class ConfigDialogFragment extends InstrumentedDialogFragment implements
return; return;
} }
// Delete from KeyStore // Delete from profile store.
KeyStore keyStore = KeyStore.getInstance(); LegacyVpnProfileStore.remove(Credentials.VPN + profile.key);
keyStore.delete(Credentials.VPN + profile.key, KeyStore.UID_SELF);
updateLockdownVpn(false, profile); updateLockdownVpn(false, profile);
} }
@@ -188,8 +187,7 @@ public class ConfigDialogFragment extends InstrumentedDialogFragment implements
} }
private void save(VpnProfile profile, boolean lockdown) { private void save(VpnProfile profile, boolean lockdown) {
KeyStore.getInstance().put(Credentials.VPN + profile.key, profile.encode(), LegacyVpnProfileStore.put(Credentials.VPN + profile.key, profile.encode());
KeyStore.UID_SELF, /* flags */ 0);
// Flush out old version of profile // Flush out old version of profile
disconnect(profile); disconnect(profile);

View File

@@ -41,7 +41,7 @@ import android.os.Message;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.security.Credentials; import android.security.Credentials;
import android.security.KeyStore; import android.security.LegacyVpnProfileStore;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log; import android.util.Log;
@@ -57,7 +57,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile; import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.RestrictedSettingsFragment; import com.android.settings.RestrictedSettingsFragment;
import com.android.settings.widget.GearPreference; import com.android.settings.widget.GearPreference;
@@ -94,8 +93,6 @@ public class VpnSettings extends RestrictedSettingsFragment implements
private UserManager mUserManager; private UserManager mUserManager;
private VpnManager mVpnManager; private VpnManager mVpnManager;
private final KeyStore mKeyStore = KeyStore.getInstance();
private Map<String, LegacyVpnPreference> mLegacyVpnPreferences = new ArrayMap<>(); private Map<String, LegacyVpnPreference> mLegacyVpnPreferences = new ArrayMap<>();
private Map<AppVpnInfo, AppPreference> mAppPreferences = new ArrayMap<>(); private Map<AppVpnInfo, AppPreference> mAppPreferences = new ArrayMap<>();
@@ -222,7 +219,7 @@ public class VpnSettings extends RestrictedSettingsFragment implements
final Context context = activity.getApplicationContext(); final Context context = activity.getApplicationContext();
// Run heavy RPCs before switching to UI thread // Run heavy RPCs before switching to UI thread
final List<VpnProfile> vpnProfiles = loadVpnProfiles(mKeyStore); final List<VpnProfile> vpnProfiles = loadVpnProfiles();
final List<AppVpnInfo> vpnApps = getVpnApps(context, /* includeProfiles */ true); final List<AppVpnInfo> vpnApps = getVpnApps(context, /* includeProfiles */ true);
final Map<String, LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns(); final Map<String, LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns();
@@ -540,12 +537,13 @@ public class VpnSettings extends RestrictedSettingsFragment implements
return result; return result;
} }
static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) { private static List<VpnProfile> loadVpnProfiles() {
final ArrayList<VpnProfile> result = Lists.newArrayList(); final ArrayList<VpnProfile> result = Lists.newArrayList();
for (String key : keyStore.list(Credentials.VPN)) { for (String key : LegacyVpnProfileStore.list(Credentials.VPN)) {
final VpnProfile profile = VpnProfile.decode(key, keyStore.get(Credentials.VPN + key)); final VpnProfile profile = VpnProfile.decode(key,
if (profile != null && !ArrayUtils.contains(excludeTypes, profile.type)) { LegacyVpnProfileStore.get(Credentials.VPN + key));
if (profile != null) {
result.add(profile); result.add(profile);
} }
} }

View File

@@ -20,7 +20,7 @@ import android.net.VpnManager;
import android.os.RemoteException; import android.os.RemoteException;
import android.provider.Settings; import android.provider.Settings;
import android.security.Credentials; import android.security.Credentials;
import android.security.KeyStore; import android.security.LegacyVpnProfileStore;
import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnConfig;
@@ -28,27 +28,25 @@ import com.android.internal.net.VpnConfig;
/** /**
* Utility functions for vpn. * Utility functions for vpn.
* *
* Keystore methods should only be called in system user * LegacyVpnProfileStore methods should only be called in system user
*/ */
public class VpnUtils { public class VpnUtils {
private static final String TAG = "VpnUtils"; private static final String TAG = "VpnUtils";
public static String getLockdownVpn() { public static String getLockdownVpn() {
final byte[] value = KeyStore.getInstance().get( final byte[] value = LegacyVpnProfileStore.get(Credentials.LOCKDOWN_VPN);
Credentials.LOCKDOWN_VPN, true /* suppressKeyNotFoundWarning */);
return value == null ? null : new String(value); return value == null ? null : new String(value);
} }
public static void clearLockdownVpn(Context context) { public static void clearLockdownVpn(Context context) {
KeyStore.getInstance().delete(Credentials.LOCKDOWN_VPN); LegacyVpnProfileStore.remove(Credentials.LOCKDOWN_VPN);
// Always notify VpnManager after keystore update // Always notify VpnManager after keystore update
getVpnManager(context).updateLockdownVpn(); getVpnManager(context).updateLockdownVpn();
} }
public static void setLockdownVpn(Context context, String lockdownKey) { public static void setLockdownVpn(Context context, String lockdownKey) {
KeyStore.getInstance().put(Credentials.LOCKDOWN_VPN, lockdownKey.getBytes(), LegacyVpnProfileStore.put(Credentials.LOCKDOWN_VPN, lockdownKey.getBytes());
KeyStore.UID_SELF, /* flags */ 0);
// Always notify VpnManager after keystore update // Always notify VpnManager after keystore update
getVpnManager(context).updateLockdownVpn(); getVpnManager(context).updateLockdownVpn();
} }