onc_certificate_importer_impl.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1// Copyright 2013 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/onc/onc_certificate_importer_impl.h" 6 7#include <cert.h> 8#include <keyhi.h> 9#include <pk11pub.h> 10 11#include "base/base64.h" 12#include "base/logging.h" 13#include "base/values.h" 14#include "chromeos/network/network_event_log.h" 15#include "chromeos/network/onc/onc_utils.h" 16#include "components/onc/onc_constants.h" 17#include "crypto/scoped_nss_types.h" 18#include "net/base/crypto_module.h" 19#include "net/base/net_errors.h" 20#include "net/cert/nss_cert_database.h" 21#include "net/cert/x509_certificate.h" 22 23#define ONC_LOG_WARNING(message) \ 24 NET_LOG_DEBUG("ONC Certificate Import Warning", message) 25#define ONC_LOG_ERROR(message) \ 26 NET_LOG_ERROR("ONC Certificate Import Error", message) 27 28namespace chromeos { 29namespace onc { 30 31CertificateImporterImpl::CertificateImporterImpl( 32 net::NSSCertDatabase* target_nssdb) 33 : target_nssdb_(target_nssdb) { 34 CHECK(target_nssdb); 35} 36 37bool CertificateImporterImpl::ImportCertificates( 38 const base::ListValue& certificates, 39 ::onc::ONCSource source, 40 net::CertificateList* onc_trusted_certificates) { 41 VLOG(2) << "ONC file has " << certificates.GetSize() << " certificates"; 42 43 // Web trust is only granted to certificates imported by the user. 44 bool allow_trust_imports = source == ::onc::ONC_SOURCE_USER_IMPORT; 45 if (!ParseAndStoreCertificates(allow_trust_imports, 46 certificates, 47 onc_trusted_certificates, 48 NULL)) { 49 LOG(ERROR) << "Cannot parse some of the certificates in the ONC from " 50 << onc::GetSourceAsString(source); 51 return false; 52 } 53 return true; 54} 55 56bool CertificateImporterImpl::ParseAndStoreCertificates( 57 bool allow_trust_imports, 58 const base::ListValue& certificates, 59 net::CertificateList* onc_trusted_certificates, 60 CertsByGUID* imported_server_and_ca_certs) { 61 bool success = true; 62 for (size_t i = 0; i < certificates.GetSize(); ++i) { 63 const base::DictionaryValue* certificate = NULL; 64 certificates.GetDictionary(i, &certificate); 65 DCHECK(certificate != NULL); 66 67 VLOG(2) << "Parsing certificate at index " << i << ": " << *certificate; 68 69 if (!ParseAndStoreCertificate(allow_trust_imports, 70 *certificate, 71 onc_trusted_certificates, 72 imported_server_and_ca_certs)) { 73 success = false; 74 ONC_LOG_ERROR( 75 base::StringPrintf("Cannot parse certificate at index %zu", i)); 76 } else { 77 VLOG(2) << "Successfully imported certificate at index " << i; 78 } 79 } 80 return success; 81} 82 83// static 84void CertificateImporterImpl::ListCertsWithNickname( 85 const std::string& label, 86 net::CertificateList* result, 87 net::NSSCertDatabase* target_nssdb) { 88 net::CertificateList all_certs; 89 // TODO(tbarzic): Use async |ListCerts|. 90 target_nssdb->ListCertsSync(&all_certs); 91 result->clear(); 92 for (net::CertificateList::iterator iter = all_certs.begin(); 93 iter != all_certs.end(); ++iter) { 94 if (iter->get()->os_cert_handle()->nickname) { 95 // Separate the nickname stored in the certificate at the colon, since 96 // NSS likes to store it as token:nickname. 97 const char* delimiter = 98 ::strchr(iter->get()->os_cert_handle()->nickname, ':'); 99 if (delimiter) { 100 ++delimiter; // move past the colon. 101 if (strcmp(delimiter, label.c_str()) == 0) { 102 result->push_back(*iter); 103 continue; 104 } 105 } 106 } 107 // Now we find the private key for this certificate and see if it has a 108 // nickname that matches. If there is a private key, and it matches, 109 // then this is a client cert that we are looking for. 110 SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert( 111 iter->get()->os_cert_handle()->slot, 112 iter->get()->os_cert_handle(), 113 NULL); // wincx 114 if (private_key) { 115 char* private_key_nickname = PK11_GetPrivateKeyNickname(private_key); 116 if (private_key_nickname && std::string(label) == private_key_nickname) 117 result->push_back(*iter); 118 PORT_Free(private_key_nickname); 119 SECKEY_DestroyPrivateKey(private_key); 120 } 121 } 122} 123 124// static 125bool CertificateImporterImpl::DeleteCertAndKeyByNickname( 126 const std::string& label, 127 net::NSSCertDatabase* target_nssdb) { 128 net::CertificateList cert_list; 129 ListCertsWithNickname(label, &cert_list, target_nssdb); 130 bool result = true; 131 for (net::CertificateList::iterator iter = cert_list.begin(); 132 iter != cert_list.end(); ++iter) { 133 // If we fail, we try and delete the rest still. 134 // TODO(gspencer): this isn't very "transactional". If we fail on some, but 135 // not all, then it's possible to leave things in a weird state. 136 // Luckily there should only be one cert with a particular 137 // label, and the cert not being found is one of the few reasons the 138 // delete could fail, but still... The other choice is to return 139 // failure immediately, but that doesn't seem to do what is intended. 140 if (!target_nssdb->DeleteCertAndKey(iter->get())) 141 result = false; 142 } 143 return result; 144} 145 146bool CertificateImporterImpl::ParseAndStoreCertificate( 147 bool allow_trust_imports, 148 const base::DictionaryValue& certificate, 149 net::CertificateList* onc_trusted_certificates, 150 CertsByGUID* imported_server_and_ca_certs) { 151 // Get out the attributes of the given certificate. 152 std::string guid; 153 certificate.GetStringWithoutPathExpansion(::onc::certificate::kGUID, &guid); 154 DCHECK(!guid.empty()); 155 156 bool remove = false; 157 if (certificate.GetBooleanWithoutPathExpansion(::onc::kRemove, &remove) && 158 remove) { 159 if (!DeleteCertAndKeyByNickname(guid, target_nssdb_)) { 160 ONC_LOG_ERROR("Unable to delete certificate"); 161 return false; 162 } else { 163 return true; 164 } 165 } 166 167 // Not removing, so let's get the data we need to add this certificate. 168 std::string cert_type; 169 certificate.GetStringWithoutPathExpansion(::onc::certificate::kType, 170 &cert_type); 171 if (cert_type == ::onc::certificate::kServer || 172 cert_type == ::onc::certificate::kAuthority) { 173 return ParseServerOrCaCertificate(allow_trust_imports, 174 cert_type, 175 guid, 176 certificate, 177 onc_trusted_certificates, 178 imported_server_and_ca_certs); 179 } else if (cert_type == ::onc::certificate::kClient) { 180 return ParseClientCertificate(guid, certificate); 181 } 182 183 NOTREACHED(); 184 return false; 185} 186 187bool CertificateImporterImpl::ParseServerOrCaCertificate( 188 bool allow_trust_imports, 189 const std::string& cert_type, 190 const std::string& guid, 191 const base::DictionaryValue& certificate, 192 net::CertificateList* onc_trusted_certificates, 193 CertsByGUID* imported_server_and_ca_certs) { 194 bool web_trust_flag = false; 195 const base::ListValue* trust_list = NULL; 196 if (certificate.GetListWithoutPathExpansion(::onc::certificate::kTrustBits, 197 &trust_list)) { 198 for (base::ListValue::const_iterator it = trust_list->begin(); 199 it != trust_list->end(); ++it) { 200 std::string trust_type; 201 if (!(*it)->GetAsString(&trust_type)) 202 NOTREACHED(); 203 204 if (trust_type == ::onc::certificate::kWeb) { 205 // "Web" implies that the certificate is to be trusted for SSL 206 // identification. 207 web_trust_flag = true; 208 } else { 209 // Trust bits should only increase trust and never restrict. Thus, 210 // ignoring unknown bits should be safe. 211 ONC_LOG_WARNING("Certificate contains unknown trust type " + 212 trust_type); 213 } 214 } 215 } 216 217 bool import_with_ssl_trust = false; 218 if (web_trust_flag) { 219 if (!allow_trust_imports) 220 ONC_LOG_WARNING("Web trust not granted for certificate: " + guid); 221 else 222 import_with_ssl_trust = true; 223 } 224 225 std::string x509_data; 226 if (!certificate.GetStringWithoutPathExpansion(::onc::certificate::kX509, 227 &x509_data) || 228 x509_data.empty()) { 229 ONC_LOG_ERROR( 230 "Certificate missing appropriate certificate data for type: " + 231 cert_type); 232 return false; 233 } 234 235 scoped_refptr<net::X509Certificate> x509_cert = 236 DecodePEMCertificate(x509_data); 237 if (!x509_cert.get()) { 238 ONC_LOG_ERROR("Unable to create certificate from PEM encoding, type: " + 239 cert_type); 240 return false; 241 } 242 243 net::NSSCertDatabase::TrustBits trust = (import_with_ssl_trust ? 244 net::NSSCertDatabase::TRUSTED_SSL : 245 net::NSSCertDatabase::TRUST_DEFAULT); 246 247 if (x509_cert->os_cert_handle()->isperm) { 248 net::CertType net_cert_type = 249 cert_type == ::onc::certificate::kServer ? net::SERVER_CERT 250 : net::CA_CERT; 251 VLOG(1) << "Certificate is already installed."; 252 net::NSSCertDatabase::TrustBits missing_trust_bits = 253 trust & ~target_nssdb_->GetCertTrust(x509_cert.get(), net_cert_type); 254 if (missing_trust_bits) { 255 std::string error_reason; 256 bool success = false; 257 if (target_nssdb_->IsReadOnly(x509_cert.get())) { 258 error_reason = " Certificate is stored read-only."; 259 } else { 260 success = target_nssdb_->SetCertTrust(x509_cert.get(), 261 net_cert_type, 262 trust); 263 } 264 if (!success) { 265 ONC_LOG_ERROR("Certificate of type " + cert_type + 266 " was already present, but trust couldn't be set." + 267 error_reason); 268 } 269 } 270 } else { 271 net::CertificateList cert_list; 272 cert_list.push_back(x509_cert); 273 net::NSSCertDatabase::ImportCertFailureList failures; 274 bool success = false; 275 if (cert_type == ::onc::certificate::kServer) 276 success = target_nssdb_->ImportServerCert(cert_list, trust, &failures); 277 else // Authority cert 278 success = target_nssdb_->ImportCACerts(cert_list, trust, &failures); 279 280 if (!failures.empty()) { 281 ONC_LOG_ERROR( 282 base::StringPrintf("Error ( %s ) importing %s certificate", 283 net::ErrorToString(failures[0].net_error), 284 cert_type.c_str())); 285 return false; 286 } 287 288 if (!success) { 289 ONC_LOG_ERROR("Unknown error importing " + cert_type + " certificate."); 290 return false; 291 } 292 } 293 294 if (web_trust_flag && onc_trusted_certificates) 295 onc_trusted_certificates->push_back(x509_cert); 296 297 if (imported_server_and_ca_certs) 298 (*imported_server_and_ca_certs)[guid] = x509_cert; 299 300 return true; 301} 302 303bool CertificateImporterImpl::ParseClientCertificate( 304 const std::string& guid, 305 const base::DictionaryValue& certificate) { 306 std::string pkcs12_data; 307 if (!certificate.GetStringWithoutPathExpansion(::onc::certificate::kPKCS12, 308 &pkcs12_data) || 309 pkcs12_data.empty()) { 310 ONC_LOG_ERROR("PKCS12 data is missing for client certificate."); 311 return false; 312 } 313 314 std::string decoded_pkcs12; 315 if (!base::Base64Decode(pkcs12_data, &decoded_pkcs12)) { 316 ONC_LOG_ERROR( 317 "Unable to base64 decode PKCS#12 data: \"" + pkcs12_data + "\"."); 318 return false; 319 } 320 321 // Since this has a private key, always use the private module. 322 crypto::ScopedPK11Slot private_slot(target_nssdb_->GetPrivateSlot()); 323 if (!private_slot) 324 return false; 325 scoped_refptr<net::CryptoModule> module( 326 net::CryptoModule::CreateFromHandle(private_slot.get())); 327 net::CertificateList imported_certs; 328 329 int import_result = target_nssdb_->ImportFromPKCS12( 330 module.get(), decoded_pkcs12, base::string16(), false, &imported_certs); 331 if (import_result != net::OK) { 332 ONC_LOG_ERROR( 333 base::StringPrintf("Unable to import client certificate (error %s)", 334 net::ErrorToString(import_result))); 335 return false; 336 } 337 338 if (imported_certs.size() == 0) { 339 ONC_LOG_WARNING("PKCS12 data contains no importable certificates."); 340 return true; 341 } 342 343 if (imported_certs.size() != 1) { 344 ONC_LOG_WARNING("ONC File: PKCS12 data contains more than one certificate. " 345 "Only the first one will be imported."); 346 } 347 348 scoped_refptr<net::X509Certificate> cert_result = imported_certs[0]; 349 350 // Find the private key associated with this certificate, and set the 351 // nickname on it. 352 SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert( 353 cert_result->os_cert_handle()->slot, 354 cert_result->os_cert_handle(), 355 NULL); // wincx 356 if (private_key) { 357 PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str())); 358 SECKEY_DestroyPrivateKey(private_key); 359 } else { 360 ONC_LOG_WARNING("Unable to find private key for certificate."); 361 } 362 return true; 363} 364 365} // namespace onc 366} // namespace chromeos 367