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
This commit is contained in:
Samuel Tan
2016-02-02 15:54:37 -08:00
parent 3d8cdb10f6
commit 2b16cd3920
3 changed files with 105 additions and 35 deletions

View File

@@ -175,6 +175,18 @@
android:inputType="textNoSuggestions" /> android:inputType="textNoSuggestions" />
</LinearLayout> </LinearLayout>
<LinearLayout android:id="@+id/no_domain_warning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
style="@style/wifi_item" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/wifi_item_warning"
android:text="@string/wifi_no_domain_warning" />
</LinearLayout>
<LinearLayout android:id="@+id/l_user_cert" <LinearLayout android:id="@+id/l_user_cert"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@@ -1586,12 +1586,17 @@
<string name="wifi_unspecified">Please select</string> <string name="wifi_unspecified">Please select</string>
<!-- Hint for multiple certificates being added to the configuration --> <!-- Hint for multiple certificates being added to the configuration -->
<string name="wifi_multiple_cert_added">(Multiple certificates added)</string> <string name="wifi_multiple_cert_added">(Multiple certificates added)</string>
<!-- Menu option for using trusted system CA certificates to validate EAP servers -->
<string name="wifi_use_system_certs">Use system certificates</string>
<!-- Menu option for not providing an EAP user certificate --> <!-- Menu option for not providing an EAP user certificate -->
<string name="wifi_do_not_provide_eap_user_cert">Do not provide</string> <string name="wifi_do_not_provide_eap_user_cert">Do not provide</string>
<!-- Menu option for not validating the EAP server --> <!-- Menu option for not validating the EAP server -->
<string name="wifi_do_not_validate_eap_server">Do not validate</string> <string name="wifi_do_not_validate_eap_server">Do not validate</string>
<!-- Warning message displayed if user choses not to validate the EAP server --> <!-- Warning message displayed if user choses not to validate the EAP server -->
<string name="wifi_do_not_validate_eap_server_warning">No certificate specified. Your connection will not be private.</string> <string name="wifi_do_not_validate_eap_server_warning">No certificate specified. Your connection will not be private.</string>
<!-- Warning message displayed if user does not specify a domain for the CA certificate.
Only displayed if the user also chooses to use system certificates. -->
<string name="wifi_no_domain_warning">Must specify a domain.</string>
<!-- Substring of status line when Wi-Fi Protected Setup (WPS) is available and <!-- Substring of status line when Wi-Fi Protected Setup (WPS) is available and
string is listed first [CHAR LIMIT=20]--> string is listed first [CHAR LIMIT=20]-->
<string name="wifi_wps_available_first_item">WPS available</string> <string name="wifi_wps_available_first_item">WPS available</string>

View File

