• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chromeos/network/client_cert_util.h"
6 
7 #include <cert.h>
8 #include <pk11pub.h>
9 
10 #include <list>
11 #include <string>
12 #include <vector>
13 
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"
27 
28 namespace chromeos {
29 
30 namespace client_cert {
31 
32 namespace {
33 
34 const char kDefaultTPMPin[] = "111111";
35 
GetStringFromDictionary(const base::DictionaryValue & dict,const std::string & key)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;
41 }
42 
GetClientCertTypeAndPattern(const base::DictionaryValue & dict_with_client_cert,ClientCertConfig * cert_config)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);
49 
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);
57     }
58   }
59 }
60 
61 }  // namespace
62 
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.
CertPrincipalMatches(const IssuerSubjectPattern & pattern,const net::CertPrincipal & 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) {
71     return false;
72   }
73 
74   if (!pattern.locality().empty() &&
75       pattern.locality() != principal.locality_name) {
76     return false;
77   }
78 
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;
85     }
86   }
87 
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;
94     }
95   }
96 
97   return true;
98 }
99 
GetPkcs11AndSlotIdFromEapCertId(const std::string & cert_id,int * slot_id)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();
105 
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;
110   }
111   if (delimiter_pos + 1 >= cert_id.size()) {
112     LOG(ERROR) << "Empty PKCS11 id in cert id.";
113     return std::string();
114   }
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);
121 }
122 
GetClientCertFromShillProperties(const base::DictionaryValue & shill_properties,ConfigType * cert_config_type,int * tpm_slot,std::string * pkcs11_id)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();
131 
132   // Look for VPN specific client certificate properties.
133   //
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;
144     }
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;
154       }
155 
156       *cert_config_type = CONFIG_TYPE_IPSEC;
157     }
158     return;
159   }
160 
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;
176     }
177     *pkcs11_id = GetPkcs11AndSlotIdFromEapCertId(cert_id, tpm_slot);
178     *cert_config_type = CONFIG_TYPE_EAP;
179   }
180 }
181 
SetShillProperties(const ConfigType cert_config_type,const int tpm_slot,const std::string & pkcs11_id,base::DictionaryValue * properties)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;
189     }
190     case CONFIG_TYPE_OPENVPN: {
191       properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty,
192                                                 kDefaultTPMPin);
193       properties->SetStringWithoutPathExpansion(
194           shill::kOpenVPNClientCertIdProperty, pkcs11_id);
195       break;
196     }
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;
205     }
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());
211 
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;
220     }
221   }
222 }
223 
SetEmptyShillProperties(const ConfigType cert_config_type,base::DictionaryValue * properties)224 void SetEmptyShillProperties(const ConfigType cert_config_type,
225                              base::DictionaryValue* properties) {
226   switch (cert_config_type) {
227     case CONFIG_TYPE_NONE: {
228       return;
229     }
230     case CONFIG_TYPE_OPENVPN: {
231       properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty,
232                                                 std::string());
233       properties->SetStringWithoutPathExpansion(
234           shill::kOpenVPNClientCertIdProperty, std::string());
235       break;
236     }
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;
245     }
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;
257     }
258   }
259 }
260 
ClientCertConfig()261 ClientCertConfig::ClientCertConfig()
262     : location(CONFIG_TYPE_NONE),
263       client_cert_type(onc::client_cert::kClientCertTypeNone) {
264 }
265 
OncToClientCertConfig(const base::DictionaryValue & network_config,ClientCertConfig * cert_config)266 void OncToClientCertConfig(const base::DictionaryValue& network_config,
267                            ClientCertConfig* cert_config) {
268   using namespace ::onc;
269 
270   *cert_config = ClientCertConfig();
271 
272   const base::DictionaryValue* dict_with_client_cert = NULL;
273 
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;
282 
283     dict_with_client_cert = eap;
284     cert_config->location = CONFIG_TYPE_EAP;
285   }
286 
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;
302     }
303   }
304 
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;
315   }
316 
317   if (dict_with_client_cert)
318     GetClientCertTypeAndPattern(*dict_with_client_cert, cert_config);
319 }
320 
IsCertificateConfigured(const ConfigType cert_config_type,const base::DictionaryValue & service_properties)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;
337 
338       std::string client_cert_id;
339       provider_properties->GetStringWithoutPathExpansion(
340           shill::kL2tpIpsecClientCertIdProperty, &client_cert_id);
341       return !client_cert_id.empty();
342     }
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();
351     }
352   }
353   NOTREACHED();
354   return false;
355 }
356 
357 }  // namespace client_cert
358 
359 }  // namespace chromeos
360