diff options
-rw-r--r-- | chrome/browser/certificate_manager_model.cc | 2 | ||||
-rw-r--r-- | chrome/browser/chromeos/cros/network_library.cc | 3 | ||||
-rw-r--r-- | chrome/browser/chromeos/cros/onc_network_parser.cc | 165 | ||||
-rw-r--r-- | chrome/browser/chromeos/cros/onc_network_parser.h | 34 | ||||
-rw-r--r-- | chrome/browser/chromeos/cros/onc_network_parser_unittest.cc | 189 | ||||
-rw-r--r-- | net/base/cert_database.h | 16 | ||||
-rw-r--r-- | net/base/cert_database_nss.cc | 37 | ||||
-rw-r--r-- | net/base/cert_database_nss_unittest.cc | 24 | ||||
-rw-r--r-- | net/base/cert_database_openssl.cc | 7 | ||||
-rw-r--r-- | net/base/x509_certificate.h | 43 | ||||
-rw-r--r-- | net/base/x509_certificate_nss.cc | 101 | ||||
-rw-r--r-- | net/third_party/mozilla_security_manager/nsNSSCertificateDB.cpp | 38 | ||||
-rw-r--r-- | net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp | 93 | ||||
-rw-r--r-- | net/third_party/mozilla_security_manager/nsPKCS12Blob.h | 8 |
14 files changed, 559 insertions, 201 deletions
diff --git a/chrome/browser/certificate_manager_model.cc b/chrome/browser/certificate_manager_model.cc index e722ad8..6989f72 100644 --- a/chrome/browser/certificate_manager_model.cc +++ b/chrome/browser/certificate_manager_model.cc @@ -115,7 +115,7 @@ int CertificateManagerModel::ImportFromPKCS12(net::CryptoModule* module, const string16& password, bool is_extractable) { int result = cert_db_.ImportFromPKCS12(module, data, password, - is_extractable); + is_extractable, NULL); if (result == net::OK) Refresh(); return result; diff --git a/chrome/browser/chromeos/cros/network_library.cc b/chrome/browser/chromeos/cros/network_library.cc index 787a5c1..43acb7c 100644 --- a/chrome/browser/chromeos/cros/network_library.cc +++ b/chrome/browser/chromeos/cros/network_library.cc @@ -39,6 +39,7 @@ #include "content/public/browser/browser_thread.h" #include "crypto/nss_util.h" // crypto::GetTPMTokenInfo() for 802.1X and VPN. #include "grit/generated_resources.h" +#include "net/base/x509_certificate.h" #include "third_party/cros_system_api/dbus/service_constants.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/text/bytes_formatting.h" @@ -2839,7 +2840,7 @@ bool NetworkLibraryImplBase::LoadOncNetworks(const std::string& onc_blob, for (int i = 0; i < parser.GetCertificatesSize(); i++) { // Insert each of the available certs into the certificate DB. - if (!parser.ParseCertificate(i)) { + if (parser.ParseCertificate(i).get() == NULL) { DLOG(WARNING) << "Cannot parse certificate in ONC file"; return false; } diff --git a/chrome/browser/chromeos/cros/onc_network_parser.cc b/chrome/browser/chromeos/cros/onc_network_parser.cc index 19ed94d..55f8e9a 100644 --- a/chrome/browser/chromeos/cros/onc_network_parser.cc +++ b/chrome/browser/chromeos/cros/onc_network_parser.cc @@ -4,6 +4,9 @@ #include "chrome/browser/chromeos/cros/onc_network_parser.h" +#include <pk11pub.h> +#include <keyhi.h> + #include "base/base64.h" #include "base/json/json_value_serializer.h" #include "base/json/json_writer.h" // for debug output only. @@ -200,9 +203,8 @@ OncNetworkParser::OncNetworkParser(const std::string& onc_blob) root_dict_->GetList("Certificates", &certificates_); VLOG(2) << "ONC file has " << GetNetworkConfigsSize() << " networks and " << GetCertificatesSize() << " certificates"; - if (!has_network_configurations || !has_certificates) { - LOG(WARNING) << "ONC file has no NetworkConfigurations or Certificates."; - } + LOG_IF(WARNING, (!has_network_configurations && !has_certificates)) + << "ONC file has no NetworkConfigurations or Certificates."; } } @@ -228,7 +230,8 @@ int OncNetworkParser::GetCertificatesSize() const { return certificates_ ? certificates_->GetSize() : 0; } -bool OncNetworkParser::ParseCertificate(int cert_index) { +scoped_refptr<net::X509Certificate> OncNetworkParser::ParseCertificate( + int cert_index) { CHECK(certificates_); CHECK(static_cast<size_t>(cert_index) < certificates_->GetSize()); CHECK(cert_index >= 0); @@ -244,35 +247,39 @@ bool OncNetworkParser::ParseCertificate(int cert_index) { << ": " << certificate_json; } - // Get out the attributes of the given cert. + // Get out the attributes of the given certificate. std::string guid; bool remove = false; if (!certificate->GetString("GUID", &guid) || guid.empty()) { LOG(WARNING) << "ONC File: certificate missing identifier at index" << cert_index; - return false; + return NULL; } if (!certificate->GetBoolean("Remove", &remove)) remove = false; net::CertDatabase cert_database; - if (remove) - return cert_database.DeleteCertAndKeyByLabel(guid); + if (remove) { + bool success = DeleteCertAndKeyByNickname(guid); + DCHECK(success); + // TODO(gspencer): return removed certificate? + return NULL; + } - // Not removing, so let's get the data we need to add this cert. + // Not removing, so let's get the data we need to add this certificate. std::string cert_type; certificate->GetString("Type", &cert_type); if (cert_type == "Server" || cert_type == "Authority") { - return ParseServerOrCaCertificate(cert_index, cert_type, certificate); + return ParseServerOrCaCertificate(cert_index, cert_type, guid, certificate); } if (cert_type == "Client") { - return ParseClientCertificate(cert_index, certificate); + return ParseClientCertificate(cert_index, guid, certificate); } LOG(WARNING) << "ONC File: certificate of unknown type: " << cert_type << " at index " << cert_index; - return false; + return NULL; } Network* OncNetworkParser::ParseNetwork(int n) { @@ -350,9 +357,11 @@ std::string OncNetworkParser::GetGuidFromDictionary( return guid_string; } -bool OncNetworkParser::ParseServerOrCaCertificate( +scoped_refptr<net::X509Certificate> +OncNetworkParser::ParseServerOrCaCertificate( int cert_index, const std::string& cert_type, + const std::string& guid, base::DictionaryValue* certificate) { net::CertDatabase cert_database; bool web_trust = false; @@ -363,7 +372,7 @@ bool OncNetworkParser::ParseServerOrCaCertificate( if (!trust_list->GetString(i, &trust_type)) { LOG(WARNING) << "ONC File: certificate trust is invalid at index " << cert_index; - return false; + return NULL; } if (trust_type == "Web") { web_trust = true; @@ -371,7 +380,7 @@ bool OncNetworkParser::ParseServerOrCaCertificate( LOG(WARNING) << "ONC File: certificate contains unknown " << "trust type: " << trust_type << " at index " << cert_index; - return false; + return NULL; } } } @@ -381,23 +390,26 @@ bool OncNetworkParser::ParseServerOrCaCertificate( LOG(WARNING) << "ONC File: certificate missing appropriate " << "certificate data for type: " << cert_type << " at index " << cert_index; - return false; + return NULL; } std::string decoded_x509; if (!base::Base64Decode(x509_data, &decoded_x509)) { LOG(WARNING) << "Unable to base64 decode X509 data: \"" << x509_data << "\"."; - return false; + return NULL; } - scoped_refptr<net::X509Certificate> x509_cert( - net::X509Certificate::CreateFromBytes(decoded_x509.c_str(), - decoded_x509.size())); + scoped_refptr<net::X509Certificate> x509_cert = + net::X509Certificate::CreateFromBytesWithNickname( + decoded_x509.c_str(), + decoded_x509.size(), + guid.c_str()); if (!x509_cert.get()) { LOG(WARNING) << "Unable to create X509 certificate from bytes."; - return false; + return NULL; } + net::CertificateList cert_list; cert_list.push_back(x509_cert); net::CertDatabase::ImportCertFailureList failures; @@ -415,20 +427,22 @@ bool OncNetworkParser::ParseServerOrCaCertificate( << net::ErrorToString(failures[0].net_error) << ") importing " << cert_type << " certificate at index " << cert_index; - return false; + return NULL; } if (!success) { LOG(WARNING) << "ONC File: Unknown error importing " << cert_type << " certificate at index " << cert_index; - return false; + return NULL; } VLOG(2) << "Successfully imported server/ca certificate at index " << cert_index; - return true; + + return x509_cert; } -bool OncNetworkParser::ParseClientCertificate( +scoped_refptr<net::X509Certificate> OncNetworkParser::ParseClientCertificate( int cert_index, + const std::string& guid, base::DictionaryValue* certificate) { net::CertDatabase cert_database; std::string pkcs12_data; @@ -436,29 +450,60 @@ bool OncNetworkParser::ParseClientCertificate( pkcs12_data.empty()) { LOG(WARNING) << "ONC File: PKCS12 data is missing for Client " << "certificate at index " << cert_index; - return false; + return NULL; } std::string decoded_pkcs12; if (!base::Base64Decode(pkcs12_data, &decoded_pkcs12)) { LOG(WARNING) << "Unable to base64 decode PKCS#12 data: \"" << pkcs12_data << "\"."; - return false; + return NULL; } // Since this has a private key, always use the private module. scoped_refptr<net::CryptoModule> module(cert_database.GetPrivateModule()); + net::CertificateList imported_certs; + int result = cert_database.ImportFromPKCS12( - module.get(), decoded_pkcs12, string16(), false); + module.get(), decoded_pkcs12, string16(), false, &imported_certs); if (result != net::OK) { LOG(WARNING) << "ONC File: Unable to import Client certificate at index " << cert_index << " (error " << net::ErrorToString(result) << ")."; - return false; + return NULL; } + + if (imported_certs.size() == 0) { + LOG(WARNING) << "ONC File: PKCS12 data contains no importable certificates" + << " at index " << cert_index; + return NULL; + } + + if (imported_certs.size() != 1) { + LOG(WARNING) << "ONC File: PKCS12 data at index " << cert_index + << " contains more than one certificate. Only the first one will" + << " be imported."; + } + + scoped_refptr<net::X509Certificate> cert_result = imported_certs[0]; + + // Find the private key associated with this certificate, and set the + // nickname on it. + SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert( + cert_result->os_cert_handle()->slot, + cert_result->os_cert_handle(), + NULL); // wincx + if (private_key) { + PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str())); + SECKEY_DestroyPrivateKey(private_key); + } else { + LOG(WARNING) << "ONC File: Unable to find private key for cert at index" + << cert_index; + } + VLOG(2) << "Successfully imported client certificate at index " << cert_index; - return true; + return cert_result; } bool OncNetworkParser::ParseNestedObject(Network* network, @@ -588,6 +633,66 @@ bool OncNetworkParser::ParseNetworkConfigurationValue( return false; } +// static +bool OncNetworkParser::DeleteCertAndKeyByNickname(const std::string& label) { + net::CertificateList cert_list; + ListCertsWithNickname(label, &cert_list); + net::CertDatabase cert_db; + bool result = true; + for (net::CertificateList::iterator iter = cert_list.begin(); + iter != cert_list.end(); ++iter) { + // If we fail, we try and delete the rest still. + // TODO(gspencer): this isn't very "transactional". If we fail on some, but + // not all, then it's possible to leave things in a weird state. + // Luckily there should only be one cert with a particular + // label, and the cert not being found is one of the few reasons the + // delete could fail, but still... The other choice is to return + // failure immediately, but that doesn't seem to do what is intended. + if (!cert_db.DeleteCertAndKey(iter->get())) + result = false; + } + return result; +} + +// static +void OncNetworkParser::ListCertsWithNickname(const std::string& label, + net::CertificateList* result) { + net::CertificateList all_certs; + net::CertDatabase cert_db; + cert_db.ListCerts(&all_certs); + result->clear(); + for (net::CertificateList::iterator iter = all_certs.begin(); + iter != all_certs.end(); ++iter) { + if (iter->get()->os_cert_handle()->nickname) { + // Separate the nickname stored in the certificate at the colon, since + // NSS likes to store it as token:nickname. + const char* delimiter = + ::strchr(iter->get()->os_cert_handle()->nickname, ':'); + if (delimiter) { + delimiter++; // move past the colon. + if (strcmp(delimiter, label.c_str()) == 0) { + result->push_back(*iter); + continue; + } + } + } + // Now we find the private key for this certificate and see if it has a + // nickname that matches. If there is a private key, and it matches, + // then this is a client cert that we are looking for. + SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert( + iter->get()->os_cert_handle()->slot, + iter->get()->os_cert_handle(), + NULL); // wincx + if (private_key) { + char* private_key_nickname = PK11_GetPrivateKeyNickname(private_key); + if (private_key_nickname && private_key_nickname == label) + result->push_back(*iter); + PORT_Free(private_key_nickname); + SECKEY_DestroyPrivateKey(private_key); + } + } +} + // -------------------- OncWirelessNetworkParser -------------------- OncWirelessNetworkParser::OncWirelessNetworkParser() {} diff --git a/chrome/browser/chromeos/cros/onc_network_parser.h b/chrome/browser/chromeos/cros/onc_network_parser.h index 48fbe61..ee3bd65 100644 --- a/chrome/browser/chromeos/cros/onc_network_parser.h +++ b/chrome/browser/chromeos/cros/onc_network_parser.h @@ -9,6 +9,7 @@ #include <string> #include "base/compiler_specific.h" // for OVERRIDE +#include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "base/values.h" #include "chrome/browser/chromeos/cros/network_parser.h" @@ -19,6 +20,11 @@ class ListValue; class Value; } +namespace net { +class X509Certificate; +typedef std::vector<scoped_refptr<X509Certificate> > CertificateList; +} + namespace chromeos { // This is a simple representation of the signature of an ONC typed @@ -52,16 +58,17 @@ class OncNetworkParser : public NetworkParser { // Returns the number of networks in the "NetworkConfigs" list. int GetNetworkConfigsSize() const; - // Returns the number of certificates in the "Certificates" list. - int GetCertificatesSize() const; - // Call to create the network by parsing network config in the nth position. // (0-based). Returns NULL if there's a parse error or if n is out of range. Network* ParseNetwork(int n); + // Returns the number of certificates in the "Certificates" list. + int GetCertificatesSize() const; + // Call to parse and import the nth certificate in the certificate - // list. Returns false on failure. - bool ParseCertificate(int n); + // list into the certificate store. Returns a NULL refptr if + // there's a parse error or if n is out of range. + scoped_refptr<net::X509Certificate> ParseCertificate(int n); virtual Network* CreateNetworkFromInfo(const std::string& service_path, const base::DictionaryValue& info) OVERRIDE; @@ -104,14 +111,27 @@ class OncNetworkParser : public NetworkParser { const std::string& onc_type); private: - bool ParseServerOrCaCertificate( + FRIEND_TEST_ALL_PREFIXES(OncNetworkParserTest, TestAddClientCertificate); + FRIEND_TEST_ALL_PREFIXES(OncNetworkParserTest, TestAddServerCertificate); + FRIEND_TEST_ALL_PREFIXES(OncNetworkParserTest, TestAddAuthorityCertificate); + scoped_refptr<net::X509Certificate> ParseServerOrCaCertificate( int cert_index, const std::string& cert_type, + const std::string& guid, base::DictionaryValue* certificate); - bool ParseClientCertificate( + scoped_refptr<net::X509Certificate> ParseClientCertificate( int cert_index, + const std::string& guid, base::DictionaryValue* certificate); + // This lists the certificates that have the string |label| as their + // certificate nickname (exact match). + static void ListCertsWithNickname(const std::string& label, + net::CertificateList* result); + // This deletes any certificate that has the string |label| as its + // nickname (exact match). + static bool DeleteCertAndKeyByNickname(const std::string& label); + // Error message from the JSON parser, if applicable. std::string parse_error_; diff --git a/chrome/browser/chromeos/cros/onc_network_parser_unittest.cc b/chrome/browser/chromeos/cros/onc_network_parser_unittest.cc index b2a2486..ec5907a 100644 --- a/chrome/browser/chromeos/cros/onc_network_parser_unittest.cc +++ b/chrome/browser/chromeos/cros/onc_network_parser_unittest.cc @@ -5,21 +5,27 @@ #include "chrome/browser/chromeos/cros/onc_network_parser.h" #include <cert.h> +#include <keyhi.h> #include <pk11pub.h> #include "base/lazy_instance.h" #include "base/scoped_temp_dir.h" #include "base/values.h" #include "chrome/browser/chromeos/cros/network_library.h" +#include "chrome/browser/chromeos/system/runtime_environment.h" #include "crypto/nss_util.h" #include "net/base/cert_database.h" +#include "net/base/cert_type.h" #include "net/base/crypto_module.h" #include "net/base/x509_certificate.h" +#include "net/third_party/mozilla_security_manager/nsNSSCertTrust.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/cros_system_api/dbus/service_constants.h" +namespace msm = mozilla_security_manager; namespace chromeos { +namespace { const char kNetworkConfigurationOpenVPN[] = " {" " \"GUID\": \"{408290ea-9299-4757-ab04-8957d55f0f13}\"," @@ -102,6 +108,21 @@ const char kCertificateWebAuthority[] = "1kohau6FauQx87by5NIRPdkNPvkQ==\"" " }"; +const char g_token_name[] = "OncNetworkParserTest token"; + +net::CertType GetCertType(const net::X509Certificate* cert) { + DCHECK(cert); + msm::nsNSSCertTrust trust(cert->os_cert_handle()->trust); + if (trust.HasAnyUser()) + return net::USER_CERT; + if (trust.HasAnyCA() || CERT_IsCACert(cert->os_cert_handle(), NULL)) + return net::CA_CERT; + if (trust.HasPeer(PR_TRUE, PR_FALSE, PR_FALSE)) + return net::SERVER_CERT; + return net::UNKNOWN_CERT; +} + +} // namespace class OncNetworkParserTest : public testing::Test { public: @@ -113,8 +134,7 @@ class OncNetworkParserTest : public testing::Test { // it once, and empty it for each test case. Here's the bug: // https://bugzilla.mozilla.org/show_bug.cgi?id=588269 ASSERT_TRUE( - crypto::OpenTestNSSDB(temp_db_dir_.Get().path(), - "OncNetworkParserTest db")); + crypto::OpenTestNSSDB(temp_db_dir_.Get().path(), g_token_name)); } static void TearDownTestCase() { @@ -446,47 +466,6 @@ TEST_F(OncNetworkParserTest, TestCreateNetworkL2TPIPsec) { EXPECT_FALSE(vpn->save_credentials()); } -TEST_F(OncNetworkParserTest, TestAddServerCertificate) { - std::string test_blob( - "{" - " \"Certificates\": [" - " {" - " \"GUID\": \"{f998f760-272b-6939-4c2beffe428697aa}\"," - " \"Type\": \"Server\"," - " \"X509\": \"MIICWDCCAcECAxAAATANBgkqhkiG9w0BAQQFADCBkzEVM" - "BMGA1UEChMMR29vZ2xlLCBJbmMuMREwDwYDVQQLEwhDaHJvbWVPUzEiMCAGCSqGSIb3DQ" - "EJARYTZ3NwZW5jZXJAZ29vZ2xlLmNvbTEaMBgGA1UEBxMRTW91bnRhaW4gVmlldywgQ0E" - "xCzAJBgNVBAgTAkNBMQswCQYDVQQGEwJVUzENMAsGA1UEAxMEbG1hbzAeFw0xMTAzMTYy" - "MzQ5MzhaFw0xMjAzMTUyMzQ5MzhaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVM" - "BMGA1UEChMMR29vZ2xlLCBJbmMuMREwDwYDVQQLEwhDaHJvbWVPUzENMAsGA1UEAxMEbG" - "1hbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA31WiJ9LvprrhKtDlW0RdLFAO7Qj" - "kvs+sG6j2Vp2aBSrlhALG/0BVHUhWi4F/HHJho+ncLHAg5AGO0sdAjYUdQG6tfPqjLsIA" - "LtoKEZZdFe/JhmqOEaxWsSdu2S2RdPgCQOsP79EH58gXwu2gejCkJDmU22WL4YLuqOc17" - "nxbDC8CAwEAATANBgkqhkiG9w0BAQQFAAOBgQCv4vMD+PMlfnftu4/6Yf/oMLE8yCOqZT" - "Q/dWCxB9PiJnOefiBeSzSZE6Uv3G7qnblZPVZaFeJMd+ostt0viCyPucFsFgLMyyoV1dM" - "VPVwJT5Iq1AHehWXnTBbxUK9wioA5jOEKdroKjuSSsg/Q8Wx6cpJmttQz5olGPgstmACR" - "WA==\"" - " }" - " ]," - "}"); - OncNetworkParser parser(test_blob); - - EXPECT_EQ(1, parser.GetCertificatesSize()); - EXPECT_TRUE(parser.ParseCertificate(0)); -} - -TEST_F(OncNetworkParserTest, TestAddAuthorityCertificate) { - const std::string test_blob(std::string("{" - " \"Certificates\": [") + - std::string(kCertificateWebAuthority) + std::string( - " ]," - "}")); - OncNetworkParser parser(test_blob); - - EXPECT_EQ(1, parser.GetCertificatesSize()); - EXPECT_TRUE(parser.ParseCertificate(0)); -} - TEST_F(OncNetworkParserTest, TestAddClientCertificate) { std::string test_blob( "{" @@ -529,10 +508,130 @@ TEST_F(OncNetworkParserTest, TestAddClientCertificate) { " }" " ]," "}"); + std::string test_guid("{f998f760-272b-6939-4c2beffe428697ac}"); OncNetworkParser parser(test_blob); + ASSERT_EQ(1, parser.GetCertificatesSize()); + + scoped_refptr<net::X509Certificate> cert = parser.ParseCertificate(0).get(); + EXPECT_TRUE(cert.get() != NULL); + EXPECT_EQ(net::USER_CERT, GetCertType(cert.get())); + + EXPECT_STREQ(test_guid.c_str(), + cert->GetDefaultNickname(net::USER_CERT).c_str()); + net::CertificateList result_list; + OncNetworkParser::ListCertsWithNickname(test_guid, &result_list); + ASSERT_EQ(1ul, result_list.size()); + EXPECT_EQ(net::USER_CERT, GetCertType(result_list[0].get())); + + SECKEYPrivateKeyList* privkey_list = + PK11_ListPrivKeysInSlot(slot_->os_module_handle(), NULL, NULL); + EXPECT_TRUE(privkey_list); + if (privkey_list) { + SECKEYPrivateKeyListNode* node = PRIVKEY_LIST_HEAD(privkey_list); + int count = 0; + while (!PRIVKEY_LIST_END(node, privkey_list)) { + char* name = PK11_GetPrivateKeyNickname(node->key); + EXPECT_STREQ(test_guid.c_str(), name); + PORT_Free(name); + count++; + node = PRIVKEY_LIST_NEXT(node); + } + EXPECT_EQ(1, count); + SECKEY_DestroyPrivateKeyList(privkey_list); + } + + SECKEYPublicKeyList* pubkey_list = + PK11_ListPublicKeysInSlot(slot_->os_module_handle(), NULL); + EXPECT_TRUE(pubkey_list); + if (pubkey_list) { + SECKEYPublicKeyListNode* node = PUBKEY_LIST_HEAD(pubkey_list); + int count = 0; + while (!PUBKEY_LIST_END(node, pubkey_list)) { + count++; + node = PUBKEY_LIST_NEXT(node); + } + EXPECT_EQ(1, count); + SECKEY_DestroyPublicKeyList(pubkey_list); + } +} + +TEST_F(OncNetworkParserTest, TestAddServerCertificate) { + std::string test_blob( + "{" + " \"Certificates\": [" + " {" + " \"GUID\": \"{f998f760-272b-6939-4c2beffe428697aa}\"," + " \"Type\": \"Server\"," + " \"X509\": \"MIICWDCCAcECAxAAATANBgkqhkiG9w0BAQQFADCBkzEVM" + "BMGA1UEChMMR29vZ2xlLCBJbmMuMREwDwYDVQQLEwhDaHJvbWVPUzEiMCAGCSqGSIb3DQ" + "EJARYTZ3NwZW5jZXJAZ29vZ2xlLmNvbTEaMBgGA1UEBxMRTW91bnRhaW4gVmlldywgQ0E" + "xCzAJBgNVBAgTAkNBMQswCQYDVQQGEwJVUzENMAsGA1UEAxMEbG1hbzAeFw0xMTAzMTYy" + "MzQ5MzhaFw0xMjAzMTUyMzQ5MzhaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVM" + "BMGA1UEChMMR29vZ2xlLCBJbmMuMREwDwYDVQQLEwhDaHJvbWVPUzENMAsGA1UEAxMEbG" + "1hbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA31WiJ9LvprrhKtDlW0RdLFAO7Qj" + "kvs+sG6j2Vp2aBSrlhALG/0BVHUhWi4F/HHJho+ncLHAg5AGO0sdAjYUdQG6tfPqjLsIA" + "LtoKEZZdFe/JhmqOEaxWsSdu2S2RdPgCQOsP79EH58gXwu2gejCkJDmU22WL4YLuqOc17" + "nxbDC8CAwEAATANBgkqhkiG9w0BAQQFAAOBgQCv4vMD+PMlfnftu4/6Yf/oMLE8yCOqZT" + "Q/dWCxB9PiJnOefiBeSzSZE6Uv3G7qnblZPVZaFeJMd+ostt0viCyPucFsFgLMyyoV1dM" + "VPVwJT5Iq1AHehWXnTBbxUK9wioA5jOEKdroKjuSSsg/Q8Wx6cpJmttQz5olGPgstmACR" + "WA==\"" + " }" + " ]," + "}"); + std::string test_guid("{f998f760-272b-6939-4c2beffe428697aa}"); + OncNetworkParser parser(test_blob); + ASSERT_EQ(1, parser.GetCertificatesSize()); + + scoped_refptr<net::X509Certificate> cert = parser.ParseCertificate(0).get(); + EXPECT_TRUE(cert.get() != NULL); + EXPECT_EQ(net::SERVER_CERT, GetCertType(cert.get())); + + EXPECT_STREQ(test_guid.c_str(), + cert->GetDefaultNickname(net::SERVER_CERT).c_str()); + net::CertificateList result_list; + OncNetworkParser::ListCertsWithNickname(test_guid, &result_list); + ASSERT_EQ(1ul, result_list.size()); + EXPECT_EQ(net::SERVER_CERT, GetCertType(result_list[0].get())); + + SECKEYPrivateKeyList* privkey_list = + PK11_ListPrivKeysInSlot(slot_->os_module_handle(), NULL, NULL); + EXPECT_FALSE(privkey_list); + + SECKEYPublicKeyList* pubkey_list = + PK11_ListPublicKeysInSlot(slot_->os_module_handle(), NULL); + EXPECT_FALSE(pubkey_list); + +} + +TEST_F(OncNetworkParserTest, TestAddAuthorityCertificate) { + const std::string test_blob(std::string("{" + " \"Certificates\": [") + + std::string(kCertificateWebAuthority) + std::string( + " ]," + "}")); + std::string test_guid("{f998f760-272b-6939-4c2beffe428697ab}"); + OncNetworkParser parser(test_blob); + ASSERT_EQ(1, parser.GetCertificatesSize()); + + scoped_refptr<net::X509Certificate> cert = parser.ParseCertificate(0).get(); + EXPECT_TRUE(cert.get() != NULL); + EXPECT_EQ(net::CA_CERT, GetCertType(cert.get())); + + EXPECT_STREQ(test_guid.c_str(), + cert->GetDefaultNickname(net::CA_CERT).c_str()); + net::CertificateList result_list; + OncNetworkParser::ListCertsWithNickname(test_guid, &result_list); + ASSERT_EQ(1ul, result_list.size()); + EXPECT_EQ(net::CA_CERT, GetCertType(result_list[0].get())); + + SECKEYPrivateKeyList* privkey_list = + PK11_ListPrivKeysInSlot(slot_->os_module_handle(), NULL, NULL); + EXPECT_FALSE(privkey_list); + + SECKEYPublicKeyList* pubkey_list = + PK11_ListPublicKeysInSlot(slot_->os_module_handle(), NULL); + EXPECT_FALSE(pubkey_list); - EXPECT_EQ(1, parser.GetCertificatesSize()); - EXPECT_TRUE(parser.ParseCertificate(0)); } TEST_F(OncNetworkParserTest, TestNetworkAndCertificate) { diff --git a/net/base/cert_database.h b/net/base/cert_database.h index a735e693..cfd670c 100644 --- a/net/base/cert_database.h +++ b/net/base/cert_database.h @@ -99,8 +99,8 @@ class NET_EXPORT CertDatabase { int AddUserCert(X509Certificate* cert); #if defined(USE_NSS) || defined(USE_OPENSSL) - // Get a list of unique certificates in the certificate database. (One - // instance of all certificates.) + // Get a list of unique certificates in the certificate database (one + // instance of all certificates). void ListCerts(CertificateList* certs); // Get the default module for public key data. @@ -119,11 +119,13 @@ class NET_EXPORT CertDatabase { // If |is_extractable| is false, mark the private key as being unextractable // from the module. // Returns OK or a network error code such as ERR_PKCS12_IMPORT_BAD_PASSWORD - // or ERR_PKCS12_IMPORT_ERROR. + // or ERR_PKCS12_IMPORT_ERROR. |imported_certs|, if non-NULL, returns a list + // of certs that were imported. int ImportFromPKCS12(CryptoModule* module, const std::string& data, const string16& password, - bool is_extractable); + bool is_extractable, + CertificateList* imported_certs); // Export the given certificates and private keys into a PKCS #12 blob, // storing into |output|. @@ -178,12 +180,6 @@ class NET_EXPORT CertDatabase { // success. bool DeleteCertAndKey(const X509Certificate* cert); - // Delete the certificate and associated public and private key (if - // one exists) with the given label from the database. Returns true - // on success. ("label" here refers to the NSS Attribute CKA_LABEL, - // also referred to as a nickname or friendly name). - bool DeleteCertAndKeyByLabel(const std::string& label); - // Check whether cert is stored in a readonly slot. bool IsReadOnly(const X509Certificate* cert) const; #endif diff --git a/net/base/cert_database_nss.cc b/net/base/cert_database_nss.cc index 817372d..6781917 100644 --- a/net/base/cert_database_nss.cc +++ b/net/base/cert_database_nss.cc @@ -66,28 +66,12 @@ int CertDatabase::AddUserCert(X509Certificate* cert_obj) { PK11SlotInfo* slot = NULL; std::string nickname; - // Create a nickname for this certificate. - // We use the scheme used by Firefox: - // --> <subject's common name>'s <issuer's common name> ID. - - std::string username, ca_name; - char* temp_username = CERT_GetCommonName(&cert->subject); - char* temp_ca_name = CERT_GetCommonName(&cert->issuer); - if (temp_username) { - username = temp_username; - PORT_Free(temp_username); - } - if (temp_ca_name) { - ca_name = temp_ca_name; - PORT_Free(temp_ca_name); - } - nickname = username + "'s " + ca_name + " ID"; - { crypto::AutoNSSWriteLock lock; - slot = PK11_ImportCertForKey(cert, - const_cast<char*>(nickname.c_str()), - NULL); + slot = PK11_ImportCertForKey( + cert, + cert_obj->GetDefaultNickname(net::USER_CERT).c_str(), + NULL); } if (!slot) { @@ -161,11 +145,13 @@ int CertDatabase::ImportFromPKCS12( CryptoModule* module, const std::string& data, const string16& password, - bool is_extractable) { + bool is_extractable, + net::CertificateList* imported_certs) { int result = psm::nsPKCS12Blob_Import(module->os_module_handle(), data.data(), data.size(), password, - is_extractable); + is_extractable, + imported_certs); if (result == net::OK) CertDatabase::NotifyObserversOfUserCertAdded(NULL); @@ -328,13 +314,6 @@ bool CertDatabase::DeleteCertAndKey(const X509Certificate* cert) { return true; } -bool CertDatabase::DeleteCertAndKeyByLabel(const std::string& label) { - // TODO(gspencer):Find the certificate with the given CKA_LABEL - // (nickname), and delete it. - NOTIMPLEMENTED(); - return false; -} - bool CertDatabase::IsReadOnly(const X509Certificate* cert) const { PK11SlotInfo* slot = cert->os_cert_handle()->slot; return slot && PK11_IsReadOnly(slot); diff --git a/net/base/cert_database_nss_unittest.cc b/net/base/cert_database_nss_unittest.cc index 1129544..c76b5bb 100644 --- a/net/base/cert_database_nss_unittest.cc +++ b/net/base/cert_database_nss_unittest.cc @@ -154,7 +154,8 @@ TEST_F(CertDatabaseNSSTest, ImportFromPKCS12WrongPassword) { cert_db_.ImportFromPKCS12(slot_, pkcs12_data, string16(), - true)); // is_extractable + true, // is_extractable + NULL)); // Test db should still be empty. EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size()); @@ -166,7 +167,8 @@ TEST_F(CertDatabaseNSSTest, ImportFromPKCS12AsExtractableAndExportAgain) { EXPECT_EQ(OK, cert_db_.ImportFromPKCS12(slot_, pkcs12_data, ASCIIToUTF16("12345"), - true)); // is_extractable + true, // is_extractable + NULL)); CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); @@ -189,7 +191,8 @@ TEST_F(CertDatabaseNSSTest, ImportFromPKCS12Twice) { EXPECT_EQ(OK, cert_db_.ImportFromPKCS12(slot_, pkcs12_data, ASCIIToUTF16("12345"), - true)); // is_extractable + true, // is_extractable + NULL)); EXPECT_EQ(1U, ListCertsInSlot(slot_->os_module_handle()).size()); // NSS has a SEC_ERROR_PKCS12_DUPLICATE_DATA error, but it doesn't look like @@ -197,7 +200,8 @@ TEST_F(CertDatabaseNSSTest, ImportFromPKCS12Twice) { EXPECT_EQ(OK, cert_db_.ImportFromPKCS12(slot_, pkcs12_data, ASCIIToUTF16("12345"), - true)); // is_extractable + true, // is_extractable + NULL)); EXPECT_EQ(1U, ListCertsInSlot(slot_->os_module_handle()).size()); } @@ -207,7 +211,8 @@ TEST_F(CertDatabaseNSSTest, ImportFromPKCS12AsUnextractableAndExportAgain) { EXPECT_EQ(OK, cert_db_.ImportFromPKCS12(slot_, pkcs12_data, ASCIIToUTF16("12345"), - false)); // is_extractable + false, // is_extractable + NULL)); CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); @@ -228,7 +233,8 @@ TEST_F(CertDatabaseNSSTest, ImportFromPKCS12OnlyMarkIncludedKey) { EXPECT_EQ(OK, cert_db_.ImportFromPKCS12(slot_, pkcs12_data, ASCIIToUTF16("12345"), - true)); // is_extractable + true, // is_extractable + NULL)); CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); @@ -238,7 +244,8 @@ TEST_F(CertDatabaseNSSTest, ImportFromPKCS12OnlyMarkIncludedKey) { EXPECT_EQ(OK, cert_db_.ImportFromPKCS12(slot_, pkcs12_data, ASCIIToUTF16("12345"), - false)); // is_extractable + false, // is_extractable + NULL)); cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); @@ -257,7 +264,8 @@ TEST_F(CertDatabaseNSSTest, ImportFromPKCS12InvalidFile) { cert_db_.ImportFromPKCS12(slot_, pkcs12_data, string16(), - true)); // is_extractable + true, // is_extractable + NULL)); // Test db should still be empty. EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size()); diff --git a/net/base/cert_database_openssl.cc b/net/base/cert_database_openssl.cc index 76bd3f8..ca429c08 100644 --- a/net/base/cert_database_openssl.cc +++ b/net/base/cert_database_openssl.cc @@ -82,13 +82,6 @@ bool CertDatabase::DeleteCertAndKey(const X509Certificate* cert) { return false; } -bool CertDatabase::DeleteCertAndKeyByLabel(const std::string& label) { - // TODO(gspencer):Find the certificate with the given label - // (nickname), and delete it. - NOTIMPLEMENTED(); - return false; -} - CertDatabase::TrustBits CertDatabase::GetCertTrust(const X509Certificate* cert, CertType type) const { // TODO(bulach): implement me. diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h index 6e155683..05e74b3 100644 --- a/net/base/x509_certificate.h +++ b/net/base/x509_certificate.h @@ -15,6 +15,7 @@ #include "base/memory/ref_counted.h" #include "base/string_piece.h" #include "base/time.h" +#include "net/base/cert_type.h" #include "net/base/net_export.h" #include "net/base/x509_cert_types.h" @@ -146,6 +147,24 @@ class NET_EXPORT X509Certificate // The returned pointer must be stored in a scoped_refptr<X509Certificate>. static X509Certificate* CreateFromBytes(const char* data, int length); +#if defined(USE_NSS) + // Create an X509Certificate from the DER-encoded representation. + // |nickname| can be NULL if an auto-generated nickname is desired. + // Returns NULL on failure. The returned pointer must be stored in a + // scoped_refptr<X509Certificate>. + // + // This function differs from CreateFromBytes in that it takes a + // nickname that will be used when the certificate is imported into PKCS#11. + static X509Certificate* CreateFromBytesWithNickname(const char* data, + int length, + const char* nickname); + + // The default nickname of the certificate, based on the certificate type + // passed in. If this object was created using CreateFromBytesWithNickname, + // then this will return the nickname specified upon creation. + std::string GetDefaultNickname(CertType type) const; +#endif + // Create an X509Certificate from the representation stored in the given // pickle. The data for this object is found relative to the given // pickle_iter, which should be passed to the pickle's various Read* methods. @@ -413,15 +432,27 @@ class NET_EXPORT X509Certificate // Returns true if two OSCertHandles refer to identical certificates. static bool IsSameOSCert(OSCertHandle a, OSCertHandle b); - // Creates an OS certificate handle from the BER-encoded representation. + // Creates an OS certificate handle from the DER-encoded representation. // Returns NULL on failure. static OSCertHandle CreateOSCertHandleFromBytes(const char* data, int length); +#if defined(USE_NSS) + // Creates an OS certificate handle from the DER-encoded representation. + // Returns NULL on failure. Sets the default nickname if |nickname| is + // non-NULL. + static OSCertHandle CreateOSCertHandleFromBytesWithNickname( + const char* data, + int length, + const char* nickname); +#endif + // Creates all possible OS certificate handles from |data| encoded in a // specific |format|. Returns an empty collection on failure. static OSCertHandles CreateOSCertHandlesFromBytes( - const char* data, int length, Format format); + const char* data, + int length, + Format format); // Duplicates (or adds a reference to) an OS certificate handle. static OSCertHandle DupOSCertHandle(OSCertHandle cert_handle); @@ -553,6 +584,14 @@ class NET_EXPORT X509Certificate // that may be needed for chain building. OSCertHandles intermediate_ca_certs_; +#if defined(USE_NSS) + // This stores any default nickname that has been set on the certificate + // at creation time with CreateFromBytesWithNickname. + // If this is empty, then GetDefaultNickname will return a generated name + // based on the type of the certificate. + std::string default_nickname_; +#endif + #if defined(OS_MACOSX) // Blocks multiple threads from verifying the cert simultaneously. // (Marked mutable because it's used in a const method.) diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc index bbb5cef..af7172c 100644 --- a/net/base/x509_certificate_nss.cc +++ b/net/base/x509_certificate_nss.cc @@ -6,6 +6,7 @@ #include <cert.h> #include <cryptohi.h> +#include <keyhi.h> #include <nss.h> #include <pk11pub.h> #include <prerror.h> @@ -685,6 +686,90 @@ void X509Certificate::Initialize() { } // static +X509Certificate* X509Certificate::CreateFromBytesWithNickname( + const char* data, + int length, + const char* nickname) { + OSCertHandle cert_handle = CreateOSCertHandleFromBytesWithNickname(data, + length, + nickname); + if (!cert_handle) + return NULL; + + X509Certificate* cert = CreateFromHandle(cert_handle, OSCertHandles()); + FreeOSCertHandle(cert_handle); + + if (nickname) + cert->default_nickname_ = nickname; + + return cert; +} + +std::string X509Certificate::GetDefaultNickname(CertType type) const { + if (!default_nickname_.empty()) + return default_nickname_; + + std::string result; + if (type == USER_CERT) { + // Find the private key for this certificate and see if it has a + // nickname. If there is a private key, and it has a nickname, then + // we return that nickname. + SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert( + cert_handle_->slot, + cert_handle_, + NULL); // wincx + if (private_key) { + char* private_key_nickname = PK11_GetPrivateKeyNickname(private_key); + if (private_key_nickname) { + result = private_key_nickname; + PORT_Free(private_key_nickname); + SECKEY_DestroyPrivateKey(private_key); + return result; + } + SECKEY_DestroyPrivateKey(private_key); + } + } + + switch (type) { + case CA_CERT: { + char* nickname = CERT_MakeCANickname(cert_handle_); + result = nickname; + PORT_Free(nickname); + break; + } + case USER_CERT: { + // Create a nickname for a user certificate. + // We use the scheme used by Firefox: + // --> <subject's common name>'s <issuer's common name> ID. + // TODO(gspencer): internationalize this: it's wrong to + // hard code English. + + std::string username, ca_name; + char* temp_username = CERT_GetCommonName( + &cert_handle_->subject); + char* temp_ca_name = CERT_GetCommonName(&cert_handle_->issuer); + if (temp_username) { + username = temp_username; + PORT_Free(temp_username); + } + if (temp_ca_name) { + ca_name = temp_ca_name; + PORT_Free(temp_ca_name); + } + result = username + "'s " + ca_name + " ID"; + break; + } + case SERVER_CERT: + result = subject_.GetDisplayName(); + break; + case UNKNOWN_CERT: + default: + break; + } + return result; +} + +// static X509Certificate* X509Certificate::CreateSelfSigned( crypto::RSAPrivateKey* key, const std::string& subject, @@ -922,6 +1007,15 @@ bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, // static X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( const char* data, int length) { + return CreateOSCertHandleFromBytesWithNickname(data, length, NULL); +} + +// static +X509Certificate::OSCertHandle +X509Certificate::CreateOSCertHandleFromBytesWithNickname( + const char* data, + int length, + const char* nickname) { if (length < 0) return NULL; @@ -936,13 +1030,16 @@ X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( der_cert.type = siDERCertBuffer; // Parse into a certificate structure. - return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert, NULL, + return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert, + const_cast<char*>(nickname), PR_FALSE, PR_TRUE); } // static X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( - const char* data, int length, Format format) { + const char* data, + int length, + Format format) { OSCertHandles results; if (length < 0) return results; diff --git a/net/third_party/mozilla_security_manager/nsNSSCertificateDB.cpp b/net/third_party/mozilla_security_manager/nsNSSCertificateDB.cpp index 3e50cd1..0cf430d 100644 --- a/net/third_party/mozilla_security_manager/nsNSSCertificateDB.cpp +++ b/net/third_party/mozilla_security_manager/nsNSSCertificateDB.cpp @@ -80,14 +80,12 @@ bool ImportCACerts(const net::CertificateList& certificates, // Mozilla uses CERT_AddTempCertToPerm, however it is privately exported, // and it doesn't take the slot as an argument either. Instead, we use // PK11_ImportCert and CERT_ChangeCertTrust. - char* nickname = CERT_MakeCANickname(root->os_cert_handle()); - if (!nickname) - return false; - SECStatus srv = PK11_ImportCert(slot.get(), root->os_cert_handle(), - CK_INVALID_HANDLE, - nickname, - PR_FALSE /* includeTrust (unused) */); - PORT_Free(nickname); + SECStatus srv = PK11_ImportCert( + slot.get(), + root->os_cert_handle(), + CK_INVALID_HANDLE, + root->GetDefaultNickname(net::CA_CERT).c_str(), + PR_FALSE /* includeTrust (unused) */); if (srv != SECSuccess) { LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError(); return false; @@ -139,14 +137,12 @@ bool ImportCACerts(const net::CertificateList& certificates, // Mozilla uses CERT_ImportCerts, which doesn't take a slot arg. We use // PK11_ImportCert instead. - char* nickname = CERT_MakeCANickname(cert->os_cert_handle()); - if (!nickname) - return false; - SECStatus srv = PK11_ImportCert(slot.get(), cert->os_cert_handle(), - CK_INVALID_HANDLE, - nickname, - PR_FALSE /* includeTrust (unused) */); - PORT_Free(nickname); + SECStatus srv = PK11_ImportCert( + slot.get(), + cert->os_cert_handle(), + CK_INVALID_HANDLE, + cert->GetDefaultNickname(net::CA_CERT).c_str(), + PR_FALSE /* includeTrust (unused) */); if (srv != SECSuccess) { LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError(); // TODO(mattm): Should we bail or continue on error here? Mozilla doesn't @@ -174,10 +170,12 @@ bool ImportServerCert(const net::CertificateList& certificates, // Mozilla uses CERT_ImportCerts, which doesn't take a slot arg. We use // PK11_ImportCert instead. - SECStatus srv = PK11_ImportCert(slot.get(), cert->os_cert_handle(), - CK_INVALID_HANDLE, - cert->subject().GetDisplayName().c_str(), - PR_FALSE /* includeTrust (unused) */); + SECStatus srv = PK11_ImportCert( + slot.get(), + cert->os_cert_handle(), + CK_INVALID_HANDLE, + cert->GetDefaultNickname(net::SERVER_CERT).c_str(), + PR_FALSE /* includeTrust (unused) */); if (srv != SECSuccess) { LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError(); not_imported->push_back(net::CertDatabase::ImportCertFailure( diff --git a/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp b/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp index 7781bf7..3087cec 100644 --- a/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp +++ b/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp @@ -57,7 +57,7 @@ namespace { // // For the NSS PKCS#12 library, must convert PRUnichars (shorts) to // a buffer of octets. Must handle byte order correctly. -// TODO: Is there a mozilla way to do this? In the string lib? +// TODO: Is there a Mozilla way to do this? In the string lib? void unicodeToItem(const PRUnichar *uni, SECItem *item) { int len = 0; @@ -138,7 +138,7 @@ pip_ucs2_ascii_conversion_fn(PRBool toUnicode, PRBool swapBytes) { CHECK_GE(maxOutBufLen, inBufLen); - // do a no-op, since I've already got unicode. Hah! + // do a no-op, since I've already got Unicode. Hah! *outBufLen = inBufLen; memcpy(outBuf, inBuf, inBufLen); return PR_TRUE; @@ -151,7 +151,8 @@ nsPKCS12Blob_ImportHelper(const char* pkcs12_data, const string16& password, bool is_extractable, bool try_zero_length_secitem, - PK11SlotInfo *slot) + PK11SlotInfo *slot, + net::CertificateList* imported_certs) { DCHECK(pkcs12_data); DCHECK(slot); @@ -159,6 +160,10 @@ nsPKCS12Blob_ImportHelper(const char* pkcs12_data, SECStatus srv = SECSuccess; SEC_PKCS12DecoderContext *dcx = NULL; SECItem unicodePw; + SECItem attribute_value; + CK_BBOOL attribute_data = CK_FALSE; + const SEC_PKCS12DecoderItem* decoder_item = NULL; + unicodePw.type = siBuffer; unicodePw.len = 0; unicodePw.data = NULL; @@ -188,57 +193,71 @@ nsPKCS12Blob_ImportHelper(const char* pkcs12_data, // validate bags srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision); if (srv) goto finish; - // import cert and key + // import certificate and key srv = SEC_PKCS12DecoderImportBags(dcx); if (srv) goto finish; - if (!is_extractable) { - SECItem attribute_value; - CK_BBOOL attribute_data = CK_FALSE; - attribute_value.data = &attribute_data; - attribute_value.len = sizeof(attribute_data); + attribute_value.data = &attribute_data; + attribute_value.len = sizeof(attribute_data); - srv = SEC_PKCS12DecoderIterateInit(dcx); - if (srv) goto finish; + srv = SEC_PKCS12DecoderIterateInit(dcx); + if (srv) goto finish; + + if (imported_certs) + imported_certs->clear(); + + // Collect the list of decoded certificates, and mark private keys + // non-extractable if needed. + while (SEC_PKCS12DecoderIterateNext(dcx, &decoder_item) == SECSuccess) { + if (decoder_item->type != SEC_OID_PKCS12_V1_CERT_BAG_ID) + continue; + + CERTCertificate* cert = PK11_FindCertFromDERCertItem( + slot, decoder_item->der, + NULL); // wincx + if (!cert) { + LOG(ERROR) << "Could not grab a handle to the certificate in the slot " + << "from the corresponding PKCS#12 DER certificate."; + continue; + } + + // Add the cert to the list + if (imported_certs) { + // Empty list of intermediates. + net::X509Certificate::OSCertHandles intermediates; + imported_certs->push_back( + net::X509Certificate::CreateFromHandle(cert, intermediates)); + } + + // Once we have determined that the imported certificate has an + // associated private key too, only then can we mark the key as + // non-extractable. + if (!decoder_item->hasKey) { + CERT_DestroyCertificate(cert); + continue; + } - const SEC_PKCS12DecoderItem* decoder_item = NULL; // Iterate through all the imported PKCS12 items and mark any accompanying - // private keys as unextractable. - while (SEC_PKCS12DecoderIterateNext(dcx, &decoder_item) == SECSuccess) { - if (decoder_item->type != SEC_OID_PKCS12_V1_CERT_BAG_ID) - continue; - if (!decoder_item->hasKey) - continue; - - // Once we have determined that the imported certificate has an - // associated private key too, only then can we mark the key as - // unextractable. - CERTCertificate* cert = PK11_FindCertFromDERCertItem( - slot, decoder_item->der, - NULL); // wincx - if (!cert) { - LOG(ERROR) << "Could not grab a handle to the certificate in the slot " - << "from the corresponding PKCS#12 DER certificate."; - continue; - } + // private keys as non-extractable. + if (!is_extractable) { SECKEYPrivateKey* privKey = PK11_FindPrivateKeyFromCert(slot, cert, NULL); // wincx - CERT_DestroyCertificate(cert); if (privKey) { - // Mark the private key as unextractable. + // Mark the private key as non-extractable. srv = PK11_WriteRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, &attribute_value); SECKEY_DestroyPrivateKey(privKey); if (srv) { LOG(ERROR) << "Could not set CKA_EXTRACTABLE attribute on private " << "key."; + CERT_DestroyCertificate(cert); break; } } } + CERT_DestroyCertificate(cert); if (srv) goto finish; } - import_result = net::OK; finish: // If srv != SECSuccess, NSS probably set a specific error code. @@ -335,10 +354,12 @@ int nsPKCS12Blob_Import(PK11SlotInfo* slot, const char* pkcs12_data, size_t pkcs12_len, const string16& password, - bool is_extractable) { + bool is_extractable, + net::CertificateList* imported_certs) { int rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, - is_extractable, false, slot); + is_extractable, false, slot, + imported_certs); // When the user entered a zero length password: // An empty password should be represented as an empty @@ -350,7 +371,7 @@ int nsPKCS12Blob_Import(PK11SlotInfo* slot, // flavors. if (rv == net::ERR_PKCS12_IMPORT_BAD_PASSWORD && password.empty()) { rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, - is_extractable, true, slot); + is_extractable, true, slot, imported_certs); } return rv; } diff --git a/net/third_party/mozilla_security_manager/nsPKCS12Blob.h b/net/third_party/mozilla_security_manager/nsPKCS12Blob.h index ace6baa..d0c8da1 100644 --- a/net/third_party/mozilla_security_manager/nsPKCS12Blob.h +++ b/net/third_party/mozilla_security_manager/nsPKCS12Blob.h @@ -57,13 +57,15 @@ namespace mozilla_security_manager { void EnsurePKCS12Init(); // Import the private key and certificate from a PKCS#12 blob into the slot. -// If |is_extractable| is false, mark the private key as unextractable. -// Returns a net error code. +// If |is_extractable| is false, mark the private key as non-extractable. +// Returns a net error code. |imported_certs|, if non-NULL, returns a list of +// certs that were imported. int nsPKCS12Blob_Import(PK11SlotInfo* slot, const char* pkcs12_data, size_t pkcs12_len, const string16& password, - bool is_extractable); + bool is_extractable, + net::CertificateList* imported_certs); // Export the given certificates into a PKCS#12 blob, storing into output. // Returns the number of certificates exported. |