Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chromeos / network / client_cert_util.cc
blob1f810f06d366eb556b3c881f2928cb912868e690
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>
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/values.h"
15 #include "chromeos/network/network_event_log.h"
16 #include "components/onc/onc_constants.h"
17 #include "net/base/net_errors.h"
18 #include "net/cert/cert_database.h"
19 #include "net/cert/nss_cert_database.h"
20 #include "net/cert/scoped_nss_types.h"
21 #include "net/cert/x509_cert_types.h"
22 #include "net/cert/x509_certificate.h"
23 #include "third_party/cros_system_api/dbus/service_constants.h"
25 namespace chromeos {
27 namespace client_cert {
29 namespace {
31 const char kDefaultTPMPin[] = "111111";
33 std::string GetStringFromDictionary(const base::DictionaryValue& dict,
34 const std::string& key) {
35 std::string s;
36 dict.GetStringWithoutPathExpansion(key, &s);
37 return s;
40 void GetClientCertTypeAndPattern(
41 const base::DictionaryValue& dict_with_client_cert,
42 ClientCertConfig* cert_config) {
43 using namespace ::onc::client_cert;
44 dict_with_client_cert.GetStringWithoutPathExpansion(
45 kClientCertType, &cert_config->client_cert_type);
47 if (cert_config->client_cert_type == kPattern) {
48 const base::DictionaryValue* pattern = NULL;
49 dict_with_client_cert.GetDictionaryWithoutPathExpansion(kClientCertPattern,
50 &pattern);
51 if (pattern) {
52 bool success = cert_config->pattern.ReadFromONCDictionary(*pattern);
53 DCHECK(success);
58 } // namespace
60 // Returns true only if any fields set in this pattern match exactly with
61 // similar fields in the principal. If organization_ or organizational_unit_
62 // are set, then at least one of the organizations or units in the principal
63 // must match.
64 bool CertPrincipalMatches(const IssuerSubjectPattern& pattern,
65 const net::CertPrincipal& principal) {
66 if (!pattern.common_name().empty() &&
67 pattern.common_name() != principal.common_name) {
68 return false;
71 if (!pattern.locality().empty() &&
72 pattern.locality() != principal.locality_name) {
73 return false;
76 if (!pattern.organization().empty()) {
77 if (std::find(principal.organization_names.begin(),
78 principal.organization_names.end(),
79 pattern.organization()) ==
80 principal.organization_names.end()) {
81 return false;
85 if (!pattern.organizational_unit().empty()) {
86 if (std::find(principal.organization_unit_names.begin(),
87 principal.organization_unit_names.end(),
88 pattern.organizational_unit()) ==
89 principal.organization_unit_names.end()) {
90 return false;
94 return true;
97 std::string GetPkcs11AndSlotIdFromEapCertId(const std::string& cert_id,
98 int* slot_id) {
99 *slot_id = -1;
100 if (cert_id.empty())
101 return std::string();
103 size_t delimiter_pos = cert_id.find(':');
104 if (delimiter_pos == std::string::npos) {
105 // No delimiter found, so |cert_id| only contains the PKCS11 id.
106 return cert_id;
108 if (delimiter_pos + 1 >= cert_id.size()) {
109 LOG(ERROR) << "Empty PKCS11 id in cert id.";
110 return std::string();
112 int parsed_slot_id;
113 if (base::StringToInt(cert_id.substr(0, delimiter_pos), &parsed_slot_id))
114 *slot_id = parsed_slot_id;
115 else
116 LOG(ERROR) << "Slot ID is not an integer. Cert ID is: " << cert_id << ".";
117 return cert_id.substr(delimiter_pos + 1);
120 void GetClientCertFromShillProperties(
121 const base::DictionaryValue& shill_properties,
122 ConfigType* cert_config_type,
123 int* tpm_slot,
124 std::string* pkcs11_id) {
125 *cert_config_type = CONFIG_TYPE_NONE;
126 *tpm_slot = -1;
127 pkcs11_id->clear();
129 // Look for VPN specific client certificate properties.
131 // VPN Provider values are read from the "Provider" dictionary, not the
132 // "Provider.Type", etc keys (which are used only to set the values).
133 const base::DictionaryValue* provider_properties = NULL;
134 if (shill_properties.GetDictionaryWithoutPathExpansion(
135 shill::kProviderProperty, &provider_properties)) {
136 // Look for OpenVPN specific properties.
137 if (provider_properties->GetStringWithoutPathExpansion(
138 shill::kOpenVPNClientCertIdProperty, pkcs11_id)) {
139 *cert_config_type = CONFIG_TYPE_OPENVPN;
140 return;
142 // Look for L2TP-IPsec specific properties.
143 if (provider_properties->GetStringWithoutPathExpansion(
144 shill::kL2tpIpsecClientCertIdProperty, pkcs11_id)) {
145 std::string cert_slot;
146 provider_properties->GetStringWithoutPathExpansion(
147 shill::kL2tpIpsecClientCertSlotProperty, &cert_slot);
148 if (!cert_slot.empty() && !base::StringToInt(cert_slot, tpm_slot)) {
149 LOG(ERROR) << "Cert slot is not an integer: " << cert_slot << ".";
150 return;
153 *cert_config_type = CONFIG_TYPE_IPSEC;
155 return;
158 // Look for EAP specific client certificate properties, which can either be
159 // part of a WiFi or EthernetEAP configuration.
160 std::string cert_id;
161 if (shill_properties.GetStringWithoutPathExpansion(shill::kEapCertIdProperty,
162 &cert_id)) {
163 // Shill requires both CertID and KeyID for TLS connections, despite the
164 // fact that by convention they are the same ID, because one identifies
165 // the certificate and the other the private key.
166 std::string key_id;
167 shill_properties.GetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
168 &key_id);
169 // Assume the configuration to be invalid, if the two IDs are not identical.
170 if (cert_id != key_id) {
171 LOG(ERROR) << "EAP CertID differs from KeyID";
172 return;
174 *pkcs11_id = GetPkcs11AndSlotIdFromEapCertId(cert_id, tpm_slot);
175 *cert_config_type = CONFIG_TYPE_EAP;
179 void SetShillProperties(const ConfigType cert_config_type,
180 const int tpm_slot,
181 const std::string& pkcs11_id,
182 base::DictionaryValue* properties) {
183 switch (cert_config_type) {
184 case CONFIG_TYPE_NONE: {
185 return;
187 case CONFIG_TYPE_OPENVPN: {
188 properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty,
189 kDefaultTPMPin);
190 properties->SetStringWithoutPathExpansion(
191 shill::kOpenVPNClientCertIdProperty, pkcs11_id);
192 break;
194 case CONFIG_TYPE_IPSEC: {
195 properties->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty,
196 kDefaultTPMPin);
197 properties->SetStringWithoutPathExpansion(
198 shill::kL2tpIpsecClientCertSlotProperty, base::IntToString(tpm_slot));
199 properties->SetStringWithoutPathExpansion(
200 shill::kL2tpIpsecClientCertIdProperty, pkcs11_id);
201 break;
203 case CONFIG_TYPE_EAP: {
204 properties->SetStringWithoutPathExpansion(shill::kEapPinProperty,
205 kDefaultTPMPin);
206 std::string key_id =
207 base::StringPrintf("%i:%s", tpm_slot, pkcs11_id.c_str());
209 // Shill requires both CertID and KeyID for TLS connections, despite the
210 // fact that by convention they are the same ID, because one identifies
211 // the certificate and the other the private key.
212 properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty,
213 key_id);
214 properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
215 key_id);
216 break;
221 void SetEmptyShillProperties(const ConfigType cert_config_type,
222 base::DictionaryValue* properties) {
223 switch (cert_config_type) {
224 case CONFIG_TYPE_NONE: {
225 return;
227 case CONFIG_TYPE_OPENVPN: {
228 properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty,
229 std::string());
230 properties->SetStringWithoutPathExpansion(
231 shill::kOpenVPNClientCertIdProperty, std::string());
232 break;
234 case CONFIG_TYPE_IPSEC: {
235 properties->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty,
236 std::string());
237 properties->SetStringWithoutPathExpansion(
238 shill::kL2tpIpsecClientCertSlotProperty, std::string());
239 properties->SetStringWithoutPathExpansion(
240 shill::kL2tpIpsecClientCertIdProperty, std::string());
241 break;
243 case CONFIG_TYPE_EAP: {
244 properties->SetStringWithoutPathExpansion(shill::kEapPinProperty,
245 std::string());
246 // Shill requires both CertID and KeyID for TLS connections, despite the
247 // fact that by convention they are the same ID, because one identifies
248 // the certificate and the other the private key.
249 properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty,
250 std::string());
251 properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
252 std::string());
253 break;
258 ClientCertConfig::ClientCertConfig()
259 : location(CONFIG_TYPE_NONE),
260 client_cert_type(onc::client_cert::kClientCertTypeNone) {
263 void OncToClientCertConfig(const base::DictionaryValue& network_config,
264 ClientCertConfig* cert_config) {
265 using namespace ::onc;
267 *cert_config = ClientCertConfig();
269 const base::DictionaryValue* dict_with_client_cert = NULL;
271 const base::DictionaryValue* wifi = NULL;
272 network_config.GetDictionaryWithoutPathExpansion(network_config::kWiFi,
273 &wifi);
274 if (wifi) {
275 const base::DictionaryValue* eap = NULL;
276 wifi->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
277 if (!eap)
278 return;
280 dict_with_client_cert = eap;
281 cert_config->location = CONFIG_TYPE_EAP;
284 const base::DictionaryValue* vpn = NULL;
285 network_config.GetDictionaryWithoutPathExpansion(network_config::kVPN, &vpn);
286 if (vpn) {
287 const base::DictionaryValue* openvpn = NULL;
288 vpn->GetDictionaryWithoutPathExpansion(vpn::kOpenVPN, &openvpn);
289 const base::DictionaryValue* ipsec = NULL;
290 vpn->GetDictionaryWithoutPathExpansion(vpn::kIPsec, &ipsec);
291 if (openvpn) {
292 dict_with_client_cert = openvpn;
293 cert_config->location = CONFIG_TYPE_OPENVPN;
294 } else if (ipsec) {
295 dict_with_client_cert = ipsec;
296 cert_config->location = CONFIG_TYPE_IPSEC;
297 } else {
298 return;
302 const base::DictionaryValue* ethernet = NULL;
303 network_config.GetDictionaryWithoutPathExpansion(network_config::kEthernet,
304 &ethernet);
305 if (ethernet) {
306 const base::DictionaryValue* eap = NULL;
307 ethernet->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
308 if (!eap)
309 return;
310 dict_with_client_cert = eap;
311 cert_config->location = CONFIG_TYPE_EAP;
314 if (dict_with_client_cert)
315 GetClientCertTypeAndPattern(*dict_with_client_cert, cert_config);
318 bool IsCertificateConfigured(const ConfigType cert_config_type,
319 const base::DictionaryValue& service_properties) {
320 // VPN certificate properties are read from the Provider dictionary.
321 const base::DictionaryValue* provider_properties = NULL;
322 service_properties.GetDictionaryWithoutPathExpansion(
323 shill::kProviderProperty, &provider_properties);
324 switch (cert_config_type) {
325 case CONFIG_TYPE_NONE:
326 return true;
327 case CONFIG_TYPE_OPENVPN:
328 // OpenVPN generally requires a passphrase and we don't know whether or
329 // not one is required, so always return false here.
330 return false;
331 case CONFIG_TYPE_IPSEC: {
332 if (!provider_properties)
333 return false;
335 std::string client_cert_id;
336 provider_properties->GetStringWithoutPathExpansion(
337 shill::kL2tpIpsecClientCertIdProperty, &client_cert_id);
338 return !client_cert_id.empty();
340 case CONFIG_TYPE_EAP: {
341 std::string cert_id = GetStringFromDictionary(
342 service_properties, shill::kEapCertIdProperty);
343 std::string key_id = GetStringFromDictionary(
344 service_properties, shill::kEapKeyIdProperty);
345 std::string identity = GetStringFromDictionary(
346 service_properties, shill::kEapIdentityProperty);
347 return !cert_id.empty() && !key_id.empty() && !identity.empty();
350 NOTREACHED();
351 return false;
354 } // namespace client_cert
356 } // namespace chromeos