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 ðernet); 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