diff options
Diffstat (limited to 'chromeos')
-rw-r--r-- | chromeos/chromeos.gyp | 2 | ||||
-rw-r--r-- | chromeos/cryptohome/cryptohome_library.cc | 10 | ||||
-rw-r--r-- | chromeos/network/cert_loader.cc | 260 | ||||
-rw-r--r-- | chromeos/network/cert_loader.h | 139 |
4 files changed, 406 insertions, 5 deletions
diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp index c7d9f79..d683c44 100644 --- a/chromeos/chromeos.gyp +++ b/chromeos/chromeos.gyp @@ -225,6 +225,8 @@ 'ime/xkeyboard.h', 'login/login_state.cc', 'login/login_state.h', + 'network/cert_loader.cc', + 'network/cert_loader.h', 'network/certificate_handler.cc', 'network/certificate_handler.h', 'network/certificate_pattern.cc', diff --git a/chromeos/cryptohome/cryptohome_library.cc b/chromeos/cryptohome/cryptohome_library.cc index 684fcae..9529836 100644 --- a/chromeos/cryptohome/cryptohome_library.cc +++ b/chromeos/cryptohome/cryptohome_library.cc @@ -23,7 +23,7 @@ namespace chromeos { namespace { const char kStubSystemSalt[] = "stub_system_salt"; -const size_t kKeySize = 16; +const size_t kNonceSize = 16; // Does nothing. Used as a Cryptohome::VoidMethodCallback. void DoNothing(DBusMethodCallStatus call_status) {} @@ -169,10 +169,10 @@ class CryptohomeLibraryImpl : public CryptohomeLibrary { return system_salt_key_.get(); } - crypto::SymmetricKey* PassphraseToKey(const std::string& passprhase, + crypto::SymmetricKey* PassphraseToKey(const std::string& passphrase, const std::string& salt) { return crypto::SymmetricKey::DeriveKeyFromPassword( - crypto::SymmetricKey::AES, passprhase, salt, 1000, 256); + crypto::SymmetricKey::AES, passphrase, salt, 1000, 256); } @@ -185,7 +185,7 @@ class CryptohomeLibraryImpl : public CryptohomeLibrary { LOG(WARNING) << "Failed to initialize Encryptor."; return std::string(); } - std::string nonce = salt.substr(0, kKeySize); + std::string nonce = salt.substr(0, kNonceSize); std::string encoded_token; CHECK(encryptor.SetCounter(nonce)); if (!encryptor.Encrypt(token, &encoded_token)) { @@ -217,7 +217,7 @@ class CryptohomeLibraryImpl : public CryptohomeLibrary { return std::string(); } - std::string nonce = salt.substr(0, kKeySize); + std::string nonce = salt.substr(0, kNonceSize); std::string token; CHECK(encryptor.SetCounter(nonce)); if (!encryptor.Decrypt(encrypted_token, &token)) { diff --git a/chromeos/network/cert_loader.cc b/chromeos/network/cert_loader.cc new file mode 100644 index 0000000..1198acc --- /dev/null +++ b/chromeos/network/cert_loader.cc @@ -0,0 +1,260 @@ +// 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 "chromeos/network/cert_loader.h" + +#include <algorithm> + +#include "base/chromeos/chromeos_version.h" +#include "base/observer_list.h" +#include "base/task_runner_util.h" +#include "base/threading/worker_pool.h" +#include "chromeos/dbus/cryptohome_client.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "crypto/encryptor.h" +#include "crypto/nss_util.h" +#include "crypto/sha2.h" +#include "crypto/symmetric_key.h" +#include "net/cert/nss_cert_database.h" + +namespace chromeos { + +namespace { + +// Delay between certificate requests while waiting for TPM/PKCS#11 init. +const int kRequestDelayMs = 500; + +net::CertificateList* LoadNSSCertificates() { + net::CertificateList* cert_list(new net::CertificateList()); + net::NSSCertDatabase::GetInstance()->ListCerts(cert_list); + return cert_list; +} + +} // namespace + +static CertLoader* g_cert_loader = NULL; + +// static +void CertLoader::Initialize() { + CHECK(!g_cert_loader); + g_cert_loader = new CertLoader(); +} + +// static +void CertLoader::Shutdown() { + CHECK(g_cert_loader); + delete g_cert_loader; + g_cert_loader = NULL; +} + +// static +CertLoader* CertLoader::Get() { + CHECK(g_cert_loader) << "CertLoader::Get() called before Initialize()"; + return g_cert_loader; +} + +// static +bool CertLoader::IsInitialized() { + return g_cert_loader; +} + +CertLoader::CertLoader() + : tpm_token_ready_(false), + certificates_requested_(false), + certificates_loaded_(false), + key_store_loaded_(false), + weak_ptr_factory_(this) { + net::CertDatabase::GetInstance()->AddObserver(this); + LoginState::Get()->AddObserver(this); + if (LoginState::Get()->IsUserLoggedIn()) + RequestCertificates(); +} + +CertLoader::~CertLoader() { + request_task_.Reset(); + net::CertDatabase::GetInstance()->RemoveObserver(this); + LoginState::Get()->RemoveObserver(this); +} + +void CertLoader::AddObserver(CertLoader::Observer* observer) { + observers_.AddObserver(observer); +} + +void CertLoader::RemoveObserver(CertLoader::Observer* observer) { + observers_.RemoveObserver(observer); +} + +bool CertLoader::CertificatesLoading() const { + return certificates_requested_ && !certificates_loaded_; +} + +bool CertLoader::IsHardwareBacked() const { + return !tpm_token_name_.empty(); +} + +void CertLoader::RequestCertificates() { + CHECK(thread_checker_.CalledOnValidThread()); + VLOG(1) << "RequestCertificates: " << LoginState::Get()->IsUserLoggedIn(); + if (!LoginState::Get()->IsUserLoggedIn()) + return; // Certificates will be requested on login. + + if (!key_store_loaded_) { + // Ensure we've opened the real user's key/certificate database. + crypto::OpenPersistentNSSDB(); + + if (base::chromeos::IsRunningOnChromeOS()) + crypto::EnableTPMTokenForNSS(); + + key_store_loaded_ = true; + } + + certificates_requested_ = true; + + VLOG(1) << "Requesting Certificates."; + DBusThreadManager::Get()->GetCryptohomeClient()->TpmIsEnabled( + base::Bind(&CertLoader::OnTpmIsEnabled, + weak_ptr_factory_.GetWeakPtr())); + + return; +} + +void CertLoader::OnTpmIsEnabled(DBusMethodCallStatus call_status, + bool tpm_is_enabled) { + VLOG(1) << "OnTpmIsEnabled: " << tpm_is_enabled; + if (call_status != DBUS_METHOD_CALL_SUCCESS || !tpm_is_enabled) { + // TPM is not enabled, so proceed with empty tpm token name. + VLOG(1) << "TPM not available."; + StartLoadCertificates(); + } else if (tpm_token_ready_) { + // Once the TPM token is ready, initialize it. + InitializeTPMToken(); + } else { + DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11IsTpmTokenReady( + base::Bind(&CertLoader::OnPkcs11IsTpmTokenReady, + weak_ptr_factory_.GetWeakPtr())); + } +} + +void CertLoader::OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status, + bool is_tpm_token_ready) { + VLOG(1) << "OnPkcs11IsTpmTokenReady: " << is_tpm_token_ready; + if (call_status != DBUS_METHOD_CALL_SUCCESS || !is_tpm_token_ready) { + MaybeRetryRequestCertificates(); + return; + } + + // Retrieve token_name_ and user_pin_ here since they will never change + // and CryptohomeClient calls are not thread safe. + DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11GetTpmTokenInfo( + base::Bind(&CertLoader::OnPkcs11GetTpmTokenInfo, + weak_ptr_factory_.GetWeakPtr())); +} + +void CertLoader::OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status, + const std::string& token_name, + const std::string& user_pin) { + VLOG(1) << "OnPkcs11GetTpmTokenInfo: " << token_name; + if (call_status != DBUS_METHOD_CALL_SUCCESS) { + MaybeRetryRequestCertificates(); + return; + } + tpm_token_name_ = token_name; + // TODO(stevenjb): The network code expects a slot ID, not a label. See + // crbug.com/201101. For now, use a hard coded, well known slot instead. + const char kHardcodedTpmSlot[] = "0"; + tpm_token_slot_ = kHardcodedTpmSlot; + tpm_user_pin_ = user_pin; + tpm_token_ready_ = true; + + InitializeTPMToken(); +} + +void CertLoader::InitializeTPMToken() { + VLOG(1) << "InitializeTPMToken"; + if (!crypto::InitializeTPMToken(tpm_token_name_, tpm_user_pin_)) { + MaybeRetryRequestCertificates(); + return; + } + StartLoadCertificates(); +} + +void CertLoader::StartLoadCertificates() { + VLOG(1) << "Start Load Certificates"; + base::PostTaskAndReplyWithResult( + base::WorkerPool::GetTaskRunner(true /* task_is_slow */), + FROM_HERE, + base::Bind(LoadNSSCertificates), + base::Bind(&CertLoader::UpdateCertificates, + weak_ptr_factory_.GetWeakPtr())); +} + +void CertLoader::UpdateCertificates(net::CertificateList* cert_list) { + CHECK(thread_checker_.CalledOnValidThread()); + DCHECK(cert_list); + VLOG(1) << "Update Certificates: " << cert_list->size(); + + // Clear any existing certificates. + cert_list_.swap(*cert_list); + + // cert_list is constructed in LoadCertificates. + delete cert_list; + + // Set loaded state and notify observers. + if (!certificates_loaded_) { + certificates_loaded_ = true; + NotifyCertificatesLoaded(true); + } else { + NotifyCertificatesLoaded(false); + } +} + +void CertLoader::MaybeRetryRequestCertificates() { + if (!request_task_.is_null()) + return; + + // Cryptohome does not notify us when the token is ready, so call + // this again after a delay. + request_task_ = base::Bind(&CertLoader::RequestCertificatesTask, + weak_ptr_factory_.GetWeakPtr()); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + request_task_, + base::TimeDelta::FromMilliseconds(kRequestDelayMs)); +} + +void CertLoader::RequestCertificatesTask() { + // Reset the task to the initial state so is_null() returns true. + request_task_.Reset(); + RequestCertificates(); +} + +void CertLoader::NotifyCertificatesLoaded(bool initial_load) { + FOR_EACH_OBSERVER(Observer, observers_, + OnCertificatesLoaded(cert_list_, initial_load)); +} + +void CertLoader::OnCertTrustChanged(const net::X509Certificate* cert) { +} + +void CertLoader::OnCertAdded(const net::X509Certificate* cert) { + // Only load certificates if we have completed an initial request. + if (!certificates_loaded_) + return; + StartLoadCertificates(); +} + +void CertLoader::OnCertRemoved(const net::X509Certificate* cert) { + // Only load certificates if we have completed an initial request. + if (!certificates_loaded_) + return; + StartLoadCertificates(); +} + +void CertLoader::LoggedInStateChanged(LoginState::LoggedInState state) { + VLOG(1) << "LoggedInStateChanged: " << state; + if (LoginState::Get()->IsUserLoggedIn() && !certificates_requested_) + RequestCertificates(); +} + +} // namespace chromeos diff --git a/chromeos/network/cert_loader.h b/chromeos/network/cert_loader.h new file mode 100644 index 0000000..f3b69ae --- /dev/null +++ b/chromeos/network/cert_loader.h @@ -0,0 +1,139 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_CERT_LOADER_H_ +#define CHROMEOS_NETWORK_CERT_LOADER_H_ + +#include <string> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list_threadsafe.h" +#include "base/threading/thread_checker.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_method_call_status.h" +#include "chromeos/login/login_state.h" +#include "net/cert/cert_database.h" +#include "net/cert/x509_certificate.h" + +namespace crypto { +class SymmetricKey; +} + +namespace chromeos { + +// This class is responsible for initializing the TPM token and loading +// certificates once the TPM is initialized. It is expected to be constructed +// on the UI thread and public methods should all be called from the UI thread. +// When certificates have been loaded (after login completes), or the cert +// database changes, observers are called with OnCertificatesLoaded(). +class CHROMEOS_EXPORT CertLoader : public net::CertDatabase::Observer, + public LoginState::Observer { + public: + class Observer { + public: + virtual ~Observer() {} + + // Called when the certificates, passed for convenience as |cert_list|, + // have completed loading. |initial_load| is true the first time this + // is called. + virtual void OnCertificatesLoaded(const net::CertificateList& cert_list, + bool initial_load) = 0; + + protected: + Observer() {} + + private: + DISALLOW_COPY_AND_ASSIGN(Observer); + }; + + // Manage the global instance. + static void Initialize(); + static void Shutdown(); + static CertLoader* Get(); + static bool IsInitialized(); + + void AddObserver(CertLoader::Observer* observer); + void RemoveObserver(CertLoader::Observer* observer); + + // Returns true when the certificate list has been requested but not loaded. + bool CertificatesLoading() const; + + // Returns true if the TPM is available for hardware-backed certificates. + bool IsHardwareBacked() const; + + bool certificates_loaded() const { return certificates_loaded_; } + + // TPM info is only valid once the TPM is available (IsHardwareBacked is + // true). Otherwise empty strings will be returned. + const std::string& tpm_token_name() const { return tpm_token_name_; } + const std::string& tpm_token_slot() const { return tpm_token_slot_; } + const std::string& tpm_user_pin() const { return tpm_user_pin_; } + + // This will be empty until certificates_loaded() is true. + const net::CertificateList& cert_list() const { return cert_list_; } + + private: + CertLoader(); + virtual ~CertLoader(); + + void RequestCertificates(); + + void OnTpmIsEnabled(DBusMethodCallStatus call_status, + bool tpm_is_enabled); + void OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status, + bool is_tpm_token_ready); + void OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status, + const std::string& token_name, + const std::string& user_pin); + void InitializeTPMToken(); + void StartLoadCertificates(); + void UpdateCertificates(net::CertificateList* cert_list); + void MaybeRetryRequestCertificates(); + void RequestCertificatesTask(); + + void NotifyCertificatesLoaded(bool initial_load); + + // net::CertDatabase::Observer + virtual void OnCertTrustChanged(const net::X509Certificate* cert) OVERRIDE; + virtual void OnCertAdded(const net::X509Certificate* cert) OVERRIDE; + virtual void OnCertRemoved(const net::X509Certificate* cert) OVERRIDE; + + // LoginState::Observer + virtual void LoggedInStateChanged(LoginState::LoggedInState state) OVERRIDE; + + ObserverList<Observer> observers_; + + // Active request task for re-requests while waiting for TPM init. + base::Closure request_task_; + + // Local state. + bool tpm_token_ready_; + bool certificates_requested_; + bool certificates_loaded_; + // The key store for the current user has been loaded. This flag is needed to + // ensure that the key store will not be loaded twice in the policy recovery + // "safe-mode". + bool key_store_loaded_; + + // Cached TPM token info. + std::string tpm_token_name_; + std::string tpm_token_slot_; + std::string tpm_user_pin_; + + // Cached Certificates. + net::CertificateList cert_list_; + + base::ThreadChecker thread_checker_; + + // TODO(stevenjb): Use multiple factories to track callback chains. + base::WeakPtrFactory<CertLoader> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(CertLoader); +}; + +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_CERT_LOADER_H_ |