client_cert_util.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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
28namespace chromeos {
29
30namespace client_cert {
31
32namespace {
33
34const char kDefaultTPMPin[] = "111111";
35
36std::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
43void 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.
67bool 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
100std::string GetPkcs11IdFromEapCertId(const std::string& cert_id) {
101  if (cert_id.empty())
102    return std::string();
103
104  size_t delimiter_pos = cert_id.find(':');
105  if (delimiter_pos == std::string::npos) {
106    // No delimiter found, so |cert_id| only contains the PKCS11 id.
107    return cert_id;
108  }
109  if (delimiter_pos + 1 >= cert_id.size()) {
110    LOG(ERROR) << "Empty PKCS11 id in cert id.";
111    return std::string();
112  }
113  return cert_id.substr(delimiter_pos + 1);
114}
115
116void SetShillProperties(const ConfigType cert_config_type,
117                        const int tpm_slot,
118                        const std::string& pkcs11_id,
119                        base::DictionaryValue* properties) {
120  switch (cert_config_type) {
121    case CONFIG_TYPE_NONE: {
122      return;
123    }
124    case CONFIG_TYPE_OPENVPN: {
125      properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty,
126                                                kDefaultTPMPin);
127      properties->SetStringWithoutPathExpansion(
128          shill::kOpenVPNClientCertIdProperty, pkcs11_id);
129      break;
130    }
131    case CONFIG_TYPE_IPSEC: {
132      properties->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty,
133                                                kDefaultTPMPin);
134      properties->SetStringWithoutPathExpansion(
135          shill::kL2tpIpsecClientCertSlotProperty, base::IntToString(tpm_slot));
136      properties->SetStringWithoutPathExpansion(
137          shill::kL2tpIpsecClientCertIdProperty, pkcs11_id);
138      break;
139    }
140    case CONFIG_TYPE_EAP: {
141      properties->SetStringWithoutPathExpansion(shill::kEapPinProperty,
142                                                kDefaultTPMPin);
143      std::string key_id =
144          base::StringPrintf("%i:%s", tpm_slot, pkcs11_id.c_str());
145
146      // Shill requires both CertID and KeyID for TLS connections, despite the
147      // fact that by convention they are the same ID, because one identifies
148      // the certificate and the other the private key.
149      properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty,
150                                                key_id);
151      properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
152                                                key_id);
153      break;
154    }
155  }
156}
157
158void SetEmptyShillProperties(const ConfigType cert_config_type,
159                             base::DictionaryValue* properties) {
160  switch (cert_config_type) {
161    case CONFIG_TYPE_NONE: {
162      return;
163    }
164    case CONFIG_TYPE_OPENVPN: {
165      properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty,
166                                                std::string());
167      properties->SetStringWithoutPathExpansion(
168          shill::kOpenVPNClientCertIdProperty, std::string());
169      break;
170    }
171    case CONFIG_TYPE_IPSEC: {
172      properties->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty,
173                                                std::string());
174      properties->SetStringWithoutPathExpansion(
175          shill::kL2tpIpsecClientCertSlotProperty, std::string());
176      properties->SetStringWithoutPathExpansion(
177          shill::kL2tpIpsecClientCertIdProperty, std::string());
178      break;
179    }
180    case CONFIG_TYPE_EAP: {
181      properties->SetStringWithoutPathExpansion(shill::kEapPinProperty,
182                                                std::string());
183      // Shill requires both CertID and KeyID for TLS connections, despite the
184      // fact that by convention they are the same ID, because one identifies
185      // the certificate and the other the private key.
186      properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty,
187                                                std::string());
188      properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
189                                                std::string());
190      break;
191    }
192  }
193}
194
195ClientCertConfig::ClientCertConfig()
196    : location(CONFIG_TYPE_NONE),
197      client_cert_type(onc::client_cert::kClientCertTypeNone) {
198}
199
200void OncToClientCertConfig(const base::DictionaryValue& network_config,
201                           ClientCertConfig* cert_config) {
202  using namespace ::onc;
203
204  *cert_config = ClientCertConfig();
205
206  const base::DictionaryValue* dict_with_client_cert = NULL;
207
208  const base::DictionaryValue* wifi = NULL;
209  network_config.GetDictionaryWithoutPathExpansion(network_config::kWiFi,
210                                                   &wifi);
211  if (wifi) {
212    const base::DictionaryValue* eap = NULL;
213    wifi->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
214    if (!eap)
215      return;
216
217    dict_with_client_cert = eap;
218    cert_config->location = CONFIG_TYPE_EAP;
219  }
220
221  const base::DictionaryValue* vpn = NULL;
222  network_config.GetDictionaryWithoutPathExpansion(network_config::kVPN, &vpn);
223  if (vpn) {
224    const base::DictionaryValue* openvpn = NULL;
225    vpn->GetDictionaryWithoutPathExpansion(vpn::kOpenVPN, &openvpn);
226    const base::DictionaryValue* ipsec = NULL;
227    vpn->GetDictionaryWithoutPathExpansion(vpn::kIPsec, &ipsec);
228    if (openvpn) {
229      dict_with_client_cert = openvpn;
230      cert_config->location = CONFIG_TYPE_OPENVPN;
231    } else if (ipsec) {
232      dict_with_client_cert = ipsec;
233      cert_config->location = CONFIG_TYPE_IPSEC;
234    } else {
235      return;
236    }
237  }
238
239  const base::DictionaryValue* ethernet = NULL;
240  network_config.GetDictionaryWithoutPathExpansion(network_config::kEthernet,
241                                                   &ethernet);
242  if (ethernet) {
243    const base::DictionaryValue* eap = NULL;
244    ethernet->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
245    if (!eap)
246      return;
247    dict_with_client_cert = eap;
248    cert_config->location = CONFIG_TYPE_EAP;
249  }
250
251  if (dict_with_client_cert)
252    GetClientCertTypeAndPattern(*dict_with_client_cert, cert_config);
253}
254
255bool IsCertificateConfigured(const ConfigType cert_config_type,
256                             const base::DictionaryValue& service_properties) {
257  // VPN certificate properties are read from the Provider dictionary.
258  const base::DictionaryValue* provider_properties = NULL;
259  service_properties.GetDictionaryWithoutPathExpansion(
260      shill::kProviderProperty, &provider_properties);
261  switch (cert_config_type) {
262    case CONFIG_TYPE_NONE:
263      return true;
264    case CONFIG_TYPE_OPENVPN:
265      // OpenVPN generally requires a passphrase and we don't know whether or
266      // not one is required, so always return false here.
267      return false;
268    case CONFIG_TYPE_IPSEC: {
269      if (!provider_properties)
270        return false;
271
272      std::string client_cert_id;
273      provider_properties->GetStringWithoutPathExpansion(
274          shill::kL2tpIpsecClientCertIdProperty, &client_cert_id);
275      return !client_cert_id.empty();
276    }
277    case CONFIG_TYPE_EAP: {
278      std::string cert_id = GetStringFromDictionary(
279          service_properties, shill::kEapCertIdProperty);
280      std::string key_id = GetStringFromDictionary(
281          service_properties, shill::kEapKeyIdProperty);
282      std::string identity = GetStringFromDictionary(
283          service_properties, shill::kEapIdentityProperty);
284      return !cert_id.empty() && !key_id.empty() && !identity.empty();
285    }
286  }
287  NOTREACHED();
288  return false;
289}
290
291}  // namespace client_cert
292
293}  // namespace chromeos
294