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"
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"
27 namespace client_cert
{
31 const char kDefaultTPMPin
[] = "111111";
33 std::string
GetStringFromDictionary(const base::DictionaryValue
& dict
,
34 const std::string
& key
) {
36 dict
.GetStringWithoutPathExpansion(key
, &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
,
52 bool success
= cert_config
->pattern
.ReadFromONCDictionary(*pattern
);
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
64 bool CertPrincipalMatches(const IssuerSubjectPattern
& pattern
,
65 const net::CertPrincipal
& principal
) {
66 if (!pattern
.common_name().empty() &&
67 pattern
.common_name() != principal
.common_name
) {
71 if (!pattern
.locality().empty() &&
72 pattern
.locality() != principal
.locality_name
) {
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()) {
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()) {
97 std::string
GetPkcs11AndSlotIdFromEapCertId(const std::string
& cert_id
,
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.
108 if (delimiter_pos
+ 1 >= cert_id
.size()) {
109 LOG(ERROR
) << "Empty PKCS11 id in cert id.";
110 return std::string();
113 if (base::StringToInt(cert_id
.substr(0, delimiter_pos
), &parsed_slot_id
))
114 *slot_id
= parsed_slot_id
;
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
,
124 std::string
* pkcs11_id
) {
125 *cert_config_type
= CONFIG_TYPE_NONE
;
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
;
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
<< ".";
153 *cert_config_type
= CONFIG_TYPE_IPSEC
;
158 // Look for EAP specific client certificate properties, which can either be
159 // part of a WiFi or EthernetEAP configuration.
161 if (shill_properties
.GetStringWithoutPathExpansion(shill::kEapCertIdProperty
,
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.
167 shill_properties
.GetStringWithoutPathExpansion(shill::kEapKeyIdProperty
,
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";
174 *pkcs11_id
= GetPkcs11AndSlotIdFromEapCertId(cert_id
, tpm_slot
);
175 *cert_config_type
= CONFIG_TYPE_EAP
;
179 void SetShillProperties(const ConfigType cert_config_type
,
181 const std::string
& pkcs11_id
,
182 base::DictionaryValue
* properties
) {
183 switch (cert_config_type
) {
184 case CONFIG_TYPE_NONE
: {
187 case CONFIG_TYPE_OPENVPN
: {
188 properties
->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty
,
190 properties
->SetStringWithoutPathExpansion(
191 shill::kOpenVPNClientCertIdProperty
, pkcs11_id
);
194 case CONFIG_TYPE_IPSEC
: {
195 properties
->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty
,
197 properties
->SetStringWithoutPathExpansion(
198 shill::kL2tpIpsecClientCertSlotProperty
, base::IntToString(tpm_slot
));
199 properties
->SetStringWithoutPathExpansion(
200 shill::kL2tpIpsecClientCertIdProperty
, pkcs11_id
);
203 case CONFIG_TYPE_EAP
: {
204 properties
->SetStringWithoutPathExpansion(shill::kEapPinProperty
,
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
,
214 properties
->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty
,
221 void SetEmptyShillProperties(const ConfigType cert_config_type
,
222 base::DictionaryValue
* properties
) {
223 switch (cert_config_type
) {
224 case CONFIG_TYPE_NONE
: {
227 case CONFIG_TYPE_OPENVPN
: {
228 properties
->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty
,
230 properties
->SetStringWithoutPathExpansion(
231 shill::kOpenVPNClientCertIdProperty
, std::string());
234 case CONFIG_TYPE_IPSEC
: {
235 properties
->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty
,
237 properties
->SetStringWithoutPathExpansion(
238 shill::kL2tpIpsecClientCertSlotProperty
, std::string());
239 properties
->SetStringWithoutPathExpansion(
240 shill::kL2tpIpsecClientCertIdProperty
, std::string());
243 case CONFIG_TYPE_EAP
: {
244 properties
->SetStringWithoutPathExpansion(shill::kEapPinProperty
,
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
,
251 properties
->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty
,
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
,
275 const base::DictionaryValue
* eap
= NULL
;
276 wifi
->GetDictionaryWithoutPathExpansion(wifi::kEAP
, &eap
);
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
);
287 const base::DictionaryValue
* openvpn
= NULL
;
288 vpn
->GetDictionaryWithoutPathExpansion(vpn::kOpenVPN
, &openvpn
);
289 const base::DictionaryValue
* ipsec
= NULL
;
290 vpn
->GetDictionaryWithoutPathExpansion(vpn::kIPsec
, &ipsec
);
292 dict_with_client_cert
= openvpn
;
293 cert_config
->location
= CONFIG_TYPE_OPENVPN
;
295 dict_with_client_cert
= ipsec
;
296 cert_config
->location
= CONFIG_TYPE_IPSEC
;
302 const base::DictionaryValue
* ethernet
= NULL
;
303 network_config
.GetDictionaryWithoutPathExpansion(network_config::kEthernet
,
306 const base::DictionaryValue
* eap
= NULL
;
307 ethernet
->GetDictionaryWithoutPathExpansion(wifi::kEAP
, &eap
);
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
:
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.
331 case CONFIG_TYPE_IPSEC
: {
332 if (!provider_properties
)
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();
354 } // namespace client_cert
356 } // namespace chromeos