@@ -73,6 +73,8 @@ public class WifiConfigController
implements TextWatcher, AdapterView.OnItemSelectedListener, OnCheckedChangeListener { implements TextWatcher, AdapterView.OnItemSelectedListener, OnCheckedChangeListener {
private static final String TAG = "WifiConfigController"; private static final String TAG = "WifiConfigController";
private static final String SYSTEM_CA_STORE_PATH = "/system/etc/security/cacerts";
private final WifiConfigUiBase mConfigUi; private final WifiConfigUiBase mConfigUi;
private final View mView; private final View mView;
private final AccessPoint mAccessPoint; private final AccessPoint mAccessPoint;
@@ -113,10 +115,7 @@ public class WifiConfigController
private String mUnspecifiedCertString; private String mUnspecifiedCertString;
private String mMultipleCertSetString; private String mMultipleCertSetString;
private static final int UNSPECIFIED_CERT_INDEX = 0; private String mUseSystemCertsString;
private static final int NO_CERT_INDEX = 1;
private static final int MULTIPLE_CERT_SET_INDEX = 2;
private String mDoNotProvideEapUserCertString; private String mDoNotProvideEapUserCertString;
private String mDoNotValidateEapServerString; private String mDoNotValidateEapServerString;
@@ -185,6 +184,7 @@ public class WifiConfigController
mUnspecifiedCertString = mContext.getString(R.string.wifi_unspecified); mUnspecifiedCertString = mContext.getString(R.string.wifi_unspecified);
mMultipleCertSetString = mContext.getString(R.string.wifi_multiple_cert_added); mMultipleCertSetString = mContext.getString(R.string.wifi_multiple_cert_added);
mUseSystemCertsString = mContext.getString(R.string.wifi_use_system_certs);
mDoNotProvideEapUserCertString = mDoNotProvideEapUserCertString =
mContext.getString(R.string.wifi_do_not_provide_eap_user_cert); mContext.getString(R.string.wifi_do_not_provide_eap_user_cert);
mDoNotValidateEapServerString = mDoNotValidateEapServerString =
@@ -381,28 +381,54 @@ public class WifiConfigController
} }
} }
if (mEapCaCertSpinner != null if (mEapCaCertSpinner != null
&& mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
&& ((String) mEapCaCertSpinner.getSelectedItem()).equals(mUnspecifiedCertString)) { String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
enabled = false; 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 if (mEapUserCertSpinner != null
&& mView.findViewById(R.id.l_user_cert).getVisibility() != View.GONE && mView.findViewById(R.id.l_user_cert).getVisibility() != View.GONE
&& ((String) mEapUserCertSpinner.getSelectedItem()) && ((String) mEapUserCertSpinner.getSelectedItem())
.equals(mUnspecifiedCertString)) { .equals(mUnspecifiedCertString)) {
// Disallow submit if the user has not selected a user certificate for an EAP network
// configuration.
enabled = false; enabled = false;
} }
submit.setEnabled(enabled); submit.setEnabled(enabled);
} }
void showWarningMessageIfAppropriate() { void showWarningMessagesIfAppropriate() {
mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.GONE); mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.GONE);
mView.findViewById(R.id.no_domain_warning).setVisibility(View.GONE);
if (mEapCaCertSpinner != null if (mEapCaCertSpinner != null
&& mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
&& ((String) mEapCaCertSpinner.getSelectedItem()) String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
.equals(mDoNotValidateEapServerString)) { if (caCertSelection.equals(mDoNotValidateEapServerString)) {
// Display warning if user chooses not to validate the EAP server with a user-supplied // Display warning if user chooses not to validate the EAP server with a
// CA certificate in an EAP network configuration. // user-supplied CA certificate in an EAP network configuration.
mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.VISIBLE); 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 { } else {
config.enterpriseConfig.setDomainSuffixMatch( config.enterpriseConfig.setDomainSuffixMatch(
mEapDomainView.getText().toString()); 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 != null) {
if (!mAccessPoint.isSaved()) { if (!mAccessPoint.isSaved()) {
Log.e(TAG, "Multiple certs can only be set " 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 = (Spinner) mView.findViewById(R.id.ca_cert);
mEapCaCertSpinner.setOnItemSelectedListener(this); mEapCaCertSpinner.setOnItemSelectedListener(this);
mEapDomainView = (TextView) mView.findViewById(R.id.domain); mEapDomainView = (TextView) mView.findViewById(R.id.domain);
mEapDomainView.addTextChangedListener(this);
mEapUserCertSpinner = (Spinner) mView.findViewById(R.id.user_cert); mEapUserCertSpinner = (Spinner) mView.findViewById(R.id.user_cert);
mEapUserCertSpinner.setOnItemSelectedListener(this); mEapUserCertSpinner.setOnItemSelectedListener(this);
mEapIdentityView = (TextView) mView.findViewById(R.id.identity); mEapIdentityView = (TextView) mView.findViewById(R.id.identity);
mEapAnonymousView = (TextView) mView.findViewById(R.id.anonymous); mEapAnonymousView = (TextView) mView.findViewById(R.id.anonymous);
loadCertificates(mEapCaCertSpinner, Credentials.CA_CERTIFICATE, false, loadCertificates(
mDoNotValidateEapServerString); mEapCaCertSpinner,
loadCertificates(mEapUserCertSpinner, Credentials.USER_PRIVATE_KEY, false, Credentials.CA_CERTIFICATE,
mDoNotProvideEapUserCertString); mDoNotValidateEapServerString,
false,
true);
loadCertificates(
mEapUserCertSpinner,
Credentials.USER_PRIVATE_KEY,
mDoNotProvideEapUserCertString,
false,
false);
// Modifying an existing network // Modifying an existing network
if (mAccessPoint != null && mAccessPoint.isSaved()) { if (mAccessPoint != null && mAccessPoint.isSaved()) {
@@ -763,16 +800,24 @@ public class WifiConfigController
mPhase2Spinner.setSelection(phase2Method); mPhase2Spinner.setSelection(phase2Method);
break; break;
} }
String[] caCerts = enterpriseConfig.getCaCertificateAliases(); if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) {
if (caCerts == null) { setSelection(mEapCaCertSpinner, mUseSystemCertsString);
setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString);
} else if (caCerts.length == 1) {
setSelection(mEapCaCertSpinner, caCerts[0]);
} else { } else {
// Reload the cert spinner with an extra "multiple certificates added" item String[] caCerts = enterpriseConfig.getCaCertificateAliases();
loadCertificates(mEapCaCertSpinner, if (caCerts == null) {
Credentials.CA_CERTIFICATE, true, mDoNotValidateEapServerString); setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString);
mEapCaCertSpinner.setSelection(MULTIPLE_CERT_SET_INDEX); } 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()); mEapDomainView.setText(enterpriseConfig.getDomainSuffixMatch());
String userCert = enterpriseConfig.getClientCertificateAlias(); String userCert = enterpriseConfig.getClientCertificateAlias();
@@ -896,7 +941,7 @@ public class WifiConfigController
private void setCaCertInvisible() { private void setCaCertInvisible() {
mView.findViewById(R.id.l_ca_cert).setVisibility(View.GONE); mView.findViewById(R.id.l_ca_cert).setVisibility(View.GONE);
mEapCaCertSpinner.setSelection(UNSPECIFIED_CERT_INDEX); setSelection(mEapCaCertSpinner, mUnspecifiedCertString);
} }
private void setDomainInvisible() { private void setDomainInvisible() {
@@ -906,7 +951,7 @@ public class WifiConfigController
private void setUserCertInvisible() { private void setUserCertInvisible() {
mView.findViewById(R.id.l_user_cert).setVisibility(View.GONE); mView.findViewById(R.id.l_user_cert).setVisibility(View.GONE);
mEapUserCertSpinner.setSelection(UNSPECIFIED_CERT_INDEX); setSelection(mEapUserCertSpinner, mUnspecifiedCertString);
} }
private void setAnonymousIdentInvisible() { private void setAnonymousIdentInvisible() {
@@ -1031,17 +1076,24 @@ public class WifiConfigController
} }
private void loadCertificates( 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(); final Context context = mConfigUi.getContext();
ArrayList<String> certs = new ArrayList<String>(); ArrayList<String> certs = new ArrayList<String>();
certs.add(UNSPECIFIED_CERT_INDEX, mUnspecifiedCertString); certs.add(mUnspecifiedCertString);
certs.add(NO_CERT_INDEX, noCertificateString);
if (showMultipleCerts) { if (showMultipleCerts) {
certs.add(MULTIPLE_CERT_SET_INDEX, mMultipleCertSetString); certs.add(mMultipleCertSetString);
}
if (showUsePreinstalledCertOption) {
certs.add(mUseSystemCertsString);
} }
certs.addAll( certs.addAll(
Arrays.asList(KeyStore.getInstance().list(prefix, android.os.Process.WIFI_UID))); Arrays.asList(KeyStore.getInstance().list(prefix, android.os.Process.WIFI_UID)));
certs.add(noCertificateString);
final ArrayAdapter<String> adapter = new ArrayAdapter<String>( final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
context, android.R.layout.simple_spinner_item, context, android.R.layout.simple_spinner_item,
@@ -1075,6 +1127,7 @@ public class WifiConfigController
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
mTextViewChangedHandler.post(new Runnable() { mTextViewChangedHandler.post(new Runnable() {
public void run() { public void run() {
showWarningMessagesIfAppropriate();
enableSubmitIfAppropriate(); enableSubmitIfAppropriate();
} }
}); });
@@ -1121,7 +1174,7 @@ public class WifiConfigController
} else { } else {
showIpConfigFields(); showIpConfigFields();
} }
showWarningMessageIfAppropriate(); showWarningMessagesIfAppropriate();
enableSubmitIfAppropriate(); enableSubmitIfAppropriate();
} }