From 2b16cd3920f95b2cd5c2426d81b5e1584ffc682d Mon Sep 17 00:00:00 2001 From: Samuel Tan Date: Tue, 2 Feb 2016 15:54:37 -0800 Subject: [PATCH] Allow trusted system certificates to be used in EAP network configs Add an option to the "CA certificate" field of the EAP network configuration menu, "Use system certificates". Choosing this option will cause the trusted, pre-installed, system CA certificates to be used to validate EAP servers during the authentication process. This only applies to EAP-TLS, EAP-TTLS, and EAP-PEAP network configurations, where the CA certificate option is available. If the user selects "Use system certificates" and leaves the "Domain" field empty, display a warning and prevent the EAP network configuration from being saved. Such a configuration would be insecure--the user should constrain the domain that the system certificates can be used to validate. BUG: 26879191 TEST: 1) Set up AP connected to test RADIUS server. TEST: 2) Generate a self-signed cert (Cert 1) TEST: 3) Use Cert 1 to sign another cert (Cert 2) with common name "sub1.sub2.domain.com" TEST: 4) Setup RADIUS server, and configure it to present Cert 2 to EAP peer. TEST: 5) Build angler image with Cert 1 installed in /system/etc/security/cacerts/ TEST: 6) Set up an AP connected to the RADIUS server to broadcast a WPA-Enterprise network. TEST: 7) On Angler, connect to this WPA-Enterprise network with settings: Network name: (AP SSID) Security: 802.1x EAP EAP method: TLS CA certificate: Use system certificates Domain: domain.com User certificate: (test certificate from RADIUS setup) Identity: (identity used for RADIUS setup) TEST: 8) Verify that we connect successfully to the AP. TEST: 9) Verify that connection still succeeds if Domain is set to "sub2.domain.com" and "sub1.sub2.domain.com". TEST: 10) Verify that connection fails if Domain is set to "sub0.sub1.domain.com" and "otherdomain.com". TEST: 11) Verify that network configuration cannot be saved, and an warning message "Must specify a domain" is displayed if Domain is left blank in the configuration in step 7 TEST: 12) Verify that the "Do not validate" option still appears in the CA certificate dropdown menu. Change-Id: I346d4d301305719033b84ec4599bf3d57d9d4ee5 --- res/layout/wifi_dialog.xml | 12 ++ res/values/strings.xml | 5 + .../settings/wifi/WifiConfigController.java | 123 +++++++++++++----- 3 files changed, 105 insertions(+), 35 deletions(-) diff --git a/res/layout/wifi_dialog.xml b/res/layout/wifi_dialog.xml index 5233e5e82fd..77ad3f87905 100644 --- a/res/layout/wifi_dialog.xml +++ b/res/layout/wifi_dialog.xml @@ -175,6 +175,18 @@ android:inputType="textNoSuggestions" /> + + + + Please select (Multiple certificates added) + + Use system certificates Do not provide Do not validate No certificate specified. Your connection will not be private. + + Must specify a domain. WPS available diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java index 4145a083440..285b31605bd 100644 --- a/src/com/android/settings/wifi/WifiConfigController.java +++ b/src/com/android/settings/wifi/WifiConfigController.java @@ -73,6 +73,8 @@ public class WifiConfigController implements TextWatcher, AdapterView.OnItemSelectedListener, OnCheckedChangeListener { private static final String TAG = "WifiConfigController"; + private static final String SYSTEM_CA_STORE_PATH = "/system/etc/security/cacerts"; + private final WifiConfigUiBase mConfigUi; private final View mView; private final AccessPoint mAccessPoint; @@ -113,10 +115,7 @@ public class WifiConfigController private String mUnspecifiedCertString; private String mMultipleCertSetString; - private static final int UNSPECIFIED_CERT_INDEX = 0; - private static final int NO_CERT_INDEX = 1; - private static final int MULTIPLE_CERT_SET_INDEX = 2; - + private String mUseSystemCertsString; private String mDoNotProvideEapUserCertString; private String mDoNotValidateEapServerString; @@ -185,6 +184,7 @@ public class WifiConfigController mUnspecifiedCertString = mContext.getString(R.string.wifi_unspecified); mMultipleCertSetString = mContext.getString(R.string.wifi_multiple_cert_added); + mUseSystemCertsString = mContext.getString(R.string.wifi_use_system_certs); mDoNotProvideEapUserCertString = mContext.getString(R.string.wifi_do_not_provide_eap_user_cert); mDoNotValidateEapServerString = @@ -381,28 +381,54 @@ public class WifiConfigController } } if (mEapCaCertSpinner != null - && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE - && ((String) mEapCaCertSpinner.getSelectedItem()).equals(mUnspecifiedCertString)) { - enabled = false; + && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) { + String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem(); + if (caCertSelection.equals(mUnspecifiedCertString)) { + // Disallow submit if the user has not selected a CA certificate for an EAP network + // configuration. + enabled = false; + } + if (caCertSelection.equals(mUseSystemCertsString) + && mEapDomainView != null + && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE + && TextUtils.isEmpty(mEapDomainView.getText().toString())) { + // Disallow submit if the user chooses to use system certificates for EAP server + // validation, but does not provide a domain. + enabled = false; + } } if (mEapUserCertSpinner != null && mView.findViewById(R.id.l_user_cert).getVisibility() != View.GONE && ((String) mEapUserCertSpinner.getSelectedItem()) .equals(mUnspecifiedCertString)) { + // Disallow submit if the user has not selected a user certificate for an EAP network + // configuration. enabled = false; } submit.setEnabled(enabled); } - void showWarningMessageIfAppropriate() { + void showWarningMessagesIfAppropriate() { mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.GONE); + mView.findViewById(R.id.no_domain_warning).setVisibility(View.GONE); + if (mEapCaCertSpinner != null - && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE - && ((String) mEapCaCertSpinner.getSelectedItem()) - .equals(mDoNotValidateEapServerString)) { - // Display warning if user chooses not to validate the EAP server with a user-supplied - // CA certificate in an EAP network configuration. - mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.VISIBLE); + && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) { + String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem(); + if (caCertSelection.equals(mDoNotValidateEapServerString)) { + // Display warning if user chooses not to validate the EAP server with a + // user-supplied CA certificate in an EAP network configuration. + mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.VISIBLE); + } + if (caCertSelection.equals(mUseSystemCertsString) + && mEapDomainView != null + && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE + && TextUtils.isEmpty(mEapDomainView.getText().toString())) { + // Display warning if user chooses to use pre-installed public CA certificates + // without restricting the server domain that these certificates can be used to + // validate. + mView.findViewById(R.id.no_domain_warning).setVisibility(View.VISIBLE); + } } } @@ -500,7 +526,9 @@ public class WifiConfigController } else { config.enterpriseConfig.setDomainSuffixMatch( mEapDomainView.getText().toString()); - if (caCert.equals(mMultipleCertSetString)) { + if (caCert.equals(mUseSystemCertsString)) { + config.enterpriseConfig.setCaPath(SYSTEM_CA_STORE_PATH); + } else if (caCert.equals(mMultipleCertSetString)) { if (mAccessPoint != null) { if (!mAccessPoint.isSaved()) { Log.e(TAG, "Multiple certs can only be set " @@ -725,15 +753,24 @@ public class WifiConfigController mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert); mEapCaCertSpinner.setOnItemSelectedListener(this); mEapDomainView = (TextView) mView.findViewById(R.id.domain); + mEapDomainView.addTextChangedListener(this); mEapUserCertSpinner = (Spinner) mView.findViewById(R.id.user_cert); mEapUserCertSpinner.setOnItemSelectedListener(this); mEapIdentityView = (TextView) mView.findViewById(R.id.identity); mEapAnonymousView = (TextView) mView.findViewById(R.id.anonymous); - loadCertificates(mEapCaCertSpinner, Credentials.CA_CERTIFICATE, false, - mDoNotValidateEapServerString); - loadCertificates(mEapUserCertSpinner, Credentials.USER_PRIVATE_KEY, false, - mDoNotProvideEapUserCertString); + loadCertificates( + mEapCaCertSpinner, + Credentials.CA_CERTIFICATE, + mDoNotValidateEapServerString, + false, + true); + loadCertificates( + mEapUserCertSpinner, + Credentials.USER_PRIVATE_KEY, + mDoNotProvideEapUserCertString, + false, + false); // Modifying an existing network if (mAccessPoint != null && mAccessPoint.isSaved()) { @@ -763,16 +800,24 @@ public class WifiConfigController mPhase2Spinner.setSelection(phase2Method); break; } - String[] caCerts = enterpriseConfig.getCaCertificateAliases(); - if (caCerts == null) { - setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString); - } else if (caCerts.length == 1) { - setSelection(mEapCaCertSpinner, caCerts[0]); + if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) { + setSelection(mEapCaCertSpinner, mUseSystemCertsString); } else { - // Reload the cert spinner with an extra "multiple certificates added" item - loadCertificates(mEapCaCertSpinner, - Credentials.CA_CERTIFICATE, true, mDoNotValidateEapServerString); - mEapCaCertSpinner.setSelection(MULTIPLE_CERT_SET_INDEX); + String[] caCerts = enterpriseConfig.getCaCertificateAliases(); + if (caCerts == null) { + setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString); + } else if (caCerts.length == 1) { + setSelection(mEapCaCertSpinner, caCerts[0]); + } else { + // Reload the cert spinner with an extra "multiple certificates added" item. + loadCertificates( + mEapCaCertSpinner, + Credentials.CA_CERTIFICATE, + mDoNotValidateEapServerString, + true, + true); + setSelection(mEapCaCertSpinner, mMultipleCertSetString); + } } mEapDomainView.setText(enterpriseConfig.getDomainSuffixMatch()); String userCert = enterpriseConfig.getClientCertificateAlias(); @@ -896,7 +941,7 @@ public class WifiConfigController private void setCaCertInvisible() { mView.findViewById(R.id.l_ca_cert).setVisibility(View.GONE); - mEapCaCertSpinner.setSelection(UNSPECIFIED_CERT_INDEX); + setSelection(mEapCaCertSpinner, mUnspecifiedCertString); } private void setDomainInvisible() { @@ -906,7 +951,7 @@ public class WifiConfigController private void setUserCertInvisible() { mView.findViewById(R.id.l_user_cert).setVisibility(View.GONE); - mEapUserCertSpinner.setSelection(UNSPECIFIED_CERT_INDEX); + setSelection(mEapUserCertSpinner, mUnspecifiedCertString); } private void setAnonymousIdentInvisible() { @@ -1031,17 +1076,24 @@ public class WifiConfigController } private void loadCertificates( - Spinner spinner, String prefix, boolean showMultipleCerts, String noCertificateString) { + Spinner spinner, + String prefix, + String noCertificateString, + boolean showMultipleCerts, + boolean showUsePreinstalledCertOption) { final Context context = mConfigUi.getContext(); ArrayList certs = new ArrayList(); - certs.add(UNSPECIFIED_CERT_INDEX, mUnspecifiedCertString); - certs.add(NO_CERT_INDEX, noCertificateString); + certs.add(mUnspecifiedCertString); if (showMultipleCerts) { - certs.add(MULTIPLE_CERT_SET_INDEX, mMultipleCertSetString); + certs.add(mMultipleCertSetString); + } + if (showUsePreinstalledCertOption) { + certs.add(mUseSystemCertsString); } certs.addAll( Arrays.asList(KeyStore.getInstance().list(prefix, android.os.Process.WIFI_UID))); + certs.add(noCertificateString); final ArrayAdapter adapter = new ArrayAdapter( context, android.R.layout.simple_spinner_item, @@ -1075,6 +1127,7 @@ public class WifiConfigController public void afterTextChanged(Editable s) { mTextViewChangedHandler.post(new Runnable() { public void run() { + showWarningMessagesIfAppropriate(); enableSubmitIfAppropriate(); } }); @@ -1121,7 +1174,7 @@ public class WifiConfigController } else { showIpConfigFields(); } - showWarningMessageIfAppropriate(); + showWarningMessagesIfAppropriate(); enableSubmitIfAppropriate(); }