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 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 123void 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 182void 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 224void 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 261ClientCertConfig::ClientCertConfig() 262 : location(CONFIG_TYPE_NONE), 263 client_cert_type(onc::client_cert::kClientCertTypeNone) { 264} 265 266void 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 ðernet); 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 321bool 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