Some additional network settings cleanup
[chromium-blink-merge.git] / chromeos / network / client_cert_util.cc
blob8f00865e6b1a215e1f1473513d980efdd88ec507
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromeos/network/client_cert_util.h"
7 #include <cert.h>
8 #include <pk11pub.h>
10 #include <list>
11 #include <string>
12 #include <vector>
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/values.h"
17 #include "chromeos/network/certificate_pattern.h"
18 #include "chromeos/network/network_event_log.h"
19 #include "components/onc/onc_constants.h"
20 #include "net/base/net_errors.h"
21 #include "net/cert/cert_database.h"
22 #include "net/cert/nss_cert_database.h"
23 #include "net/cert/scoped_nss_types.h"
24 #include "net/cert/x509_cert_types.h"
25 #include "net/cert/x509_certificate.h"
26 #include "third_party/cros_system_api/dbus/service_constants.h"
28 namespace chromeos {
30 namespace client_cert {
32 namespace {
34 const char kDefaultTPMPin[] = "111111";
36 std::string GetStringFromDictionary(const base::DictionaryValue& dict,
37 const std::string& key) {
38 std::string s;
39 dict.GetStringWithoutPathExpansion(key, &s);
40 return s;
43 void GetClientCertTypeAndPattern(
44 const base::DictionaryValue& dict_with_client_cert,
45 ClientCertConfig* cert_config) {
46 using namespace ::onc::client_cert;
47 dict_with_client_cert.GetStringWithoutPathExpansion(
48 kClientCertType, &cert_config->client_cert_type);
50 if (cert_config->client_cert_type == kPattern) {
51 const base::DictionaryValue* pattern = NULL;
52 dict_with_client_cert.GetDictionaryWithoutPathExpansion(kClientCertPattern,
53 &pattern);
54 if (pattern) {
55 bool success = cert_config->pattern.ReadFromONCDictionary(*pattern);
56 DCHECK(success);
61 } // namespace
63 // Returns true only if any fields set in this pattern match exactly with
64 // similar fields in the principal. If organization_ or organizational_unit_
65 // are set, then at least one of the organizations or units in the principal
66 // must match.
67 bool CertPrincipalMatches(const IssuerSubjectPattern& pattern,
68 const net::CertPrincipal& principal) {
69 if (!pattern.common_name().empty() &&
70 pattern.common_name() != principal.common_name) {
71 return false;
74 if (!pattern.locality().empty() &&
75 pattern.locality() != principal.locality_name) {
76 return false;
79 if (!pattern.organization().empty()) {
80 if (std::find(principal.organization_names.begin(),
81 principal.organization_names.end(),
82 pattern.organization()) ==
83 principal.organization_names.end()) {
84 return false;
88 if (!pattern.organizational_unit().empty()) {
89 if (std::find(principal.organization_unit_names.begin(),
90 principal.organization_unit_names.end(),
91 pattern.organizational_unit()) ==
92 principal.organization_unit_names.end()) {
93 return false;
97 return true;
100 std::string GetPkcs11AndSlotIdFromEapCertId(const std::string& cert_id,
101 int* slot_id) {
102 *slot_id = -1;
103 if (cert_id.empty())
104 return std::string();
106 size_t delimiter_pos = cert_id.find(':');
107 if (delimiter_pos == std::string::npos) {
108 // No delimiter found, so |cert_id| only contains the PKCS11 id.
109 return cert_id;
111 if (delimiter_pos + 1 >= cert_id.size()) {
112 LOG(ERROR) << "Empty PKCS11 id in cert id.";
113 return std::string();
115 int parsed_slot_id;
116 if (base::StringToInt(cert_id.substr(0, delimiter_pos), &parsed_slot_id))
117 *slot_id = parsed_slot_id;
118 else
119 LOG(ERROR) << "Slot ID is not an integer. Cert ID is: " << cert_id << ".";
120 return cert_id.substr(delimiter_pos + 1);
123 void GetClientCertFromShillProperties(
124 const base::DictionaryValue& shill_properties,
125 ConfigType* cert_config_type,
126 int* tpm_slot,
127 std::string* pkcs11_id) {
128 *cert_config_type = CONFIG_TYPE_NONE;
129 *tpm_slot = -1;
130 pkcs11_id->clear();
132 // Look for VPN specific client certificate properties.
134 // VPN Provider values are read from the "Provider" dictionary, not the
135 // "Provider.Type", etc keys (which are used only to set the values).
136 const base::DictionaryValue* provider_properties = NULL;
137 if (shill_properties.GetDictionaryWithoutPathExpansion(
138 shill::kProviderProperty, &provider_properties)) {
139 // Look for OpenVPN specific properties.
140 if (provider_properties->GetStringWithoutPathExpansion(
141 shill::kOpenVPNClientCertIdProperty, pkcs11_id)) {
142 *cert_config_type = CONFIG_TYPE_OPENVPN;
143 return;
145 // Look for L2TP-IPsec specific properties.
146 if (provider_properties->GetStringWithoutPathExpansion(
147 shill::kL2tpIpsecClientCertIdProperty, pkcs11_id)) {
148 std::string cert_slot;
149 provider_properties->GetStringWithoutPathExpansion(
150 shill::kL2tpIpsecClientCertSlotProperty, &cert_slot);
151 if (!cert_slot.empty() && !base::StringToInt(cert_slot, tpm_slot)) {
152 LOG(ERROR) << "Cert slot is not an integer: " << cert_slot << ".";
153 return;
156 *cert_config_type = CONFIG_TYPE_IPSEC;
158 return;
161 // Look for EAP specific client certificate properties, which can either be
162 // part of a WiFi or EthernetEAP configuration.
163 std::string cert_id;
164 if (shill_properties.GetStringWithoutPathExpansion(shill::kEapCertIdProperty,
165 &cert_id)) {
166 // Shill requires both CertID and KeyID for TLS connections, despite the
167 // fact that by convention they are the same ID, because one identifies
168 // the certificate and the other the private key.
169 std::string key_id;
170 shill_properties.GetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
171 &key_id);
172 // Assume the configuration to be invalid, if the two IDs are not identical.
173 if (cert_id != key_id) {
174 LOG(ERROR) << "EAP CertID differs from KeyID";
175 return;
177 *pkcs11_id = GetPkcs11AndSlotIdFromEapCertId(cert_id, tpm_slot);
178 *cert_config_type = CONFIG_TYPE_EAP;
182 void SetShillProperties(const ConfigType cert_config_type,
183 const int tpm_slot,
184 const std::string& pkcs11_id,
185 base::DictionaryValue* properties) {
186 switch (cert_config_type) {
187 case CONFIG_TYPE_NONE: {
188 return;
190 case CONFIG_TYPE_OPENVPN: {
191 properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty,
192 kDefaultTPMPin);
193 properties->SetStringWithoutPathExpansion(
194 shill::kOpenVPNClientCertIdProperty, pkcs11_id);
195 break;
197 case CONFIG_TYPE_IPSEC: {
198 properties->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty,
199 kDefaultTPMPin);
200 properties->SetStringWithoutPathExpansion(
201 shill::kL2tpIpsecClientCertSlotProperty, base::IntToString(tpm_slot));
202 properties->SetStringWithoutPathExpansion(
203 shill::kL2tpIpsecClientCertIdProperty, pkcs11_id);
204 break;
206 case CONFIG_TYPE_EAP: {
207 properties->SetStringWithoutPathExpansion(shill::kEapPinProperty,
208 kDefaultTPMPin);
209 std::string key_id =
210 base::StringPrintf("%i:%s", tpm_slot, pkcs11_id.c_str());
212 // Shill requires both CertID and KeyID for TLS connections, despite the
213 // fact that by convention they are the same ID, because one identifies
214 // the certificate and the other the private key.
215 properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty,
216 key_id);
217 properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
218 key_id);
219 break;
224 void SetEmptyShillProperties(const ConfigType cert_config_type,
225 base::DictionaryValue* properties) {
226 switch (cert_config_type) {
227 case CONFIG_TYPE_NONE: {
228 return;
230 case CONFIG_TYPE_OPENVPN: {
231 properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty,
232 std::string());
233 properties->SetStringWithoutPathExpansion(
234 shill::kOpenVPNClientCertIdProperty, std::string());
235 break;
237 case CONFIG_TYPE_IPSEC: {
238 properties->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty,
239 std::string());
240 properties->SetStringWithoutPathExpansion(
241 shill::kL2tpIpsecClientCertSlotProperty, std::string());
242 properties->SetStringWithoutPathExpansion(
243 shill::kL2tpIpsecClientCertIdProperty, std::string());
244 break;
246 case CONFIG_TYPE_EAP: {
247 properties->SetStringWithoutPathExpansion(shill::kEapPinProperty,
248 std::string());
249 // Shill requires both CertID and KeyID for TLS connections, despite the
250 // fact that by convention they are the same ID, because one identifies
251 // the certificate and the other the private key.
252 properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty,
253 std::string());
254 properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
255 std::string());
256 break;
261 ClientCertConfig::ClientCertConfig()
262 : location(CONFIG_TYPE_NONE),
263 client_cert_type(onc::client_cert::kClientCertTypeNone) {
266 void OncToClientCertConfig(const base::DictionaryValue& network_config,
267 ClientCertConfig* cert_config) {
268 using namespace ::onc;
270 *cert_config = ClientCertConfig();
272 const base::DictionaryValue* dict_with_client_cert = NULL;
274 const base::DictionaryValue* wifi = NULL;
275 network_config.GetDictionaryWithoutPathExpansion(network_config::kWiFi,
276 &wifi);
277 if (wifi) {
278 const base::DictionaryValue* eap = NULL;
279 wifi->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
280 if (!eap)
281 return;
283 dict_with_client_cert = eap;
284 cert_config->location = CONFIG_TYPE_EAP;
287 const base::DictionaryValue* vpn = NULL;
288 network_config.GetDictionaryWithoutPathExpansion(network_config::kVPN, &vpn);
289 if (vpn) {
290 const base::DictionaryValue* openvpn = NULL;
291 vpn->GetDictionaryWithoutPathExpansion(vpn::kOpenVPN, &openvpn);
292 const base::DictionaryValue* ipsec = NULL;
293 vpn->GetDictionaryWithoutPathExpansion(vpn::kIPsec, &ipsec);
294 if (openvpn) {
295 dict_with_client_cert = openvpn;
296 cert_config->location = CONFIG_TYPE_OPENVPN;
297 } else if (ipsec) {
298 dict_with_client_cert = ipsec;
299 cert_config->location = CONFIG_TYPE_IPSEC;
300 } else {
301 return;
305 const base::DictionaryValue* ethernet = NULL;
306 network_config.GetDictionaryWithoutPathExpansion(network_config::kEthernet,
307 &ethernet);
308 if (ethernet) {
309 const base::DictionaryValue* eap = NULL;
310 ethernet->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
311 if (!eap)
312 return;
313 dict_with_client_cert = eap;
314 cert_config->location = CONFIG_TYPE_EAP;
317 if (dict_with_client_cert)
318 GetClientCertTypeAndPattern(*dict_with_client_cert, cert_config);
321 bool IsCertificateConfigured(const ConfigType cert_config_type,
322 const base::DictionaryValue& service_properties) {
323 // VPN certificate properties are read from the Provider dictionary.
324 const base::DictionaryValue* provider_properties = NULL;
325 service_properties.GetDictionaryWithoutPathExpansion(
326 shill::kProviderProperty, &provider_properties);
327 switch (cert_config_type) {
328 case CONFIG_TYPE_NONE:
329 return true;
330 case CONFIG_TYPE_OPENVPN:
331 // OpenVPN generally requires a passphrase and we don't know whether or
332 // not one is required, so always return false here.
333 return false;
334 case CONFIG_TYPE_IPSEC: {
335 if (!provider_properties)
336 return false;
338 std::string client_cert_id;
339 provider_properties->GetStringWithoutPathExpansion(
340 shill::kL2tpIpsecClientCertIdProperty, &client_cert_id);
341 return !client_cert_id.empty();
343 case CONFIG_TYPE_EAP: {
344 std::string cert_id = GetStringFromDictionary(
345 service_properties, shill::kEapCertIdProperty);
346 std::string key_id = GetStringFromDictionary(
347 service_properties, shill::kEapKeyIdProperty);
348 std::string identity = GetStringFromDictionary(
349 service_properties, shill::kEapIdentityProperty);
350 return !cert_id.empty() && !key_id.empty() && !identity.empty();
353 NOTREACHED();
354 return false;
357 } // namespace client_cert
359 } // namespace chromeos