Commit 057ad5f7 authored by mattm@chromium.org's avatar mattm@chromium.org

Add NSSCertDatabaseChromeOS which uses the correct slots for each user.

Add GetNSSCertDatabaseForResourceContext to nss_context to get one.

BUG=218643

Review URL: https://codereview.chromium.org/111273002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@242131 0039d316-1c4b-4281-b951-d872f2087c98
parent 1e96003f
......@@ -11,6 +11,10 @@
#include "base/compiler_specific.h"
#include "crypto/scoped_nss_types.h"
namespace net {
class NSSCertDatabase;
}
namespace content {
class ResourceContext;
} // namespace content
......@@ -29,4 +33,15 @@ crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext(
const base::Callback<void(crypto::ScopedPK11Slot)>& callback)
WARN_UNUSED_RESULT;
// Returns a pointer to the NSSCertDatabase for the user associated with
// |context|, if it is ready. If it is not ready and |callback| is non-null, the
// |callback| will be run once the DB is initialized. Ownership is not
// transferred, but the caller may save the pointer, which will remain valid for
// the lifetime of the ResourceContext.
// Should be called only on the IO thread.
net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext(
content::ResourceContext* context,
const base::Callback<void(net::NSSCertDatabase*)>& callback)
WARN_UNUSED_RESULT;
#endif // CHROME_BROWSER_NET_NSS_CONTEXT_H_
......@@ -4,14 +4,76 @@
#include "chrome/browser/net/nss_context.h"
#include "base/memory/weak_ptr.h"
#include "base/supports_user_data.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "content/public/browser/browser_thread.h"
#include "crypto/nss_util_internal.h"
#include "net/cert/nss_cert_database_chromeos.h"
namespace {
void* kDatabaseManagerKey = &kDatabaseManagerKey;
class NSSCertDatabaseChromeOSManager : public base::SupportsUserData::Data {
public:
explicit NSSCertDatabaseChromeOSManager(const std::string& username_hash)
: username_hash_(username_hash), weak_ptr_factory_(this) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
crypto::ScopedPK11Slot private_slot(crypto::GetPrivateSlotForChromeOSUser(
username_hash,
base::Bind(&NSSCertDatabaseChromeOSManager::DidGetPrivateSlot,
weak_ptr_factory_.GetWeakPtr())));
if (private_slot)
DidGetPrivateSlot(private_slot.Pass());
}
virtual ~NSSCertDatabaseChromeOSManager() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
}
net::NSSCertDatabase* GetNSSCertDatabase(
const base::Callback<void(net::NSSCertDatabase*)>& callback) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
if (nss_cert_database_)
return nss_cert_database_.get();
ready_callback_list_.push_back(callback);
return NULL;
}
private:
typedef std::vector<base::Callback<void(net::NSSCertDatabase*)> >
ReadyCallbackList;
void DidGetPrivateSlot(crypto::ScopedPK11Slot private_slot) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
nss_cert_database_.reset(new net::NSSCertDatabaseChromeOS(
crypto::GetPublicSlotForChromeOSUser(username_hash_),
private_slot.Pass()));
ReadyCallbackList callback_list;
callback_list.swap(ready_callback_list_);
for (ReadyCallbackList::iterator i = callback_list.begin();
i != callback_list.end();
++i) {
(*i).Run(nss_cert_database_.get());
}
}
std::string username_hash_;
scoped_ptr<net::NSSCertDatabaseChromeOS> nss_cert_database_;
ReadyCallbackList ready_callback_list_;
base::WeakPtrFactory<NSSCertDatabaseChromeOSManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(NSSCertDatabaseChromeOSManager);
};
std::string GetUsername(content::ResourceContext* context) {
return ProfileIOData::FromResourceContext(context)->username_hash();
}
} // namespace
crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext(
......@@ -27,3 +89,16 @@ crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext(
return crypto::GetPrivateSlotForChromeOSUser(GetUsername(context), callback);
}
net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext(
content::ResourceContext* context,
const base::Callback<void(net::NSSCertDatabase*)>& callback) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
NSSCertDatabaseChromeOSManager* manager =
static_cast<NSSCertDatabaseChromeOSManager*>(
context->GetUserData(kDatabaseManagerKey));
if (!manager) {
manager = new NSSCertDatabaseChromeOSManager(GetUsername(context));
context->SetUserData(kDatabaseManagerKey, manager);
}
return manager->GetNSSCertDatabase(callback);
}
// Copyright 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 "chrome/browser/net/nss_context.h"
#include "base/bind.h"
#include "base/run_loop.h"
#include "chrome/browser/chromeos/login/login_manager_test.h"
#include "chrome/browser/chromeos/login/startup_utils.h"
#include "chrome/browser/chromeos/login/user.h"
#include "chrome/browser/chromeos/login/user_adding_screen.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "content/public/browser/browser_thread.h"
#include "net/cert/nss_cert_database.h"
namespace {
const char kTestUser1[] = "test-user1@gmail.com";
const char kTestUser2[] = "test-user2@gmail.com";
void NotCalledDbCallback(net::NSSCertDatabase* db) { ASSERT_TRUE(false); }
// DBTester handles retrieving the NSSCertDatabase for a given profile, and
// doing some simple sanity checks.
// Browser test cases run on the UI thread, while the nss_context access needs
// to happen on the IO thread. The DBTester class encapsulates the thread
// posting and waiting on the UI thread so that the test case body can be
// written linearly.
class DBTester {
public:
explicit DBTester(Profile* profile) : profile_(profile), db_(NULL) {}
// Initial retrieval of cert database. It may be asynchronous or synchronous.
// Returns true if the database was retrieved successfully.
bool DoGetDBTests() {
base::RunLoop run_loop;
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&DBTester::GetDBAndDoTestsOnIOThread,
base::Unretained(this),
profile_->GetResourceContext(),
run_loop.QuitClosure()));
run_loop.Run();
return !!db_;
}
// Test retrieving the database again, should be called after DoGetDBTests.
void DoGetDBAgainTests() {
base::RunLoop run_loop;
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&DBTester::DoGetDBAgainTestsOnIOThread,
base::Unretained(this),
profile_->GetResourceContext(),
run_loop.QuitClosure()));
run_loop.Run();
}
void DoNotEqualsTests(DBTester* other_tester) {
// The DB and its NSS slots should be different for each profile.
EXPECT_NE(db_, other_tester->db_);
EXPECT_NE(db_->GetPublicSlot().get(),
other_tester->db_->GetPublicSlot().get());
}
private:
void GetDBAndDoTestsOnIOThread(content::ResourceContext* context,
const base::Closure& done_callback) {
net::NSSCertDatabase* db = GetNSSCertDatabaseForResourceContext(
context,
base::Bind(&DBTester::DoTestsOnIOThread,
base::Unretained(this),
done_callback));
if (db) {
DVLOG(1) << "got db synchronously";
DoTestsOnIOThread(done_callback, db);
} else {
DVLOG(1) << "getting db asynchronously...";
}
}
void DoTestsOnIOThread(const base::Closure& done_callback,
net::NSSCertDatabase* db) {
db_ = db;
EXPECT_TRUE(db);
if (db) {
EXPECT_TRUE(db->GetPublicSlot().get());
// Public and private slot are the same in tests.
EXPECT_EQ(db->GetPublicSlot().get(), db->GetPrivateSlot().get());
}
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE, done_callback);
}
void DoGetDBAgainTestsOnIOThread(content::ResourceContext* context,
const base::Closure& done_callback) {
net::NSSCertDatabase* db = GetNSSCertDatabaseForResourceContext(
context, base::Bind(&NotCalledDbCallback));
// Should always be synchronous now.
EXPECT_TRUE(db);
// Should return the same db as before.
EXPECT_EQ(db_, db);
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE, done_callback);
}
Profile* profile_;
net::NSSCertDatabase* db_;
};
} // namespace
class NSSContextChromeOSBrowserTest : public chromeos::LoginManagerTest {
public:
NSSContextChromeOSBrowserTest()
: LoginManagerTest(true /* should_launch_browser */) {}
virtual ~NSSContextChromeOSBrowserTest() {}
};
IN_PROC_BROWSER_TEST_F(NSSContextChromeOSBrowserTest, PRE_TwoUsers) {
// Initialization for ChromeOS multi-profile test infrastructure.
RegisterUser(kTestUser1);
RegisterUser(kTestUser2);
chromeos::StartupUtils::MarkOobeCompleted();
}
IN_PROC_BROWSER_TEST_F(NSSContextChromeOSBrowserTest, TwoUsers) {
chromeos::UserManager* user_manager = chromeos::UserManager::Get();
// Log in first user and get their DB.
LoginUser(kTestUser1);
Profile* profile1 =
user_manager->GetProfileByUser(user_manager->FindUser(kTestUser1));
ASSERT_TRUE(profile1);
DBTester tester1(profile1);
ASSERT_TRUE(tester1.DoGetDBTests());
// Log in second user and get their DB.
chromeos::UserAddingScreen::Get()->Start();
base::RunLoop().RunUntilIdle();
AddUser(kTestUser2);
Profile* profile2 =
user_manager->GetProfileByUser(user_manager->FindUser(kTestUser2));
ASSERT_TRUE(profile2);
DBTester tester2(profile2);
ASSERT_TRUE(tester2.DoGetDBTests());
// Get both DBs again to check that the same object is returned.
tester1.DoGetDBAgainTests();
tester2.DoGetDBAgainTests();
// Check that each user has a separate DB and NSS slots.
tester1.DoNotEqualsTests(&tester2);
}
......@@ -6,6 +6,7 @@
#include "content/public/browser/browser_thread.h"
#include "crypto/nss_util_internal.h"
#include "net/cert/nss_cert_database.h"
crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext(
content::ResourceContext* context) {
......@@ -19,3 +20,10 @@ crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext(
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
return crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot());
}
net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext(
content::ResourceContext* context,
const base::Callback<void(net::NSSCertDatabase*)>& callback) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
return net::NSSCertDatabase::GetInstance();
}
......@@ -1315,6 +1315,7 @@
'browser/net/dns_probe_browsertest.cc',
'browser/net/ftp_browsertest.cc',
'browser/net/load_timing_browsertest.cc',
'browser/net/nss_context_chromeos_browsertest.cc',
'browser/net/predictor_browsertest.cc',
'browser/net/proxy_browsertest.cc',
'browser/net/websocket_browsertest.cc',
......@@ -1751,6 +1752,7 @@
],
'sources!': [
'browser/extensions/api/terminal/terminal_private_apitest.cc',
'browser/net/nss_context_chromeos_browsertest.cc',
'browser/notifications/login_state_notification_blocker_chromeos_browsertest.cc',
'browser/ui/ash/caps_lock_delegate_chromeos_browsertest.cc',
'test/data/webui/certificate_viewer_dialog_test.js',
......
......@@ -16,6 +16,8 @@ template <class ObserverType> class ObserverListThreadSafe;
namespace net {
class NSSCertDatabase;
// This class provides cross-platform functions to verify and add user
// certificates, and to observe changes to the underlying certificate stores.
......@@ -85,6 +87,12 @@ class NET_EXPORT CertDatabase {
void OnAndroidKeyChainChanged();
#endif
#if defined(USE_NSS)
// Observe events from the |source| and forward them to observers of this
// CertDatabase.
void ObserveNSSCertDatabase(NSSCertDatabase* source);
#endif
private:
friend struct DefaultSingletonTraits<CertDatabase>;
......
......@@ -23,13 +23,9 @@ namespace net {
// the given CertDatabase.
class CertDatabase::Notifier : public NSSCertDatabase::Observer {
public:
explicit Notifier(CertDatabase* cert_db) : cert_db_(cert_db) {
NSSCertDatabase::GetInstance()->AddObserver(this);
}
explicit Notifier(CertDatabase* cert_db) : cert_db_(cert_db) {}
virtual ~Notifier() {
NSSCertDatabase::GetInstance()->RemoveObserver(this);
}
virtual ~Notifier() {}
// NSSCertDatabase::Observer implementation:
virtual void OnCertAdded(const X509Certificate* cert) OVERRIDE {
......@@ -51,10 +47,9 @@ class CertDatabase::Notifier : public NSSCertDatabase::Observer {
};
CertDatabase::CertDatabase()
: observer_list_(new ObserverListThreadSafe<Observer>) {
// Observe NSSCertDatabase events and forward them to observers of
// CertDatabase. This also makes sure that NSS has been initialized.
notifier_.reset(new Notifier(this));
: observer_list_(new ObserverListThreadSafe<Observer>),
notifier_(new Notifier(this)) {
crypto::EnsureNSSInit();
}
CertDatabase::~CertDatabase() {}
......@@ -109,4 +104,8 @@ int CertDatabase::AddUserCert(X509Certificate* cert_obj) {
return OK;
}
void CertDatabase::ObserveNSSCertDatabase(NSSCertDatabase* source) {
source->AddObserver(this->notifier_.get());
}
} // namespace net
......@@ -10,9 +10,9 @@
#include <pk11pub.h>
#include <secmod.h>
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "base/observer_list_threadsafe.h"
#include "crypto/nss_util.h"
#include "crypto/nss_util_internal.h"
......@@ -35,6 +35,14 @@ namespace psm = mozilla_security_manager;
namespace net {
namespace {
base::LazyInstance<NSSCertDatabase>::Leaky
g_nss_cert_database = LAZY_INSTANCE_INITIALIZER;
} // namespace
NSSCertDatabase::ImportCertFailure::ImportCertFailure(
const scoped_refptr<X509Certificate>& cert,
int err)
......@@ -44,13 +52,20 @@ NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {}
// static
NSSCertDatabase* NSSCertDatabase::GetInstance() {
return Singleton<NSSCertDatabase,
LeakySingletonTraits<NSSCertDatabase> >::get();
// TODO(mattm): Remove this ifdef guard once the linux impl of
// GetNSSCertDatabaseForResourceContext does not call GetInstance.
#if defined(OS_CHROMEOS)
LOG(ERROR) << "NSSCertDatabase::GetInstance() is deprecated."
<< "See http://crbug.com/329735.";
#endif
return &g_nss_cert_database.Get();
}
NSSCertDatabase::NSSCertDatabase()
: observer_list_(new ObserverListThreadSafe<Observer>) {
crypto::EnsureNSSInit();
// This also makes sure that NSS has been initialized.
CertDatabase::GetInstance()->ObserveNSSCertDatabase(this);
psm::EnsurePKCS12Init();
}
......@@ -117,6 +132,9 @@ int NSSCertDatabase::ImportFromPKCS12(
const base::string16& password,
bool is_extractable,
net::CertificateList* imported_certs) {
DVLOG(1) << __func__ << " "
<< PK11_GetModuleID(module->os_module_handle()) << ":"
<< PK11_GetSlotID(module->os_module_handle());
int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
data.data(), data.size(),
password,
......@@ -154,7 +172,7 @@ X509Certificate* NSSCertDatabase::FindRootInList(
&certn_1->os_cert_handle()->subject) == SECEqual)
return certn_1;
VLOG(1) << "certificate list is not a hierarchy";
LOG(WARNING) << "certificate list is not a hierarchy";
return cert0;
}
......
......@@ -16,7 +16,9 @@
#include "net/cert/cert_type.h"
#include "net/cert/x509_certificate.h"
template <typename T> struct DefaultSingletonTraits;
namespace base {
template <typename T> struct DefaultLazyInstanceTraits;
}
template <class ObserverType> class ObserverListThreadSafe;
namespace net {
......@@ -89,17 +91,18 @@ class NET_EXPORT NSSCertDatabase {
DISTRUSTED_OBJ_SIGN = 1 << 5,
};
// DEPRECATED: See http://crbug.com/329735.
static NSSCertDatabase* GetInstance();
// Get a list of unique certificates in the certificate database (one
// instance of all certificates).
void ListCerts(CertificateList* certs);
virtual void ListCerts(CertificateList* certs);
// Get the default slot for public key data.
crypto::ScopedPK11Slot GetPublicSlot() const;
virtual crypto::ScopedPK11Slot GetPublicSlot() const;
// Get the default slot for private key or mixed private/public key data.
crypto::ScopedPK11Slot GetPrivateSlot() const;
virtual crypto::ScopedPK11Slot GetPrivateSlot() const;
// Get the default module for public key data.
// The returned pointer must be stored in a scoped_refptr<CryptoModule>.
......@@ -116,7 +119,7 @@ class NET_EXPORT NSSCertDatabase {
// Get all modules.
// If |need_rw| is true, only writable modules will be returned.
// TODO(mattm): come up with better alternative to CryptoModuleList.
void ListModules(CryptoModuleList* modules, bool need_rw) const;
virtual void ListModules(CryptoModuleList* modules, bool need_rw) const;
// Import certificates and private keys from PKCS #12 blob into the module.
// If |is_extractable| is false, mark the private key as being unextractable
......@@ -196,17 +199,22 @@ class NET_EXPORT NSSCertDatabase {
// Registers |observer| to receive notifications of certificate changes. The
// thread on which this is called is the thread on which |observer| will be
// called back with notifications.
// NOTE: CertDatabase::AddObserver should be preferred. Observers registered
// here will only recieve notifications generated directly through the
// NSSCertDatabase, but not those from the CertDatabase. The CertDatabase
// observers will recieve both.
void AddObserver(Observer* observer);
// Unregisters |observer| from receiving notifications. This must be called
// on the same thread on which AddObserver() was called.
void RemoveObserver(Observer* observer);
private:
friend struct DefaultSingletonTraits<NSSCertDatabase>;
protected:
NSSCertDatabase();
~NSSCertDatabase();
virtual ~NSSCertDatabase();
private:
friend struct base::DefaultLazyInstanceTraits<NSSCertDatabase>;
// Broadcasts notifications to all registered observers.
void NotifyObserversOfCertAdded(const X509Certificate* cert);
......
// Copyright 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 "net/base/crypto_module.h"
#include "net/cert/x509_certificate.h"
namespace net {
NSSCertDatabaseChromeOS::NSSCertDatabaseChromeOS(
crypto::ScopedPK11Slot public_slot,
crypto::ScopedPK11Slot private_slot)
: public_slot_(public_slot.Pass()),
private_slot_(private_slot.Pass()) {
profile_filter_.Init(GetPublicSlot(), GetPrivateSlot());
}
NSSCertDatabaseChromeOS::~NSSCertDatabaseChromeOS() {}
void NSSCertDatabaseChromeOS::ListCerts(CertificateList* certs) {
NSSCertDatabase::ListCerts(certs);
size_t pre_size = certs->size();
certs->erase(std::remove_if(
certs->begin(),
certs->end(),
NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate(
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 {
NSSCertDatabase::ListModules(modules, need_rw);
size_t pre_size = modules->size();
modules->erase(
std::remove_if(
modules->begin(),
modules->end(),
NSSProfileFilterChromeOS::ModuleNotAllowedForProfilePredicate(
profile_filter_)),
modules->end());
DVLOG(1) << "filtered " << pre_size - modules->size() << " of " << pre_size
<< " modules";
}
} // namespace net
// Copyright 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.
#ifndef NET_CERT_NSS_CERT_DATABASE_CHROMEOS_
#define NET_CERT_NSS_CERT_DATABASE_CHROMEOS_
#include "base/callback.h"
#include "crypto/scoped_nss_types.h"
#include "net/cert/nss_cert_database.h"
#include "net/cert/nss_profile_filter_chromeos.h"
namespace net {
class NET_EXPORT NSSCertDatabaseChromeOS : public NSSCertDatabase {
public:
NSSCertDatabaseChromeOS(crypto::ScopedPK11Slot public_slot,
crypto::ScopedPK11Slot private_slot);
virtual ~NSSCertDatabaseChromeOS();
// NSSCertDatabase implementation.
virtual void ListCerts(CertificateList* certs) OVERRIDE;
virtual crypto::ScopedPK11Slot GetPublicSlot() const OVERRIDE;
virtual crypto::ScopedPK11Slot GetPrivateSlot() const OVERRIDE;
virtual void ListModules(CryptoModuleList* modules, bool need_rw) const
OVERRIDE;
// TODO(mattm): handle trust setting, deletion, etc correctly when certs exist
// in multiple slots.
// TODO(mattm): handle trust setting correctly for certs in read-only slots.
private:
crypto::ScopedPK11Slot public_slot_;
crypto::ScopedPK11Slot private_slot_;
NSSProfileFilterChromeOS profile_filter_;
DISALLOW_COPY_AND_ASSIGN(NSSCertDatabaseChromeOS);
};
} // namespace net
#endif // NET_CERT_NSS_CERT_DATABASE_CHROMEOS_
// Copyright 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 "base/bind.h"
#include "base/callback.h"
#include "base/run_loop.h"
#include "crypto/nss_util.h"
#include "crypto/nss_util_internal.h"
#include "net/base/test_data_directory.h"
#include "net/cert/cert_database.h"
#include "net/test/cert_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
bool IsCertInCertificateList(const X509Certificate* cert,
const CertificateList& cert_list) {
for (CertificateList::const_iterator it = cert_list.begin();
it != cert_list.end();
++it) {
if (X509Certificate::IsSameOSCert((*it)->os_cert_handle(),
cert->os_cert_handle()))
return true;
}
return false;
}
} // namespace
class NSSCertDatabaseChromeOSTest : public testing::Test,
public CertDatabase::Observer {
public:
NSSCertDatabaseChromeOSTest()
: observer_added_(false), user_1_("user1"), user_2_("user2") {}
virtual void SetUp() OVERRIDE {
// Initialize nss_util slots.
ASSERT_TRUE(user_1_.constructed_successfully());
ASSERT_TRUE(user_2_.constructed_successfully());
user_1_.FinishInit();
user_2_.FinishInit();
// Create NSSCertDatabaseChromeOS for each user.
db_1_.reset(new NSSCertDatabaseChromeOS(
crypto::GetPublicSlotForChromeOSUser(user_1_.username_hash()),
crypto::GetPrivateSlotForChromeOSUser(
user_1_.username_hash(),
base::Callback<void(crypto::ScopedPK11Slot)>())));
db_2_.reset(new NSSCertDatabaseChromeOS(
crypto::GetPublicSlotForChromeOSUser(user_2_.username_hash()),
crypto::GetPrivateSlotForChromeOSUser(
user_2_.username_hash(),
base::Callback<void(crypto::ScopedPK11Slot)>())));
// Add observer to CertDatabase for checking that notifications from
// NSSCertDatabaseChromeOS are proxied to the CertDatabase.
CertDatabase::GetInstance()->AddObserver(this);
observer_added_ = true;
}
virtual void TearDown() OVERRIDE {
if (observer_added_)
CertDatabase::GetInstance()->RemoveObserver(this);
}
// CertDatabase::Observer:
virtual void OnCertAdded(const X509Certificate* cert) OVERRIDE {
added_.push_back(cert ? cert->os_cert_handle() : NULL);
}
virtual void OnCertRemoved(const X509Certificate* cert) OVERRIDE {}
virtual void OnCACertChanged(const X509Certificate* cert) OVERRIDE {
added_ca_.push_back(cert ? cert->os_cert_handle() : NULL);
}
protected:
bool observer_added_;
// Certificates that were passed to the CertDatabase observers.
std::vector<CERTCertificate*> added_ca_;
std::vector<CERTCertificate*> added_;
crypto::ScopedTestNSSChromeOSUser user_1_;
crypto::ScopedTestNSSChromeOSUser user_2_;
scoped_ptr<NSSCertDatabaseChromeOS> db_1_;
scoped_ptr<NSSCertDatabaseChromeOS> db_2_;
};
// Test that ListModules() on each user includes that user's NSS software slot,
// and does not include the software slot of the other user. (Does not check the
// private slot, since it is the same as the public slot in tests.)
TEST_F(NSSCertDatabaseChromeOSTest, ListModules) {
CryptoModuleList modules_1;
CryptoModuleList modules_2;
db_1_->ListModules(&modules_1, false /* need_rw */);
db_2_->ListModules(&modules_2, false /* need_rw */);
bool found_1 = false;
for (CryptoModuleList::iterator it = modules_1.begin(); it != modules_1.end();
++it) {
EXPECT_NE(db_2_->GetPublicSlot().get(), (*it)->os_module_handle());
if ((*it)->os_module_handle() == db_1_->GetPublicSlot().get())
found_1 = true;
}
EXPECT_TRUE(found_1);
bool found_2 = false;
for (CryptoModuleList::iterator it = modules_2.begin(); it != modules_2.end();
++it) {
EXPECT_NE(db_1_->GetPublicSlot().get(), (*it)->os_module_handle());
if ((*it)->os_module_handle() == db_2_->GetPublicSlot().get())
found_2 = true;
}
EXPECT_TRUE(found_2);
}
// Test that ImportCACerts imports the cert to the correct slot, and that
// ListCerts includes the added cert for the correct user, and does not include
// it for the other user.
TEST_F(NSSCertDatabaseChromeOSTest, ImportCACerts) {
// Load test certs from disk.
CertificateList certs_1 =
CreateCertificateListFromFile(GetTestCertsDirectory(),
"root_ca_cert.pem",
X509Certificate::FORMAT_AUTO);
ASSERT_EQ(1U, certs_1.size());
CertificateList certs_2 =
CreateCertificateListFromFile(GetTestCertsDirectory(),
"2048-rsa-root.pem",
X509Certificate::FORMAT_AUTO);
ASSERT_EQ(1U, certs_2.size());
// Import one cert for each user.
NSSCertDatabase::ImportCertFailureList failed;
EXPECT_TRUE(
db_1_->ImportCACerts(certs_1, NSSCertDatabase::TRUSTED_SSL, &failed));
EXPECT_EQ(0U, failed.size());
failed.clear();
EXPECT_TRUE(
db_2_->ImportCACerts(certs_2, NSSCertDatabase::TRUSTED_SSL, &failed));
EXPECT_EQ(0U, failed.size());
// Get cert list for each user.
CertificateList user_1_certlist;
CertificateList user_2_certlist;
db_1_->ListCerts(&user_1_certlist);
db_2_->ListCerts(&user_2_certlist);
// Check that the imported certs only shows up in the list for the user that
// imported them.
EXPECT_TRUE(IsCertInCertificateList(certs_1[0], user_1_certlist));
EXPECT_FALSE(IsCertInCertificateList(certs_1[0], user_2_certlist));
EXPECT_TRUE(IsCertInCertificateList(certs_2[0], user_2_certlist));
EXPECT_FALSE(IsCertInCertificateList(certs_2[0], user_1_certlist));
// Run the message loop so the observer notifications get processed.
base::RunLoop().RunUntilIdle();
// Should have gotten two OnCACertChanged notifications.
ASSERT_EQ(2U, added_ca_.size());
// TODO(mattm): make NSSCertDatabase actually pass the cert to the callback,
// and enable these checks:
// EXPECT_EQ(certs_1[0]->os_cert_handle(), added_ca_[0]);
// EXPECT_EQ(certs_2[0]->os_cert_handle(), added_ca_[1]);
EXPECT_EQ(0U, added_.size());
}
// Test that ImportServerCerts imports the cert to the correct slot, and that
// ListCerts includes the added cert for the correct user, and does not include
// it for the other user.
TEST_F(NSSCertDatabaseChromeOSTest, ImportServerCert) {
// Load test certs from disk.
CertificateList certs_1 = CreateCertificateListFromFile(
GetTestCertsDirectory(), "ok_cert.pem", X509Certificate::FORMAT_AUTO);
ASSERT_EQ(1U, certs_1.size());
CertificateList certs_2 =
CreateCertificateListFromFile(GetTestCertsDirectory(),
"2048-rsa-ee-by-2048-rsa-intermediate.pem",
X509Certificate::FORMAT_AUTO);
ASSERT_EQ(1U, certs_2.size());
// Import one cert for each user.
NSSCertDatabase::ImportCertFailureList failed;
EXPECT_TRUE(
db_1_->ImportServerCert(certs_1, NSSCertDatabase::TRUSTED_SSL, &failed));
EXPECT_EQ(0U, failed.size());
failed.clear();
EXPECT_TRUE(
db_2_->ImportServerCert(certs_2, NSSCertDatabase::TRUSTED_SSL, &failed));
EXPECT_EQ(0U, failed.size());
// Get cert list for each user.
CertificateList user_1_certlist;
CertificateList user_2_certlist;
db_1_->ListCerts(&user_1_certlist);
db_2_->ListCerts(&user_2_certlist);
// Check that the imported certs only shows up in the list for the user that
// imported them.
EXPECT_TRUE(IsCertInCertificateList(certs_1[0], user_1_certlist));
EXPECT_FALSE(IsCertInCertificateList(certs_1[0], user_2_certlist));
EXPECT_TRUE(IsCertInCertificateList(certs_2[0], user_2_certlist));
EXPECT_FALSE(IsCertInCertificateList(certs_2[0], user_1_certlist));
// Run the message loop so the observer notifications get processed.
base::RunLoop().RunUntilIdle();
// TODO(mattm): ImportServerCert doesn't actually cause any observers to
// fire. Is that correct?
EXPECT_EQ(0U, added_ca_.size());
EXPECT_EQ(0U, added_.size());
}
} // namespace net
......@@ -297,6 +297,8 @@
'cert/multi_threaded_cert_verifier.h',
'cert/nss_cert_database.cc',
'cert/nss_cert_database.h',
'cert/nss_cert_database_chromeos.cc',
'cert/nss_cert_database_chromeos.h',
'cert/nss_profile_filter_chromeos.cc',
'cert/nss_profile_filter_chromeos.h',
'cert/pem_tokenizer.cc',
......@@ -1637,6 +1639,7 @@
'cert/multi_log_ct_verifier_unittest.cc',
'cert/multi_threaded_cert_verifier_unittest.cc',
'cert/nss_cert_database_unittest.cc',
'cert/nss_cert_database_chromeos_unittest.cc',
'cert/pem_tokenizer_unittest.cc',
'cert/signed_certificate_timestamp_unittest.cc',
'cert/test_root_certs_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment