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"
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"
30 namespace client_cert
{
34 const char kDefaultTPMPin
[] = "111111";
36 std::string
GetStringFromDictionary(const base::DictionaryValue
& dict
,
37 const std::string
& key
) {
39 dict
.GetStringWithoutPathExpansion(key
, &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
,
55 bool success
= cert_config
->pattern
.ReadFromONCDictionary(*pattern
);
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
67 bool CertPrincipalMatches(const IssuerSubjectPattern
& pattern
,
68 const net::CertPrincipal
& principal
) {
69 if (!pattern
.common_name().empty() &&
70 pattern
.common_name() != principal
.common_name
) {
74 if (!pattern
.locality().empty() &&
75 pattern
.locality() != principal
.locality_name
) {
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()) {
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()) {
100 std::string
GetPkcs11AndSlotIdFromEapCertId(const std::string
& cert_id
,
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.
111 if (delimiter_pos
+ 1 >= cert_id
.size()) {
112 LOG(ERROR
) << "Empty PKCS11 id in cert id.";
113 return std::string();
116 if (base::StringToInt(cert_id
.substr(0, delimiter_pos
), &parsed_slot_id
))
117 *slot_id
= parsed_slot_id
;
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
,
127 std::string
* pkcs11_id
) {
128 *cert_config_type
= CONFIG_TYPE_NONE
;
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
;
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
<< ".";
156 *cert_config_type
= CONFIG_TYPE_IPSEC
;
161 // Look for EAP specific client certificate properties, which can either be
162 // part of a WiFi or EthernetEAP configuration.
164 if (shill_properties
.GetStringWithoutPathExpansion(shill::kEapCertIdProperty
,
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.
170 shill_properties
.GetStringWithoutPathExpansion(shill::kEapKeyIdProperty
,
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";
177 *pkcs11_id
= GetPkcs11AndSlotIdFromEapCertId(cert_id
, tpm_slot
);
178 *cert_config_type
= CONFIG_TYPE_EAP
;
182 void SetShillProperties(const ConfigType cert_config_type
,
184 const std::string
& pkcs11_id
,
185 base::DictionaryValue
* properties
) {
186 switch (cert_config_type
) {
187 case CONFIG_TYPE_NONE
: {
190 case CONFIG_TYPE_OPENVPN
: {
191 properties
->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty
,
193 properties
->SetStringWithoutPathExpansion(
194 shill::kOpenVPNClientCertIdProperty
, pkcs11_id
);
197 case CONFIG_TYPE_IPSEC
: {
198 properties
->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty
,
200 properties
->SetStringWithoutPathExpansion(
201 shill::kL2tpIpsecClientCertSlotProperty
, base::IntToString(tpm_slot
));
202 properties
->SetStringWithoutPathExpansion(
203 shill::kL2tpIpsecClientCertIdProperty
, pkcs11_id
);
206 case CONFIG_TYPE_EAP
: {
207 properties
->SetStringWithoutPathExpansion(shill::kEapPinProperty
,
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
,
217 properties
->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty
,
224 void SetEmptyShillProperties(const ConfigType cert_config_type
,
225 base::DictionaryValue
* properties
) {
226 switch (cert_config_type
) {
227 case CONFIG_TYPE_NONE
: {
230 case CONFIG_TYPE_OPENVPN
: {
231 properties
->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty
,
233 properties
->SetStringWithoutPathExpansion(
234 shill::kOpenVPNClientCertIdProperty
, std::string());
237 case CONFIG_TYPE_IPSEC
: {
238 properties
->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty
,
240 properties
->SetStringWithoutPathExpansion(
241 shill::kL2tpIpsecClientCertSlotProperty
, std::string());
242 properties
->SetStringWithoutPathExpansion(
243 shill::kL2tpIpsecClientCertIdProperty
, std::string());
246 case CONFIG_TYPE_EAP
: {
247 properties
->SetStringWithoutPathExpansion(shill::kEapPinProperty
,
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
,
254 properties
->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty
,
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
,
278 const base::DictionaryValue
* eap
= NULL
;
279 wifi
->GetDictionaryWithoutPathExpansion(wifi::kEAP
, &eap
);
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
);
290 const base::DictionaryValue
* openvpn
= NULL
;
291 vpn
->GetDictionaryWithoutPathExpansion(vpn::kOpenVPN
, &openvpn
);
292 const base::DictionaryValue
* ipsec
= NULL
;
293 vpn
->GetDictionaryWithoutPathExpansion(vpn::kIPsec
, &ipsec
);
295 dict_with_client_cert
= openvpn
;
296 cert_config
->location
= CONFIG_TYPE_OPENVPN
;
298 dict_with_client_cert
= ipsec
;
299 cert_config
->location
= CONFIG_TYPE_IPSEC
;
305 const base::DictionaryValue
* ethernet
= NULL
;
306 network_config
.GetDictionaryWithoutPathExpansion(network_config::kEthernet
,
309 const base::DictionaryValue
* eap
= NULL
;
310 ethernet
->GetDictionaryWithoutPathExpansion(wifi::kEAP
, &eap
);
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
:
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.
334 case CONFIG_TYPE_IPSEC
: {
335 if (!provider_properties
)
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();
357 } // namespace client_cert
359 } // namespace chromeos