Chromium Code Reviews| Index: base/nss_util.cc |
| diff --git a/base/nss_util.cc b/base/nss_util.cc |
| index c78c6d584e51ff680436574b151571e9751f5768..8c695019c84d4e05a9727adc0aa1238404b3da04 100644 |
| --- a/base/nss_util.cc |
| +++ b/base/nss_util.cc |
| @@ -20,6 +20,7 @@ |
| #include <vector> |
| +#include "base/crypto/scoped_nss_types.h" |
| #include "base/environment.h" |
| #include "base/file_path.h" |
| #include "base/file_util.h" |
| @@ -43,6 +44,33 @@ namespace base { |
| namespace { |
| +#if defined(OS_CHROMEOS) |
| +static const char kNSSDatabaseName[] = "Real NSS database"; |
| + |
| +// TODO(gspencer): Get these values from cryptohomed's dbus API when |
| +// we ask if it has initialized the TPM yet. These should not be |
| +// hard-coded here. |
| +static const char kTPMTokenName[] = "Initialized by CrOS"; |
| +static const char kTPMUserPIN[] = "111111"; |
| +static const char kTPMSecurityOfficerPIN[] = "000000"; |
| + |
| +// Fake certificate authority database used for testing. |
| +static const FilePath::CharType kReadOnlyCertDB[] = |
| + FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb"); |
| +#endif // defined(OS_CHROMEOS) |
| + |
| +std::string GetNSSErrorMessage() { |
| + std::string result; |
| + if (PR_GetErrorTextLength()) { |
| + scoped_array<char> error_text(new char[PR_GetErrorTextLength() + 1]); |
| + PRInt32 copied = PR_GetErrorText(error_text.get()); |
| + result = std::string(error_text.get(), copied); |
| + } else { |
| + result = StringPrintf("NSS error code: %d", PR_GetError()); |
| + } |
| + return result; |
| +} |
| + |
| #if defined(USE_NSS) |
| FilePath GetDefaultConfigDirectory() { |
| FilePath dir = file_util::GetHomeDir(); |
| @@ -66,8 +94,6 @@ FilePath GetDefaultConfigDirectory() { |
| // caller to failover to NSS_NoDB_Init() at that point. |
| FilePath GetInitialConfigDirectory() { |
| #if defined(OS_CHROMEOS) |
| - static const FilePath::CharType kReadOnlyCertDB[] = |
| - FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb"); |
| return FilePath(kReadOnlyCertDB); |
| #else |
| return GetDefaultConfigDirectory(); |
| @@ -77,6 +103,12 @@ FilePath GetInitialConfigDirectory() { |
| // This callback for NSS forwards all requests to a caller-specified |
| // CryptoModuleBlockingPasswordDelegate object. |
| char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) { |
| +#if defined(OS_CHROMEOS) |
| + // If we get asked for a password for the TPM, then return the |
| + // static password we use. |
| + if (PK11_GetTokenName(slot) == base::GetTPMTokenName()) |
| + return PORT_Strdup(kTPMUserPIN); |
| +#endif |
| base::CryptoModuleBlockingPasswordDelegate* delegate = |
| reinterpret_cast<base::CryptoModuleBlockingPasswordDelegate*>(arg); |
| if (delegate) { |
| @@ -120,21 +152,38 @@ void UseLocalCacheOfNSSDatabaseIfNFS(const FilePath& database_dir) { |
| #endif // defined(OS_LINUX) |
| } |
| -// Load nss's built-in root certs. |
| -SECMODModule *InitDefaultRootCerts() { |
| - const char* kModulePath = "libnssckbi.so"; |
| - char modparams[1024]; |
| - snprintf(modparams, sizeof(modparams), |
| - "name=\"Root Certs\" library=\"%s\"", kModulePath); |
| - SECMODModule *root = SECMOD_LoadUserModule(modparams, NULL, PR_FALSE); |
| - if (root) |
| - return root; |
| - |
| - // Aw, snap. Can't find/load root cert shared library. |
| - // This will make it hard to talk to anybody via https. |
| - NOTREACHED(); |
| +// A helper class that acquires the SECMOD list read lock while the |
| +// AutoSECMODListReadLock is in scope. |
| +class AutoSECMODListReadLock { |
| + public: |
| + AutoSECMODListReadLock() |
| + : lock_(SECMOD_GetDefaultModuleListLock()) { |
| + SECMOD_GetReadLock(lock_); |
| + } |
| + |
| + ~AutoSECMODListReadLock() { |
| + SECMOD_ReleaseReadLock(lock_); |
| + } |
| + |
| + private: |
| + SECMODListLock* lock_; |
| + DISALLOW_COPY_AND_ASSIGN(AutoSECMODListReadLock); |
| +}; |
| + |
| +PK11SlotInfo* FindSlotWithTokenName(const std::string& token_name) { |
| + AutoSECMODListReadLock auto_lock; |
| + SECMODModuleList* head = SECMOD_GetDefaultModuleList(); |
| + for (SECMODModuleList* item = head; item != NULL; item = item->next) { |
| + int slot_count = item->module->loaded ? item->module->slotCount : 0; |
| + for (int i = 0; i < slot_count; i++) { |
| + PK11SlotInfo* slot = item->module->slots[i]; |
| + if (PK11_GetTokenName(slot) == token_name) |
| + return PK11_ReferenceSlot(slot); |
| + } |
| + } |
| return NULL; |
| } |
| + |
| #endif // defined(USE_NSS) |
| // A singleton to initialize/deinitialize NSPR. |
| @@ -167,18 +216,55 @@ LazyInstance<NSPRInitSingleton, LeakyLazyInstanceTraits<NSPRInitSingleton> > |
| class NSSInitSingleton { |
| public: |
| #if defined(OS_CHROMEOS) |
| - void OpenPersistentNSSDB() { |
| + void OpenPersistentNSSDB(bool load_opencryptoki) { |
| if (!chromeos_user_logged_in_) { |
| // GetDefaultConfigDirectory causes us to do blocking IO on UI thread. |
| - // Temporarily allow it until we fix http://crbug.com.70119 |
| + // Temporarily allow it until we fix http://crbug.com/70119 |
| ThreadRestrictions::ScopedAllowIO allow_io; |
| chromeos_user_logged_in_ = true; |
| - real_db_slot_ = OpenUserDB(GetDefaultConfigDirectory(), |
| - "Real NSS database"); |
| + |
| + // This creates a third DB slot in NSS that is read/write, unlike |
| + // the fake root CA cert DB and the "default" crypto key |
| + // provider, which are still read-only (because we initialized |
| + // NSS before we had a cryptohome mounted). |
| + real_db_slot_ = OpenUserDB(GetDefaultConfigDirectory(), kNSSDatabaseName); |
| + } |
| + if (!opencryptoki_module_ && load_opencryptoki) { |
| + // This loads the opencryptoki module so we can talk to the |
| + // hardware TPM. |
| + opencryptoki_module_ = LoadModule( |
| + "opencryptoki", |
| + "/usr/lib/opencryptoki/libopencryptoki.so", |
|
stevenjb
2011/04/05 23:57:25
nit: Maybe define this as a constant at the top of
|
| + // trustOrder=100 -- means it'll select this as the most |
| + // trusted slot for the mechanisms it provides. |
| + // slotParams=... -- selects RSA as only mechanism, and only |
| + // asks for the password when necessary (instead of every |
| + // time, or after a timeout). |
| + "trustOrder=100 slotParams=(1={slotFlags=[RSA] askpw=only})"); |
| + if (opencryptoki_module_) { |
| + // We shouldn't need to initialize the TPM PIN here because |
| + // it'll be taken care of by cryptohomed, but we have to make |
| + // sure that it is initialized. |
| + |
| + // TODO(gspencer): replace this with a dbus call to check and |
| + // see that cryptohomed has initialized the PINs already. |
|
stevenjb
2011/04/05 23:57:25
We probably want the DBUS calls in a function, so
|
| + EnsureTPMInit(); |
| + } |
| } |
| } |
| + |
| + std::string GetTPMTokenName() { |
| + // TODO(gspencer): This should come from the dbus interchange with |
| + // cryptohomed instead of being hard-coded. |
| + return std::string(kTPMTokenName); |
|
stevenjb
2011/04/05 23:57:25
This should be provided to the callback passed to
|
| + } |
| + |
| + PK11SlotInfo* GetTPMSlot() { |
| + return FindSlotWithTokenName(GetTPMTokenName()); |
| + } |
| #endif // defined(OS_CHROMEOS) |
| + |
| bool OpenTestNSSDB(const FilePath& path, const char* description) { |
| test_db_slot_ = OpenUserDB(path, description); |
| return !!test_db_slot_; |
| @@ -194,7 +280,7 @@ class NSSInitSingleton { |
| } |
| } |
| - PK11SlotInfo* GetDefaultKeySlot() { |
| + PK11SlotInfo* GetDefaultNSSKeySlot() { |
| if (test_db_slot_) |
| return PK11_ReferenceSlot(test_db_slot_); |
| if (real_db_slot_) |
| @@ -221,6 +307,7 @@ class NSSInitSingleton { |
| : real_db_slot_(NULL), |
| test_db_slot_(NULL), |
| root_(NULL), |
| + opencryptoki_module_(NULL), |
| chromeos_user_logged_in_(false) { |
| EnsureNSPRInit(); |
| @@ -257,7 +344,7 @@ class NSSInitSingleton { |
| status = NSS_NoDB_Init(NULL); |
| if (status != SECSuccess) { |
| LOG(ERROR) << "Error initializing NSS without a persistent " |
| - "database: NSS error code " << PR_GetError(); |
| + "database: " << GetNSSErrorMessage(); |
| } |
| } else { |
| #if defined(USE_NSS) |
| @@ -280,16 +367,15 @@ class NSSInitSingleton { |
| if (status != SECSuccess) { |
| LOG(ERROR) << "Error initializing NSS with a persistent " |
| "database (" << nss_config_dir |
| - << "): NSS error code " << PR_GetError(); |
| + << "): " << GetNSSErrorMessage(); |
| } |
| } |
| if (status != SECSuccess) { |
| - LOG(WARNING) << "Initialize NSS without a persistent database " |
| - "(~/.pki/nssdb)."; |
| + VLOG(1) << "Initializing NSS without a persistent database."; |
| status = NSS_NoDB_Init(NULL); |
| if (status != SECSuccess) { |
| LOG(ERROR) << "Error initializing NSS without a persistent " |
| - "database: NSS error code " << PR_GetError(); |
| + "database: " << GetNSSErrorMessage(); |
| return; |
| } |
| } |
| @@ -328,15 +414,70 @@ class NSSInitSingleton { |
| SECMOD_DestroyModule(root_); |
| root_ = NULL; |
| } |
| + if (opencryptoki_module_) { |
| + SECMOD_UnloadUserModule(opencryptoki_module_); |
| + SECMOD_DestroyModule(opencryptoki_module_); |
| + opencryptoki_module_ = NULL; |
| + } |
| SECStatus status = NSS_Shutdown(); |
| if (status != SECSuccess) { |
| // We VLOG(1) because this failure is relatively harmless (leaking, but |
| // we're shutting down anyway). |
| - VLOG(1) << "NSS_Shutdown failed; see " |
| - "http://code.google.com/p/chromium/issues/detail?id=4609"; |
| + VLOG(1) << "NSS_Shutdown failed; see http://crbug.com/4609"; |
| + } |
| + } |
| + |
| +#if defined(USE_NSS) |
| + // Load nss's built-in root certs. |
| + SECMODModule* InitDefaultRootCerts() { |
| + SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", NULL); |
| + if (root) |
| + return root; |
| + |
| + // Aw, snap. Can't find/load root cert shared library. |
| + // This will make it hard to talk to anybody via https. |
| + NOTREACHED(); |
| + return NULL; |
| + } |
| + |
| + // Load the given module for this NSS session. |
| + SECMODModule* LoadModule(const char* name, |
| + const char* library_path, |
| + const char* params) { |
| + std::string modparams = StringPrintf( |
| + "name=\"%s\" library=\"%s\" %s", |
| + name, library_path, params ? params : ""); |
| + |
| + // Shouldn't need to const_cast here, but SECMOD doesn't properly |
| + // declare input string arguments as const. Bug |
| + // https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed |
| + // on NSS codebase to address this. |
| + SECMODModule* module = SECMOD_LoadUserModule( |
| + const_cast<char*>(modparams.c_str()), NULL, PR_FALSE); |
| + if (!module) { |
| + LOG(ERROR) << "Error loading " << name << " module into NSS: " |
| + << GetNSSErrorMessage(); |
| + return NULL; |
| + } |
| + return module; |
| + } |
| +#endif |
| + |
| +#if defined(OS_CHROMEOS) |
| + void EnsureTPMInit() { |
| + base::ScopedPK11Slot tpm_slot(GetTPMSlot()); |
| + if (tpm_slot.get()) { |
| + // TODO(gspencer): Remove this in favor of the dbus API for |
| + // cryptohomed when that is available. |
| + if (PK11_NeedUserInit(tpm_slot.get())) { |
| + PK11_InitPin(tpm_slot.get(), |
| + kTPMSecurityOfficerPIN, |
| + kTPMUserPIN); |
| + } |
| } |
| } |
| +#endif |
| static PK11SlotInfo* OpenUserDB(const FilePath& path, |
| const char* description) { |
| @@ -350,7 +491,7 @@ class NSSInitSingleton { |
| } |
| else { |
| LOG(ERROR) << "Error opening persistent database (" << modspec |
| - << "): NSS error code " << PR_GetError(); |
| + << "): " << GetNSSErrorMessage(); |
| } |
| return db_slot; |
| } |
| @@ -360,7 +501,8 @@ class NSSInitSingleton { |
| PK11SlotInfo* real_db_slot_; // Overrides internal key slot if non-NULL. |
| PK11SlotInfo* test_db_slot_; // Overrides internal key slot and real_db_slot_ |
| - SECMODModule *root_; |
| + SECMODModule* root_; |
| + SECMODModule* opencryptoki_module_; |
| bool chromeos_user_logged_in_; |
| #if defined(USE_NSS) |
| // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011 |
| @@ -479,10 +621,14 @@ AutoNSSWriteLock::~AutoNSSWriteLock() { |
| #endif // defined(USE_NSS) |
| #if defined(OS_CHROMEOS) |
| -void OpenPersistentNSSDB() { |
| - g_nss_singleton.Get().OpenPersistentNSSDB(); |
| +void OpenPersistentNSSDB(bool load_opencryptoki) { |
| + g_nss_singleton.Get().OpenPersistentNSSDB(load_opencryptoki); |
| } |
| -#endif |
| + |
| +std::string GetTPMTokenName() { |
| + return g_nss_singleton.Get().GetTPMTokenName(); |
| +} |
| +#endif // defined(OS_CHROMEOS) |
| // TODO(port): Implement this more simply. We can convert by subtracting an |
| // offset (the difference between NSPR's and base::Time's epochs). |
| @@ -504,7 +650,15 @@ Time PRTimeToBaseTime(PRTime prtime) { |
| } |
| PK11SlotInfo* GetDefaultNSSKeySlot() { |
| - return g_nss_singleton.Get().GetDefaultKeySlot(); |
| + return g_nss_singleton.Get().GetDefaultNSSKeySlot(); |
| +} |
| + |
| +PK11SlotInfo* GetPreferredKeySlot() { |
| +#if defined(OS_CHROMEOS) |
| + return g_nss_singleton.Get().GetTPMSlot(); |
| +#else |
| + return GetDefaultNSSKeySlot(); |
| +#endif |
| } |
| } // namespace base |