Commit aff3fcc9 authored by Steven Bennetts's avatar Steven Bennetts Committed by Commit Bot

Settings > Internet: Fix certificate updates and improve selection.

This fixes a bug where certificate updates were not triggering a refresh
in the new mojo based JS.

While testing that I discovered that the current default certificate
selection logic is broken. This is a subtle thing so was not noticed
previously. This fixes the logic, cleans up some code, and adds some
tests.


Bug: 1001598
Change-Id: Ib81d7f6e093e1e5837d5272c848a3422d6a80b5a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1825120
Commit-Queue: Steven Bennetts <stevenjb@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#700040}
parent c84d167c
...@@ -115,7 +115,10 @@ class FakeNetworkConfig { ...@@ -115,7 +115,10 @@ class FakeNetworkConfig {
* @return {!Promise} * @return {!Promise}
*/ */
whenCalled(methodName) { whenCalled(methodName) {
return this.getResolver_(methodName).promise; return this.getResolver_(methodName).promise.then(() => {
// Support sequential calls to whenCalled by replacing the promise.
this.resolverMap_.set(methodName, new PromiseResolver());
});
} }
/** /**
......
...@@ -8,6 +8,10 @@ suite('network-config', function() { ...@@ -8,6 +8,10 @@ suite('network-config', function() {
/** @type {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */ /** @type {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
let mojoApi_ = null; let mojoApi_ = null;
const kCaHash = 'CAHASH';
const kUserHash1 = 'USERHASH1';
const kUserHash2 = 'USERHASH2';
suiteSetup(function() { suiteSetup(function() {
mojoApi_ = new FakeNetworkConfig(); mojoApi_ = new FakeNetworkConfig();
network_config.MojoInterfaceProviderImpl.getInstance().remote_ = mojoApi_; network_config.MojoInterfaceProviderImpl.getInstance().remote_ = mojoApi_;
...@@ -121,15 +125,6 @@ suite('network-config', function() { ...@@ -121,15 +125,6 @@ suite('network-config', function() {
networkConfig.shareDefault = false; networkConfig.shareDefault = false;
} }
function setCertificatesForTest() {
const kHash1 = 'TESTHASH1', kHash2 = 'TESTHASH2';
var clientCert = {hash: kHash1, hardwareBacked: true, deviceWide: false};
var caCert = {hash: kHash2, hardwareBacked: true, deviceWide: true};
mojoApi_.setCertificatesForTest([caCert], [clientCert]);
this.selectedUserCertHash_ = kHash1;
this.selectedServerCaHash_ = kHash2;
}
test('New Config: Login or guest', function() { test('New Config: Login or guest', function() {
// Insecure networks are always shared so test a secure config. // Insecure networks are always shared so test a secure config.
setNetworkType( setNetworkType(
...@@ -238,43 +233,83 @@ suite('network-config', function() { ...@@ -238,43 +233,83 @@ suite('network-config', function() {
assertEquals('PEAP', outer.value); assertEquals('PEAP', outer.value);
}); });
}); });
});
test('WiFi EAP TLS', function() { suite('Certificates', function() {
const wifi1 = OncMojo.getDefaultManagedProperties( setup(function() {
chromeos.networkConfig.mojom.NetworkType.kWiFi, 'eaptlsguid', ''); mojoApi_.resetForTest();
wifi1.wifi.security = chromeos.networkConfig.mojom.SecurityType.kWpaEap; });
wifi1.wifi.eap = {outer: OncMojo.createManagedString('EAP-TLS')};
setNetworkConfig(wifi1); teardown(function() {
setCertificatesForTest(); PolymerTest.clearBody();
});
function setAuthenticated() {
// Logged in users can share new networks.
networkConfig.shareAllowEnable = true;
// Authenticated networks default to not shared.
networkConfig.shareDefault = false;
}
test('WiFi EAP-TLS No Certs', function() {
setNetworkType(
chromeos.networkConfig.mojom.NetworkType.kWiFi,
chromeos.networkConfig.mojom.SecurityType.kWpaEap);
setAuthenticated(); setAuthenticated();
initNetworkConfig(); initNetworkConfig();
networkConfig.shareNetwork_ = false;
networkConfig.set('eapProperties_.outer', 'EAP-TLS');
return mojoApi_.whenCalled('getNetworkCertificates').then(() => { return mojoApi_.whenCalled('getNetworkCertificates').then(() => {
return flushAsync().then(() => { return flushAsync().then(() => {
let outer = networkConfig.$$('#outer'); let outer = networkConfig.$$('#outer');
assertEquals('EAP-TLS', outer.value); assertEquals('EAP-TLS', outer.value);
// Check that with no certificates, 'do-not-check' amd 'no-certs'
// are selected.
assertEquals('do-not-check', networkConfig.selectedServerCaHash_);
assertEquals('no-certs', networkConfig.selectedUserCertHash_);
});
});
});
// check that a valid client user certificate is selected test('WiFi EAP-TLS Certs', function() {
let clientCert = networkConfig.$$('#userCert').$$('select').value; setNetworkType(
assertTrue(!!clientCert); chromeos.networkConfig.mojom.NetworkType.kWiFi,
let caCert = networkConfig.$$('#serverCa').$$('select').value; chromeos.networkConfig.mojom.SecurityType.kWpaEap);
assertTrue(!!caCert); setAuthenticated();
mojoApi_.setCertificatesForTest(
let share = networkConfig.$$('#share'); [{hash: kCaHash, hardwareBacked: true, deviceWide: true}],
assertTrue(!!share); [{hash: kUserHash1, hardwareBacked: true, deviceWide: false}]);
// share the EAP TLS network initNetworkConfig();
share.checked = true; networkConfig.shareNetwork_ = false;
// trigger the onShareChanged_ event networkConfig.set('eapProperties_.outer', 'EAP-TLS');
var event = new Event('change'); return mojoApi_.whenCalled('getNetworkCertificates').then(() => {
share.dispatchEvent(event); return flushAsync().then(() => {
// check that share is enabled // The first Server CA and User certificate should be selected.
assertTrue(share.checked); assertEquals(kCaHash, networkConfig.selectedServerCaHash_);
assertEquals(kUserHash1, networkConfig.selectedUserCertHash_);
});
});
});
// check that client certificate selection is empty test('WiFi EAP-TLS Certs Shared', function() {
clientCert = networkConfig.$$('#userCert').$$('select').value; setNetworkType(
assertFalse(!!clientCert); chromeos.networkConfig.mojom.NetworkType.kWiFi,
// check that ca device-wide cert is still selected chromeos.networkConfig.mojom.SecurityType.kWpaEap);
caCert = networkConfig.$$('#serverCa').$$('select').value; setAuthenticated();
assertTrue(!!caCert); mojoApi_.setCertificatesForTest(
[{hash: kCaHash, hardwareBacked: true, deviceWide: true}], [
{hash: kUserHash1, hardwareBacked: true, deviceWide: false},
{hash: kUserHash2, hardwareBacked: true, deviceWide: true}
]);
initNetworkConfig();
networkConfig.shareNetwork_ = true;
networkConfig.set('eapProperties_.outer', 'EAP-TLS');
return mojoApi_.whenCalled('getNetworkCertificates').then(() => {
return flushAsync().then(() => {
// The first Server CA should be selected.
assertEquals(kCaHash, networkConfig.selectedServerCaHash_);
// Second User Hash should be selected since it is a device cert.
assertEquals(kUserHash2, networkConfig.selectedUserCertHash_);
}); });
}); });
}); });
......
...@@ -383,7 +383,7 @@ Polymer({ ...@@ -383,7 +383,7 @@ Polymer({
this.hiddenNetworkWarning_ = this.showHiddenNetworkWarning_(); this.hiddenNetworkWarning_ = this.showHiddenNetworkWarning_();
this.updateIsConfigured_(); this.updateIsConfigured_();
this.updateDeviceCertsOnly_(); this.onNetworkCertificatesChanged();
}, },
save: function() { save: function() {
...@@ -477,7 +477,7 @@ Polymer({ ...@@ -477,7 +477,7 @@ Polymer({
}, },
/** CrNetworkListenerBehavior override */ /** CrNetworkListenerBehavior override */
onCertificateListsChanged_: function() { onNetworkCertificatesChanged: function() {
this.networkConfig_.getNetworkCertificates().then(response => { this.networkConfig_.getNetworkCertificates().then(response => {
const isOpenVpn = this.configProperties_.type == mojom.NetworkType.kVPN && const isOpenVpn = this.configProperties_.type == mojom.NetworkType.kVPN &&
this.configProperties_.vpn.type == mojom.VpnType.kOpenVPN; this.configProperties_.vpn.type == mojom.VpnType.kOpenVPN;
...@@ -648,7 +648,7 @@ Polymer({ ...@@ -648,7 +648,7 @@ Polymer({
/** @private */ /** @private */
onShareChanged_: function(event) { onShareChanged_: function(event) {
this.updateDeviceCertsOnly_(); this.updateSelectedCerts_();
}, },
/** /**
...@@ -811,7 +811,6 @@ Polymer({ ...@@ -811,7 +811,6 @@ Polymer({
if (managedProperties.type == mojom.NetworkType.kVPN) { if (managedProperties.type == mojom.NetworkType.kVPN) {
this.vpnType_ = this.getVpnTypeFromProperties_(this.configProperties_); this.vpnType_ = this.getVpnTypeFromProperties_(this.configProperties_);
} }
this.onCertificateListsChanged_();
}, },
/** /**
...@@ -1031,7 +1030,6 @@ Polymer({ ...@@ -1031,7 +1030,6 @@ Polymer({
}; };
} }
this.updateCertError_(); this.updateCertError_();
this.onCertificateListsChanged_();
}, },
/** @private */ /** @private */
...@@ -1145,29 +1143,53 @@ Polymer({ ...@@ -1145,29 +1143,53 @@ Polymer({
* @private * @private
*/ */
updateSelectedCerts_: function() { updateSelectedCerts_: function() {
if (!this.serverCaCerts_.length || !this.userCerts_.length) {
return;
}
const eap = this.eapProperties_;
// Only device-wide certificates can be used for shared networks that
// require a certificate.
this.deviceCertsOnly_ =
this.shareNetwork_ && !!eap && eap.outer == 'EAP-TLS';
// Validate selected Server CA. // Validate selected Server CA.
if (!this.findCert_(this.serverCaCerts_, this.selectedServerCaHash_)) { const caCert =
this.findCert_(this.serverCaCerts_, this.selectedServerCaHash_);
if (!caCert || (this.deviceCertsOnly_ && !caCert.deviceWide)) {
this.selectedServerCaHash_ = undefined; this.selectedServerCaHash_ = undefined;
} }
if (!this.selectedServerCaHash_) { if (!this.selectedServerCaHash_) {
const eap = this.eapProperties_; if (eap && eap.useSystemCas) {
if (eap) { this.selectedServerCaHash_ = DEFAULT_HASH;
this.selectedServerCaHash_ =
eap.useSystemCas ? DEFAULT_HASH : DO_NOT_CHECK_HASH;
} else if (!this.guid && this.serverCaCerts_[0]) { } else if (!this.guid && this.serverCaCerts_[0]) {
// For unconfigured networks only, default to the first CA. // For unconfigured networks, default to the first available
this.selectedServerCaHash_ = this.serverCaCerts_[0].hash; // certificate, or DO_NOT_CHECK (i.e. skip DEFAULT_HASH). See
/// onNetworkCertificatesChanged() for how certificates are added.
let cert = this.serverCaCerts_[0];
if (cert.hash == DEFAULT_HASH && this.serverCaCerts_[1]) {
cert = this.serverCaCerts_[1];
}
this.selectedServerCaHash_ = cert.hash;
} else { } else {
this.selectedServerCaHash_ = DO_NOT_CHECK_HASH; this.selectedServerCaHash_ = DO_NOT_CHECK_HASH;
} }
} }
// Validate selected User cert. // Validate selected User cert.
if (!this.findCert_(this.userCerts_, this.selectedUserCertHash_)) { const userCert =
this.findCert_(this.userCerts_, this.selectedUserCertHash_);
if (!userCert || (this.deviceCertsOnly_ && !userCert.deviceWide)) {
this.selectedUserCertHash_ = undefined; this.selectedUserCertHash_ = undefined;
} }
if (!this.selectedUserCertHash_ && this.userCerts_[0]) { if (!this.selectedUserCertHash_) {
this.selectedUserCertHash_ = this.userCerts_[0].hash; for (let i = 0; i < this.userCerts_.length; ++i) {
const userCert = this.userCerts_[i];
if (userCert && (!this.deviceCertsOnly_ || userCert.deviceWide)) {
this.selectedUserCertHash_ = userCert.hash;
break;
}
}
} }
}, },
...@@ -1207,27 +1229,6 @@ Polymer({ ...@@ -1207,27 +1229,6 @@ Polymer({
this.isConfigured_ = this.getIsConfigured_(); this.isConfigured_ = this.getIsConfigured_();
}, },
/** @private */
updateDeviceCertsOnly_: function() {
// Only device-wide certificates can be used for networks that require a
// certificate and are shared.
const eap = this.eapProperties_;
if (!this.shareNetwork_ || !eap || eap.outer != 'EAP-TLS') {
this.deviceCertsOnly_ = false;
return;
}
// Clear selection if certificate is not device-wide.
let cert = this.findCert_(this.userCerts_, this.selectedUserCertHash_);
if (cert && !cert.deviceWide) {
this.selectedUserCertHash_ = undefined;
}
cert = this.findCert_(this.serverCaCerts_, this.selectedServerCaHash_);
if (cert && !cert.deviceWide) {
this.selectedServerCaHash_ = undefined;
}
this.deviceCertsOnly_ = true;
},
/** /**
* @param {mojom.NetworkType} networkType * @param {mojom.NetworkType} networkType
* @return {boolean} * @return {boolean}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment