Index: net/cert/nss_cert_database_chromeos.cc |
diff --git a/net/cert/nss_cert_database_chromeos.cc b/net/cert/nss_cert_database_chromeos.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0fde443face81aa0eefe7c7ce80554f31145b440 |
--- /dev/null |
+++ b/net/cert/nss_cert_database_chromeos.cc |
@@ -0,0 +1,304 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "net/cert/nss_cert_database_chromeos.h" |
+ |
+#include <cert.h> |
+#include <pk11pub.h> |
+ |
+#include <map> |
+ |
+#include "base/bind.h" |
+#include "base/lazy_instance.h" |
+#include "base/stl_util.h" |
+#include "base/threading/non_thread_safe.h" |
+#include "crypto/nss_util.h" |
+#include "crypto/nss_util_internal.h" |
+#include "net/base/crypto_module.h" |
+#include "net/cert/x509_certificate.h" |
+//#include "net/cert/x509_util_nss.h" |
+ |
+namespace net { |
+ |
+namespace { |
+ |
+scoped_refptr<const X509Certificate> FindCertInSlot(const X509Certificate* cert, |
+ PK11SlotInfo* slot) { |
+ CERTCertificate* slot_cert_handle = PK11_FindCertFromDERCert( |
+ slot, cert->os_cert_handle(), NULL); |
+ if (slot_cert_handle) { |
+ scoped_refptr<X509Certificate> slot_cert( |
+ X509Certificate::CreateFromHandle(slot_cert_handle, |
+ X509Certificate::OSCertHandles())); |
+ CERT_DestroyCertificate(slot_cert_handle); |
+ return slot_cert; |
+ } |
+ return NULL; |
+} |
+ |
+} // namespace |
+ |
+class NSSCertDatabaseChromeOS::Manager : public base::NonThreadSafe { |
+ public: |
+ ~Manager() { |
+ STLDeleteValues(&user_db_map_); |
+ } |
+ |
+ // XXX remove this? Or keep it but with a ready callback? |
+ /*NSSCertDatabase* GetPrimaryDB() { |
+ LOG(WARNING) |
+ << "Using primary NSSCertDatabase. Consider using GetForUser instead."; |
+ return primary_db_; |
+ }*/ |
+ |
+ NSSCertDatabase* GetForUser( |
+ const std::string& username_hash, |
+ const base::Callback<void(NSSCertDatabase*)>& callback) { |
+ DCHECK(CalledOnValidThread()); |
+ UserDBMap::iterator it = user_db_map_.find(username_hash); |
+ if (it != user_db_map_.end()) { |
+ // DB already exists for this user. |
+ if (it->second->ready_) |
+ return it->second; |
+ it->second->OnReady(callback); |
+ return NULL; |
+ } |
+ |
+ crypto::ScopedPK11Slot public_slot( |
+ crypto::GetPublicSlotForChromeOSUser(username_hash)); |
+ if (!public_slot) { |
+ NOTREACHED(); |
+ // Invalid user, or someone called us before InitializeNSSForChromeOSUser |
+ // got called for the user. |
+ // XXX we don't really have a way to return error.. maybe run the callback |
+ // with NULL also? |
+ //callback.Run(NULL); |
+ return NULL; |
+ } |
+ |
+ NSSCertDatabaseChromeOS* db = NULL; |
+ if (primary_db_owner_) { |
+ crypto::ScopedPK11Slot primary_public_slot(primary_db_->GetPublicSlot()); |
+ if (public_slot.get() == primary_public_slot.get()) { |
+ DVLOG(1) << "Got username for primary: " << username_hash; |
+ // We now know what username corrosponds to the primary user. Put the |
+ // primary_db_ into the user_db_map_, which will now own it. |
+ db = primary_db_owner_.release(); |
+ } |
+ } |
+ |
+ if (!db) { |
+ // Create DB for new user. |
+ DVLOG(1) << "Create DB for: " << username_hash; |
+ db = new NSSCertDatabaseChromeOS(public_slot.Pass()); |
+ crypto::ScopedPK11Slot private_slot(crypto::GetPrivateSlotForChromeOSUser( |
+ username_hash, |
+ base::Bind(&NSSCertDatabaseChromeOS::SetPrivateSlot, |
+ base::Unretained(db)))); |
+ if (private_slot) |
+ db->SetPrivateSlot(private_slot.Pass()); |
+ } |
+ |
+ user_db_map_[username_hash] = db; |
+ if (db->ready_) |
+ return db; |
+ db->OnReady(callback); |
+ return NULL; |
+ } |
+ private: |
+ friend struct base::DefaultLazyInstanceTraits<Manager>; |
+ |
+ Manager() |
+ : primary_db_(new NSSCertDatabaseChromeOS( |
+ crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot()))) { |
+ primary_db_owner_.reset(primary_db_); |
+ if (crypto::IsTPMTokenReady(base::Bind(&Manager::SetPrimaryPrivateSlot, |
+ base::Unretained(this)))) |
+ SetPrimaryPrivateSlot(); |
+ } |
+ |
+ void SetPrimaryPrivateSlot() { |
+ DCHECK(CalledOnValidThread()); |
+ primary_db_->SetPrivateSlot( |
+ crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot())); |
+ } |
+ |
+ // XXX do we still need primary db stuff? |
+ NSSCertDatabaseChromeOS* primary_db_; |
+ // Holds ownership of |primary_db_| until it has been inserted into the |
+ // |user_db_map_|. |
+ scoped_ptr<NSSCertDatabaseChromeOS> primary_db_owner_; |
+ typedef std::map<std::string, NSSCertDatabaseChromeOS*> UserDBMap; |
+ UserDBMap user_db_map_; |
+}; |
+ |
+namespace { |
+base::LazyInstance<NSSCertDatabaseChromeOS::Manager>::Leaky |
+ g_nss_cert_database_chromeos_manager = LAZY_INSTANCE_INITIALIZER; |
+} // namespace |
+ |
+// static |
+NSSCertDatabase* NSSCertDatabaseChromeOS::GetForUser( |
+ const std::string& username_hash, |
+ const base::Callback<void(NSSCertDatabase*)>& callback) { |
+ return g_nss_cert_database_chromeos_manager.Get().GetForUser(username_hash, |
+ callback); |
+} |
+ |
+NSSCertDatabaseChromeOS::NSSCertDatabaseChromeOS( |
+ crypto::ScopedPK11Slot public_slot) |
+ : ready_(false), public_slot_(public_slot.Pass()) { |
+ DVLOG(1) << __func__ << " pub:" << PK11_GetModuleID(public_slot_.get()) << ":" |
+ << PK11_GetSlotID(public_slot_.get()); |
+ NSSCertDatabase::GetInstanceNoWarn()->AddSource(this); |
+} |
+ |
+NSSCertDatabaseChromeOS::~NSSCertDatabaseChromeOS() {} |
+ |
+void NSSCertDatabaseChromeOS::ListCerts(CertificateList* certs) { |
+ if (!ready_) { |
+ LOG(WARNING) << __func__ << " called before initialization complete"; |
+ certs->clear(); |
+ return; |
+ } |
+ NSSCertDatabase::ListCerts(certs); |
+ |
+ size_t pre_size = certs->size(); |
+ certs->erase( |
+ std::remove_if(certs->begin(), certs->end(), |
+ NSSProfileFilterChromeOS::Predicate(profile_filter_)), |
+ certs->end()); |
+ DVLOG(1) << "filtered " << pre_size - certs->size() << " of " << pre_size |
+ << " certs"; |
+} |
+ |
+crypto::ScopedPK11Slot NSSCertDatabaseChromeOS::GetPublicSlot() const { |
+ return crypto::ScopedPK11Slot( |
+ public_slot_ ? PK11_ReferenceSlot(public_slot_.get()) : NULL); |
+} |
+ |
+crypto::ScopedPK11Slot NSSCertDatabaseChromeOS::GetPrivateSlot() const { |
+ return crypto::ScopedPK11Slot( |
+ private_slot_ ? PK11_ReferenceSlot(private_slot_.get()) : NULL); |
+} |
+ |
+void NSSCertDatabaseChromeOS::ListModules(CryptoModuleList* modules, |
+ bool need_rw) const { |
+ if (!ready_) { |
+ LOG(WARNING) << __func__ << " called before initialization complete"; |
+ modules->clear(); |
+ return; |
+ } |
+ NSSCertDatabase::ListModules(modules, need_rw); |
+ |
+ size_t pre_size = modules->size(); |
+ modules->erase( |
+ std::remove_if(modules->begin(), modules->end(), |
+ NSSProfileFilterChromeOS::Predicate(profile_filter_)), |
+ modules->end()); |
+ DVLOG(1) << "filtered " << pre_size - modules->size() << " of " << pre_size |
+ << " modules"; |
+} |
+ |
+NSSCertDatabase::TrustBits NSSCertDatabaseChromeOS::GetCertTrust( |
+ const X509Certificate* cert, |
+ CertType type) const { |
+ return NSSCertDatabase::GetCertTrust(ResolveCert(cert, false), type); |
+} |
+ |
+bool NSSCertDatabaseChromeOS::IsUntrusted(const X509Certificate* cert) const { |
+ return NSSCertDatabase::IsUntrusted(ResolveCert(cert, false)); |
+} |
+ |
+#if 0 |
+bool NSSCertDatabaseChromeOS::SetCertTrust(const X509Certificate* cert, |
+ CertType type, |
+ TrustBits trust_bits) { |
+ // XXX handle case of cert not existing in any of this profile's rw slots |
+ if (!FindCertInSlot(cert, public_slot_.get())) { |
+ // Copy cert to our slot so we can set trust . . . |
+ // XXX this doesn't actually help. |
+ SECStatus srv = PK11_ImportCert( |
+ public_slot_.get(), |
+ cert->os_cert_handle(), |
+ CK_INVALID_HANDLE, |
+ x509_util::GetUniqueNicknameForSlot(cert->GetDefaultNickname(type), |
+ &cert->os_cert_handle()->derSubject, |
+ public_slot_.get()).c_str(), |
+ PR_FALSE /* includeTrust (unused) */); |
+ if (srv != SECSuccess) { |
+ LOG(ERROR) << "copying cert to our slot failed with error " |
+ << PORT_GetError(); |
+ return false; |
+ } |
+ DVLOG(1) << "copied cert to our slot"; |
+ } |
+ // |
+ // XXX this doesn't work: if cert is in multiple rw slots nss will just set |
+ // the trust on the "first" one - we could return false instead of setting |
+ // trust in wrong slot? (but how do we know which slot nss would have felt |
+ // like setting the trust in? is it always the lowest numbered slotid?) |
+ return NSSCertDatabase::SetCertTrust( |
+ ResolveCert(cert, true), type, trust_bits); |
+} |
+#endif |
+ |
+void NSSCertDatabaseChromeOS::SetPrivateSlot( |
+ crypto::ScopedPK11Slot private_slot) { |
+ DCHECK(!ready_); |
+ if (!private_slot) |
+ LOG(WARNING) << "initializing with NULL private_slot."; |
+ private_slot_ = private_slot.Pass(); |
+ profile_filter_.Init(GetPublicSlot(), GetPrivateSlot()); |
+ ready_ = true; |
+ |
+ ReadyCallbackList callback_list; |
+ callback_list.swap(ready_callback_list_); |
+ for (ReadyCallbackList::iterator i = callback_list.begin(); |
+ i != callback_list.end(); |
+ ++i) { |
+ (*i).Run(this); |
+ } |
+} |
+ |
+void NSSCertDatabaseChromeOS::OnReady( |
+ const base::Callback<void(NSSCertDatabase*)>& callback) { |
+ DCHECK(!ready_); |
+ ready_callback_list_.push_back(callback); |
+} |
+ |
+scoped_refptr<const X509Certificate> NSSCertDatabaseChromeOS::ResolveCert( |
+ const X509Certificate* cert, bool need_rw) const { |
+ // If we have a copy in our slot, use that. |
+ scoped_refptr<const X509Certificate> pub_slot_cert( |
+ FindCertInSlot(cert, public_slot_.get())); |
+ if (pub_slot_cert) { |
+ DVLOG(1) << "found pub slot cert"; |
+ return pub_slot_cert; |
+ } |
+ |
+ CryptoModuleList module_list; |
+ ListModules(&module_list, need_rw); |
+ for (CryptoModuleList::iterator i = module_list.begin(); |
+ i != module_list.end(); |
+ ++i) { |
+ if ((*i)->os_module_handle() == public_slot_.get()) |
+ continue; // We already checked this slot above. |
+ scoped_refptr<const X509Certificate> slot_cert( |
+ FindCertInSlot(cert, (*i)->os_module_handle())); |
+ if (slot_cert) { |
+ DVLOG(1) << "found cert in other slot: " |
+ << PK11_GetModuleID((*i)->os_module_handle()) << ":" |
+ << PK11_GetSlotID((*i)->os_module_handle()); |
+ return slot_cert; |
+ } |
+ } |
+ |
+ // Otherwise, use whatever NSS decides as the default. |
+ // XXX should we return NULL here? NOTREACHED? |
+ LOG(WARNING) << "using default slot cert"; |
+ return cert; |
+} |
+ |
+} // namespace net |