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