| 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
|
|
|