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