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