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
diff --git a/res/layout/wifi_dialog.xml b/res/layout/wifi_dialog.xml
index 5233e5e..77ad3f8 100644
--- a/res/layout/wifi_dialog.xml
+++ b/res/layout/wifi_dialog.xml
@@ -175,6 +175,18 @@
android:inputType="textNoSuggestions" />
</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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 795c58d..81e54e2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1586,12 +1586,17 @@
<string name="wifi_unspecified">Please select</string>
<!-- Hint for multiple certificates being added to the configuration -->
<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 -->
<string name="wifi_do_not_provide_eap_user_cert">Do not provide</string>
<!-- Menu option for not validating the EAP server -->
<string name="wifi_do_not_validate_eap_server">Do not validate</string>
<!-- 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>
+ <!-- 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
string is listed first [CHAR LIMIT=20]-->
<string name="wifi_wps_available_first_item">WPS available</string>
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 4145a08..285b316 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -73,6 +73,8 @@
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 @@
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 @@
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 @@
}
}
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 @@
} 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 @@
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 @@
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 @@
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 @@
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 @@
}
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<String> certs = new ArrayList<String>();
- 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<String> adapter = new ArrayAdapter<String>(
context, android.R.layout.simple_spinner_item,
@@ -1075,6 +1127,7 @@
public void afterTextChanged(Editable s) {
mTextViewChangedHandler.post(new Runnable() {
public void run() {
+ showWarningMessagesIfAppropriate();
enableSubmitIfAppropriate();
}
});
@@ -1121,7 +1174,7 @@
} else {
showIpConfigFields();
}
- showWarningMessageIfAppropriate();
+ showWarningMessagesIfAppropriate();
enableSubmitIfAppropriate();
}