diff options
27 files changed, 817 insertions, 88 deletions
diff --git a/base/base.gypi b/base/base.gypi index f241967a..27ec764 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -565,6 +565,7 @@ 'crypto/encryptor_nss.cc', 'crypto/encryptor_openssl.cc', 'crypto/encryptor_win.cc', + 'crypto/pk11_blocking_password_delegate.h', 'crypto/rsa_private_key.h', 'crypto/rsa_private_key.cc', 'crypto/rsa_private_key_mac.cc', diff --git a/base/crypto/pk11_blocking_password_delegate.h b/base/crypto/pk11_blocking_password_delegate.h new file mode 100644 index 0000000..c9eb35c --- /dev/null +++ b/base/crypto/pk11_blocking_password_delegate.h @@ -0,0 +1,34 @@ +// Copyright (c) 2010 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 BASE_CRYPTO_PK11_BLOCKING_PASSWORD_DELEGATE_H_ +#define BASE_CRYPTO_PK11_BLOCKING_PASSWORD_DELEGATE_H_ +#pragma once + +#include <string> + +namespace base { + +// PK11_SetPasswordFunc is a global setting. An implementation of +// PK11BlockingPasswordDelegate should be passed as the user data argument +// (|wincx|) to relevant NSS functions, which the global password handler will +// call to do the actual work. +class PK11BlockingPasswordDelegate { + public: + virtual ~PK11BlockingPasswordDelegate() {} + + // Requests a password to unlock |slot_name|. The interface is + // synchronous because NSS cannot issue an asynchronous + // request. |retry| is true if this is a request for the retry + // and we previously returned the wrong password. + // The implementation should set |*cancelled| to true if the user cancelled + // instead of entering a password, otherwise it should return the password the + // user entered. + virtual std::string RequestPassword(const std::string& slot_name, bool retry, + bool* cancelled) = 0; +}; + +} + +#endif // BASE_CRYPTO_PK11_BLOCKING_PASSWORD_DELEGATE_H_ diff --git a/base/nss_util.cc b/base/nss_util.cc index b411422..8fdede3 100644 --- a/base/nss_util.cc +++ b/base/nss_util.cc @@ -29,6 +29,7 @@ // use NSS for crypto or certificate verification, and we don't use the NSS // certificate and key databases. #if defined(USE_NSS) +#include "base/crypto/pk11_blocking_password_delegate.h" #include "base/environment.h" #include "base/lock.h" #include "base/scoped_ptr.h" @@ -69,6 +70,26 @@ FilePath GetInitialConfigDirectory() { #endif // defined(OS_CHROMEOS) } +// This callback for NSS forwards all requests to a caller-specified +// PK11BlockingPasswordDelegate object. +char* PK11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) { + base::PK11BlockingPasswordDelegate* delegate = + reinterpret_cast<base::PK11BlockingPasswordDelegate*>(arg); + if (delegate) { + bool cancelled = false; + std::string password = delegate->RequestPassword(PK11_GetTokenName(slot), + retry != PR_FALSE, + &cancelled); + if (cancelled) + return NULL; + char* result = PORT_Strdup(password.c_str()); + password.replace(0, password.size(), password.size(), 0); + return result; + } + DLOG(ERROR) << "PK11 password requested with NULL arg"; + return NULL; +} + // NSS creates a local cache of the sqlite database if it detects that the // filesystem the database is on is much slower than the local disk. The // detection doesn't work with the latest versions of sqlite, such as 3.6.22 @@ -247,6 +268,8 @@ class NSSInitSingleton { } } + PK11_SetPasswordFunc(PK11PasswordFunc); + // If we haven't initialized the password for the NSS databases, // initialize an empty-string password so that we don't need to // log in. diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 04487da..59f02ee5 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -6504,6 +6504,32 @@ Keep your key file in a safe place. You will need it to create new versions of y Select a certificate to authenticate yourself to <ph name="HOST_NAME">$1<ex>www.google.com</ex></ph> </message> + <!-- NSS PK11 password dialog --> + <message name="IDS_PK11_AUTH_DIALOG_TITLE" desc="The title in the dialog that prompts for security device password."> + Sign in to Security Device + </message> + <message name="IDS_PK11_AUTH_DIALOG_TEXT_KEYGEN" desc="The text in the dialog that prompts for security device password for key generation."> + Please sign in to <ph name="TOKEN_NAME">$1<ex>Software Security Device</ex></ph> to generate a key for <ph name="HOST_NAME">$2<ex>www.google.com</ex></ph>. + </message> + <message name="IDS_PK11_AUTH_DIALOG_TEXT_CERT_ENROLLMENT" desc="The text in the dialog that prompts for security device password for cert enrollment."> + Please sign in to <ph name="TOKEN_NAME">$1<ex>Software Security Device</ex></ph> to import client certificate from <ph name="HOST_NAME">$2<ex>www.google.com</ex></ph>. + </message> + <message name="IDS_PK11_AUTH_DIALOG_TEXT_CLIENT_AUTH" desc="The text in the dialog that prompts for security device password when doing key generation."> + Please sign in to <ph name="TOKEN_NAME">$1<ex>Software Security Device</ex></ph> to authenticate to <ph name="HOST_NAME">$2<ex>www.google.com</ex></ph> with your certificate. + </message> + <message name="IDS_PK11_AUTH_DIALOG_TEXT_CERT_IMPORT" desc="The text in the dialog that prompts for security device password when using the certificate manager to import a client certificate."> + Please sign in to <ph name="TOKEN_NAME">$1<ex>Software Security Device</ex></ph> to import the client certificate. + </message> + <message name="IDS_PK11_AUTH_DIALOG_TEXT_CERT_EXPORT" desc="The text in the dialog that prompts for security device password when using the certificate manager to export a client certificate."> + Please sign in to <ph name="TOKEN_NAME">$1<ex>Software Security Device</ex></ph> to export the client certificate. + </message> + <message name="IDS_PK11_AUTH_DIALOG_PASSWORD_FIELD" desc="Label for password entry in the dialog that prompts for security device password."> + Password: + </message> + <message name="IDS_PK11_AUTH_DIALOG_OK_BUTTON_LABEL" desc="Label for OK button in the dialog that prompts for security device password."> + Unlock + </message> + <!-- Advanced Font/Language settings --> <message name="IDS_FONT_LANGUAGE_SETTING_WINDOWS_TITLE" desc="Title that appears in the dialogue title bar for advanced font/encoding and language settings"> Fonts and Languages diff --git a/chrome/browser/certificate_manager_model.cc b/chrome/browser/certificate_manager_model.cc index 619260e..067a302 100644 --- a/chrome/browser/certificate_manager_model.cc +++ b/chrome/browser/certificate_manager_model.cc @@ -74,9 +74,10 @@ string16 CertificateManagerModel::GetColumnText( return rv; } -int CertificateManagerModel::ImportFromPKCS12(const std::string& data, +int CertificateManagerModel::ImportFromPKCS12(net::CryptoModule* module, + const std::string& data, const string16& password) { - int result = cert_db_.ImportFromPKCS12(data, password); + int result = cert_db_.ImportFromPKCS12(module, data, password); if (result == net::OK) Refresh(); return result; diff --git a/chrome/browser/certificate_manager_model.h b/chrome/browser/certificate_manager_model.h index 2206370..137197d 100644 --- a/chrome/browser/certificate_manager_model.h +++ b/chrome/browser/certificate_manager_model.h @@ -57,7 +57,8 @@ class CertificateManagerModel { // Import certificates from PKCS #12 encoded |data|, using the given // |password|. Returns a net error code on failure. - int ImportFromPKCS12(const std::string& data, const string16& password); + int ImportFromPKCS12(net::CryptoModule* module, const std::string& data, + const string16& password); // Import CA certificates. // Tries to import all the certificates given. The root will be trusted diff --git a/chrome/browser/dom_ui/options/certificate_manager_handler.cc b/chrome/browser/dom_ui/options/certificate_manager_handler.cc index 480fcb4..68a7685 100644 --- a/chrome/browser/dom_ui/options/certificate_manager_handler.cc +++ b/chrome/browser/dom_ui/options/certificate_manager_handler.cc @@ -17,7 +17,9 @@ #include "chrome/browser/gtk/certificate_dialogs.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/browser/ui/pk11_password_dialog.h" #include "grit/generated_resources.h" +#include "net/base/crypto_module.h" #include "net/base/x509_certificate.h" namespace { @@ -527,6 +529,21 @@ void CertificateManagerHandler::ExportPersonalPasswordSelected( ImportExportCleanup(); return; } + + // Currently, we don't support exporting more than one at a time. If we do, + // this would need some cleanup to handle unlocking multiple slots. + DCHECK_EQ(selected_cert_list_.size(), 1U); + + // TODO(mattm): do something smarter about non-extractable keys + browser::UnlockCertSlotIfNecessary( + selected_cert_list_[0].get(), + browser::kPK11PasswordCertExport, + "", // unused. + NewCallback(this, + &CertificateManagerHandler::ExportPersonalSlotsUnlocked)); +} + +void CertificateManagerHandler::ExportPersonalSlotsUnlocked() { std::string output; int num_exported = certificate_manager_model_->cert_db().ExportToPKCS12( selected_cert_list_, @@ -605,7 +622,23 @@ void CertificateManagerHandler::ImportPersonalFileRead( UTF8ToUTF16(safe_strerror(read_errno)))); return; } - int result = certificate_manager_model_->ImportFromPKCS12(data, password_); + + file_data_ = data; + + // TODO(mattm): allow user to choose a slot to import to. + module_ = certificate_manager_model_->cert_db().GetDefaultModule(); + + browser::UnlockSlotIfNecessary( + module_.get(), + browser::kPK11PasswordCertImport, + "", // unused. + NewCallback(this, + &CertificateManagerHandler::ImportPersonalSlotUnlocked)); +} + +void CertificateManagerHandler::ImportPersonalSlotUnlocked() { + int result = certificate_manager_model_->ImportFromPKCS12( + module_, file_data_, password_); ImportExportCleanup(); dom_ui_->CallJavascriptFunction(L"CertificateRestoreOverlay.dismiss"); switch (result) { @@ -634,8 +667,10 @@ void CertificateManagerHandler::CancelImportExportProcess( void CertificateManagerHandler::ImportExportCleanup() { file_path_.clear(); password_.clear(); + file_data_.clear(); selected_cert_list_.clear(); select_file_dialog_ = NULL; + module_ = NULL; } void CertificateManagerHandler::ImportServer(const ListValue* args) { diff --git a/chrome/browser/dom_ui/options/certificate_manager_handler.h b/chrome/browser/dom_ui/options/certificate_manager_handler.h index 3ac3898..33a93a8 100644 --- a/chrome/browser/dom_ui/options/certificate_manager_handler.h +++ b/chrome/browser/dom_ui/options/certificate_manager_handler.h @@ -6,6 +6,8 @@ #define CHROME_BROWSER_DOM_UI_OPTIONS_CERTIFICATE_MANAGER_HANDLER_H_ #pragma once +#include <string> + #include "base/scoped_ptr.h" #include "chrome/browser/cancelable_request.h" #include "chrome/browser/certificate_manager_model.h" @@ -59,13 +61,15 @@ class CertificateManagerHandler : public OptionsPageUIHandler, // selector // 2. user selects file -> ExportPersonalFileSelected -> launches password // dialog - // 3. user enters password -> ExportPersonalPasswordSelected -> exports to - // memory buffer -> starts async write operation - // 4. write finishes (or fails) -> ExportPersonalFileWritten + // 3. user enters password -> ExportPersonalPasswordSelected -> unlock slots + // 4. slots unlocked -> ExportPersonalSlotsUnlocked -> exports to memory + // buffer -> starts async write operation + // 5. write finishes (or fails) -> ExportPersonalFileWritten void ExportPersonal(const ListValue* args); void ExportAllPersonal(const ListValue* args); void ExportPersonalFileSelected(const FilePath& path); void ExportPersonalPasswordSelected(const ListValue* args); + void ExportPersonalSlotsUnlocked(); void ExportPersonalFileWritten(int write_errno, int bytes_written); // Import from PKCS #12 file. The sequence goes like: @@ -75,15 +79,17 @@ class CertificateManagerHandler : public OptionsPageUIHandler, // dialog // 3. user enters password -> ImportPersonalPasswordSelected -> starts async // read operation - // 4. read operation completes -> ImportPersonalFileRead -> attempts to + // 4. read operation completes -> ImportPersonalFileRead -> unlock slot + // 5. slot unlocked -> ImportPersonalSlotUnlocked attempts to // import with previously entered password - // 5a. if import succeeds -> ImportExportCleanup - // 5b. if import fails -> show error, ImportExportCleanup + // 6a. if import succeeds -> ImportExportCleanup + // 6b. if import fails -> show error, ImportExportCleanup // TODO(mattm): allow retrying with different password void StartImportPersonal(const ListValue* args); void ImportPersonalFileSelected(const FilePath& path); void ImportPersonalPasswordSelected(const ListValue* args); void ImportPersonalFileRead(int read_errno, std::string data); + void ImportPersonalSlotUnlocked(); // Import Server certificates from file. Sequence goes like: // 1. user clicks on import button -> ImportServer -> launches file selector @@ -140,8 +146,10 @@ class CertificateManagerHandler : public OptionsPageUIHandler, // wait for file to be read, etc. FilePath file_path_; string16 password_; + std::string file_data_; net::CertificateList selected_cert_list_; scoped_refptr<SelectFileDialog> select_file_dialog_; + scoped_refptr<net::CryptoModule> module_; // Used in reading and writing certificate files. CancelableRequestConsumer consumer_; diff --git a/chrome/browser/gtk/pk11_password_dialog.cc b/chrome/browser/gtk/pk11_password_dialog.cc new file mode 100644 index 0000000..42040b3 --- /dev/null +++ b/chrome/browser/gtk/pk11_password_dialog.cc @@ -0,0 +1,218 @@ +// Copyright (c) 2010 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/ui/pk11_password_dialog.h" + +#include <gtk/gtk.h> + +#include "app/gtk_signal.h" +#include "app/l10n_util.h" +#include "base/basictypes.h" +#include "base/crypto/pk11_blocking_password_delegate.h" +#include "base/synchronization/waitable_event.h" +#include "base/task.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/gtk/gtk_util.h" +#include "googleurl/src/gurl.h" +#include "grit/generated_resources.h" + +namespace { + +class PK11BlockingDialogDelegate : public base::PK11BlockingPasswordDelegate { + public: + PK11BlockingDialogDelegate(browser::PK11PasswordReason reason, + const std::string& server) + : event_(false, false), + reason_(reason), + server_(server), + password_(), + cancelled_(false) { + } + + ~PK11BlockingDialogDelegate() { + password_.replace(0, password_.size(), password_.size(), 0); + } + + // base::PK11BlockingDialogDelegate implementation. + virtual std::string RequestPassword(const std::string& slot_name, bool retry, + bool* cancelled) { + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(!event_.IsSignaled()); + event_.Reset(); + if (BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, &PK11BlockingDialogDelegate::ShowDialog, + slot_name, retry))) { + event_.Wait(); + } + *cancelled = cancelled_; + return password_; + } + + private: + void ShowDialog(const std::string& slot_name, bool retry) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + ShowPK11PasswordDialog( + slot_name, retry, reason_, server_, + NewCallback(this, &PK11BlockingDialogDelegate::GotPassword)); + } + void GotPassword(const char* password) { + if (password) + password_ = password; + else + cancelled_ = true; + event_.Signal(); + } + base::WaitableEvent event_; + browser::PK11PasswordReason reason_; + std::string server_; + std::string password_; + bool cancelled_; + + DISALLOW_COPY_AND_ASSIGN(PK11BlockingDialogDelegate); +}; + +// TODO(mattm): change into a constrained dialog. +class PK11PasswordDialog { + public: + PK11PasswordDialog(const std::string& slot_name, + bool retry, + browser::PK11PasswordReason reason, + const std::string& server, + browser::PK11PasswordCallback* callback); + + void Show(); + + private: + CHROMEGTK_CALLBACK_1(PK11PasswordDialog, void, OnResponse, int); + CHROMEGTK_CALLBACK_0(PK11PasswordDialog, void, OnWindowDestroy); + + scoped_ptr<browser::PK11PasswordCallback> callback_; + + GtkWidget* dialog_; + GtkWidget* password_entry_; + + DISALLOW_COPY_AND_ASSIGN(PK11PasswordDialog); +}; + +PK11PasswordDialog::PK11PasswordDialog(const std::string& slot_name, + bool retry, + browser::PK11PasswordReason reason, + const std::string& server, + browser::PK11PasswordCallback* callback) + : callback_(callback) { + dialog_ = gtk_dialog_new_with_buttons( + l10n_util::GetStringUTF8(IDS_PK11_AUTH_DIALOG_TITLE).c_str(), + NULL, + GTK_DIALOG_NO_SEPARATOR, + NULL); // Populate the buttons later, for control over the OK button. + gtk_dialog_add_button(GTK_DIALOG(dialog_), + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT); + GtkWidget* ok_button = gtk_util::AddButtonToDialog( + dialog_, + l10n_util::GetStringUTF8(IDS_PK11_AUTH_DIALOG_OK_BUTTON_LABEL).c_str(), + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT); + GTK_WIDGET_SET_FLAGS(ok_button, GTK_CAN_DEFAULT); + gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_ACCEPT); + + // Select an appropriate text for the reason. + std::string text; + const string16& server16 = UTF8ToUTF16(server); + const string16& slot16 = UTF8ToUTF16(slot_name); + switch (reason) { + case browser::kPK11PasswordKeygen: + text = l10n_util::GetStringFUTF8(IDS_PK11_AUTH_DIALOG_TEXT_KEYGEN, + slot16, server16); + break; + case browser::kPK11PasswordCertEnrollment: + text = l10n_util::GetStringFUTF8( + IDS_PK11_AUTH_DIALOG_TEXT_CERT_ENROLLMENT, slot16, server16); + break; + case browser::kPK11PasswordClientAuth: + text = l10n_util::GetStringFUTF8(IDS_PK11_AUTH_DIALOG_TEXT_CLIENT_AUTH, + slot16, server16); + break; + case browser::kPK11PasswordCertImport: + text = l10n_util::GetStringFUTF8(IDS_PK11_AUTH_DIALOG_TEXT_CERT_IMPORT, + slot16); + break; + case browser::kPK11PasswordCertExport: + text = l10n_util::GetStringFUTF8(IDS_PK11_AUTH_DIALOG_TEXT_CERT_EXPORT, + slot16); + break; + default: + NOTREACHED(); + } + GtkWidget* label = gtk_label_new(text.c_str()); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_util::LeftAlignMisc(label); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), label, + FALSE, FALSE, 0); + + password_entry_ = gtk_entry_new(); + gtk_entry_set_activates_default(GTK_ENTRY(password_entry_), TRUE); + gtk_entry_set_visibility(GTK_ENTRY(password_entry_), FALSE); + + GtkWidget* password_box = gtk_hbox_new(FALSE, gtk_util::kLabelSpacing); + gtk_box_pack_start(GTK_BOX(password_box), + gtk_label_new(l10n_util::GetStringUTF8( + IDS_PK11_AUTH_DIALOG_PASSWORD_FIELD).c_str()), + FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(password_box), password_entry_, + TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), password_box, + FALSE, FALSE, 0); + + g_signal_connect(dialog_, "response", + G_CALLBACK(OnResponseThunk), this); + g_signal_connect(dialog_, "destroy", + G_CALLBACK(OnWindowDestroyThunk), this); +} + +void PK11PasswordDialog::Show() { + gtk_util::ShowDialog(dialog_); +} + +void PK11PasswordDialog::OnResponse(GtkWidget* dialog, int response_id) { + if (response_id == GTK_RESPONSE_ACCEPT) + callback_->Run(gtk_entry_get_text(GTK_ENTRY(password_entry_))); + else + callback_->Run(static_cast<const char*>(NULL)); + + // This will cause gtk to zero out the buffer. (see + // gtk_entry_buffer_normal_delete_text: + // http://git.gnome.org/browse/gtk+/tree/gtk/gtkentrybuffer.c#n187) + gtk_editable_delete_text(GTK_EDITABLE(password_entry_), 0, -1); + gtk_widget_destroy(GTK_WIDGET(dialog_)); +} + +void PK11PasswordDialog::OnWindowDestroy(GtkWidget* widget) { + delete this; +} + +} // namespace + +// Every post-task we do blocks, so there's no need to ref-count. +DISABLE_RUNNABLE_METHOD_REFCOUNT(PK11BlockingDialogDelegate); + +namespace browser { + +void ShowPK11PasswordDialog(const std::string& slot_name, + bool retry, + PK11PasswordReason reason, + const std::string& server, + PK11PasswordCallback* callback) { + (new PK11PasswordDialog(slot_name, retry, reason, server, callback))->Show(); +} + +base::PK11BlockingPasswordDelegate* NewPK11BlockingDialogDelegate( + PK11PasswordReason reason, + const std::string& server) { + return new PK11BlockingDialogDelegate(reason, server); +} + +} // namespace browser diff --git a/chrome/browser/gtk/ssl_client_certificate_selector.cc b/chrome/browser/gtk/ssl_client_certificate_selector.cc index edf560a..1be694b 100644 --- a/chrome/browser/gtk/ssl_client_certificate_selector.cc +++ b/chrome/browser/gtk/ssl_client_certificate_selector.cc @@ -21,6 +21,7 @@ #include "chrome/browser/gtk/owned_widget_gtk.h" #include "chrome/browser/ssl/ssl_client_auth_handler.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/ui/pk11_password_dialog.h" #include "chrome/common/net/x509_certificate_model.h" #include "gfx/native_widget_types.h" #include "grit/generated_resources.h" @@ -60,6 +61,9 @@ class SSLClientCertificateSelector : public ConstrainedDialogDelegate { static std::string FormatDetailsText( net::X509Certificate::OSCertHandle cert); + // Callback after unlocking certificate slot. + void Unlocked(); + CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnComboBoxChanged); CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnViewClicked); CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnCancelClicked); @@ -301,6 +305,15 @@ std::string SSLClientCertificateSelector::FormatDetailsText( return rv; } +void SSLClientCertificateSelector::Unlocked() { + // TODO(mattm): refactor so we don't need to call GetSelectedCert again. + net::X509Certificate* cert = GetSelectedCert(); + delegate_->CertificateSelected(cert); + delegate_ = NULL; + DCHECK(window_); + window_->CloseConstrainedWindow(); +} + void SSLClientCertificateSelector::OnComboBoxChanged(GtkWidget* combo_box) { int selected = gtk_combo_box_get_active( GTK_COMBO_BOX(cert_combo_box_)); @@ -328,10 +341,12 @@ void SSLClientCertificateSelector::OnCancelClicked(GtkWidget* button) { void SSLClientCertificateSelector::OnOkClicked(GtkWidget* button) { net::X509Certificate* cert = GetSelectedCert(); - delegate_->CertificateSelected(cert); - delegate_ = NULL; - DCHECK(window_); - window_->CloseConstrainedWindow(); + + browser::UnlockCertSlotIfNecessary( + cert, + browser::kPK11PasswordClientAuth, + cert_request_info_->host_and_port, + NewCallback(this, &SSLClientCertificateSelector::Unlocked)); } void SSLClientCertificateSelector::OnPromptShown(GtkWidget* widget, diff --git a/chrome/browser/renderer_host/render_message_filter.cc b/chrome/browser/renderer_host/render_message_filter.cc index a0696b5..3e23ae8 100644 --- a/chrome/browser/renderer_host/render_message_filter.cc +++ b/chrome/browser/renderer_host/render_message_filter.cc @@ -84,6 +84,9 @@ #if defined(OS_WIN) #include "chrome/common/child_process_host.h" #endif +#if defined(USE_NSS) +#include "chrome/browser/ui/pk11_password_dialog.h" +#endif #if defined(USE_TCMALLOC) #include "chrome/browser/browser_about_handler.h" #endif @@ -1378,6 +1381,13 @@ void RenderMessageFilter::OnKeygenOnWorkerThread( // Generate a signed public key and challenge, then send it back. net::KeygenHandler keygen_handler(key_size_in_bits, challenge_string, url); +#if defined(USE_NSS) + // Attach a password delegate so we can authenticate. + keygen_handler.set_pk11_password_delegate( + browser::NewPK11BlockingDialogDelegate(browser::kPK11PasswordKeygen, + url.host())); +#endif // defined(USE_NSS) + ViewHostMsg_Keygen::WriteReplyParams( reply_msg, keygen_handler.GenKeyAndSignChallenge()); diff --git a/chrome/browser/ui/pk11_password_dialog.h b/chrome/browser/ui/pk11_password_dialog.h new file mode 100644 index 0000000..4f0decc --- /dev/null +++ b/chrome/browser/ui/pk11_password_dialog.h @@ -0,0 +1,69 @@ +// Copyright (c) 2010 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 CHROME_BROWSER_UI_PK11_PASSWORD_DIALOG_H_ +#define CHROME_BROWSER_UI_PK11_PASSWORD_DIALOG_H_ +#pragma once + +#include <string> + +#include "base/callback.h" + +namespace base { +class PK11BlockingPasswordDelegate; +} + +namespace net { +class CryptoModule; +class X509Certificate; +} + +namespace browser { + +// An enum to describe the reason for the password request. +enum PK11PasswordReason { + kPK11PasswordKeygen, + kPK11PasswordCertEnrollment, + kPK11PasswordClientAuth, + kPK11PasswordCertImport, + kPK11PasswordCertExport, +}; + +typedef Callback1<const char*>::Type PK11PasswordCallback; + +// Display a dialog, prompting the user to authenticate to unlock +// |module|. |reason| describes the purpose of the authentication and +// affects the message displayed in the dialog. |server| is the name +// of the server which requested the access. +void ShowPK11PasswordDialog(const std::string& module_name, + bool retry, + PK11PasswordReason reason, + const std::string& server, + PK11PasswordCallback* callback); + +// Returns a PK11BlockingPasswordDelegate to open a dialog and block +// until returning. Should only be used on a worker thread. +base::PK11BlockingPasswordDelegate* NewPK11BlockingDialogDelegate( + PK11PasswordReason reason, + const std::string& server); + +// Asynchronously unlock |module|, if necessary. |callback| is called when done +// (regardless if module was successfully unlocked or not). Should only be +// called on UI thread. +void UnlockSlotIfNecessary(net::CryptoModule* module, + browser::PK11PasswordReason reason, + const std::string& server, + Callback0::Type* callback); + +// Asynchronously unlock the |cert|'s module, if necessary. |callback| is +// called when done (regardless if module was successfully unlocked or not). +// Should only be called on UI thread. +void UnlockCertSlotIfNecessary(net::X509Certificate* cert, + browser::PK11PasswordReason reason, + const std::string& server, + Callback0::Type* callback); + +} // namespace browser + +#endif // CHROME_BROWSER_UI_PK11_PASSWORD_DIALOG_H_ diff --git a/chrome/browser/ui/pk11_password_dialog_nss.cc b/chrome/browser/ui/pk11_password_dialog_nss.cc new file mode 100644 index 0000000..1eeeff1 --- /dev/null +++ b/chrome/browser/ui/pk11_password_dialog_nss.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2010 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/ui/pk11_password_dialog.h" + +#include <pk11pub.h> + +#include "base/logging.h" +#include "chrome/browser/browser_thread.h" +#include "net/base/crypto_module.h" +#include "net/base/x509_certificate.h" + +namespace { + +// Basically an asynchronous implementation of NSS's PK11_DoPassword. +// Note: This currently handles only the simple case. See the TODOs in +// GotPassword for what is yet unimplemented. +class SlotUnlocker { + public: + SlotUnlocker(net::CryptoModule* module, + browser::PK11PasswordReason reason, + const std::string& host, + Callback0::Type* callback); + + void Start(); + + private: + void GotPassword(const char* password); + void Done(); + + scoped_refptr<net::CryptoModule> module_; + browser::PK11PasswordReason reason_; + std::string host_; + Callback0::Type* callback_; + PRBool retry_; +}; + +SlotUnlocker::SlotUnlocker(net::CryptoModule* module, + browser::PK11PasswordReason reason, + const std::string& host, + Callback0::Type* callback) + : module_(module), + reason_(reason), + host_(host), + callback_(callback), + retry_(PR_FALSE) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +void SlotUnlocker::Start() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + ShowPK11PasswordDialog( + module_->GetTokenName(), + retry_, + reason_, + host_, + NewCallback(this, &SlotUnlocker::GotPassword)); +} + +void SlotUnlocker::GotPassword(const char* password) { + // TODO(mattm): PK11_DoPassword has something about PK11_Global.verifyPass. + // Do we need it? + // http://mxr.mozilla.org/mozilla/source/security/nss/lib/pk11wrap/pk11auth.c#577 + + if (!password) { + // User cancelled entering password. Oh well. + Done(); + return; + } + + // TODO(mattm): handle protectedAuthPath + SECStatus rv = PK11_CheckUserPassword(module_->os_module_handle(), + password); + if (rv == SECWouldBlock) { + // Incorrect password. Try again. + retry_ = PR_TRUE; + Start(); + return; + } + + // TODO(mattm): PK11_DoPassword calls nssTrustDomain_UpdateCachedTokenCerts on + // non-friendly slots. How important is that? + + // Correct password (SECSuccess) or too many attempts/other failure + // (SECFailure). Either way we're done. + Done(); +} + +void SlotUnlocker::Done() { + callback_->Run(); + delete this; +} + +} // namespace + +namespace browser { + +void UnlockSlotIfNecessary(net::CryptoModule* module, + browser::PK11PasswordReason reason, + const std::string& host, + Callback0::Type* callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc. + if (PK11_NeedLogin(module->os_module_handle()) && + !PK11_IsLoggedIn(module->os_module_handle(), NULL /* wincx */)) { + (new SlotUnlocker(module, reason, host, callback))->Start(); + } else { + callback->Run(); + } +} + +void UnlockCertSlotIfNecessary(net::X509Certificate* cert, + browser::PK11PasswordReason reason, + const std::string& host, + Callback0::Type* callback) { + scoped_refptr<net::CryptoModule> module(net::CryptoModule::CreateFromHandle( + cert->os_cert_handle()->slot)); + UnlockSlotIfNecessary(module.get(), reason, host, callback); +} + +} // namespace browser diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 7df69ae..c09a9a5 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1526,6 +1526,7 @@ 'browser/gtk/owned_widget_gtk.cc', 'browser/gtk/owned_widget_gtk.h', 'browser/gtk/page_info_bubble_gtk.cc', + 'browser/gtk/pk11_password_dialog.cc', 'browser/gtk/process_singleton_dialog.cc', 'browser/gtk/process_singleton_dialog.h', 'browser/gtk/reload_button_gtk.cc', @@ -3095,6 +3096,8 @@ 'browser/ui/options/options_window.h', 'browser/ui/options/show_options_url.cc', 'browser/ui/options/show_options_url.h', + 'browser/ui/pk11_password_dialog.h', + 'browser/ui/pk11_password_dialog_nss.cc', 'browser/ui/status_bubble.h', 'browser/ui/tab_contents/tab_contents_wrapper.cc', 'browser/ui/tab_contents/tab_contents_wrapper.h', @@ -3719,6 +3722,7 @@ 'browser/renderer_host/gpu_view_host.h', 'browser/renderer_host/video_layer_proxy.cc', 'browser/renderer_host/video_layer_proxy.h', + 'browser/ui/pk11_password_dialog_nss.cc', 'browser/ui/views/extensions/extension_view.cc', 'browser/ui/views/extensions/extension_view.h', ], @@ -3896,6 +3900,7 @@ 'browser/importer/nss_decryptor_system_nss.cc', 'browser/importer/nss_decryptor_system_nss.h', 'browser/power_save_blocker_stub.cc', + 'browser/ui/pk11_password_dialog_nss.cc', 'browser/ui/views/keyboard_overlay_delegate.cc', 'browser/ui/views/select_file_dialog.cc', ], @@ -4219,6 +4224,8 @@ ['include', '^browser/gtk/options/'], ['include', '^browser/gtk/owned_widget_gtk.cc'], ['include', '^browser/gtk/owned_widget_gtk.h'], + ['include', '^browser/gtk/pk11_password_dialog.cc'], + ['include', '^browser/gtk/pk11_password_dialog.h'], ['include', '^browser/gtk/popup_blocked_animation_gtk.cc'], ['include', '^browser/gtk/repost_form_warning_gtk.cc'], ['include', '^browser/gtk/repost_form_warning_gtk.h'], @@ -4376,6 +4383,7 @@ 'browser/importer/nss_decryptor.cc', 'browser/importer/nss_decryptor_system_nss.cc', 'browser/importer/nss_decryptor_system_nss.h', + 'browser/ui/pk11_password_dialog_nss.cc', ], }], ], diff --git a/net/base/cert_database.h b/net/base/cert_database.h index 5ffb6a2..69290ab 100644 --- a/net/base/cert_database.h +++ b/net/base/cert_database.h @@ -16,6 +16,7 @@ namespace net { +class CryptoModule; class X509Certificate; typedef std::vector<scoped_refptr<X509Certificate> > CertificateList; @@ -72,10 +73,16 @@ class CertDatabase { // instance of all certificates.) void ListCerts(CertificateList* certs); - // Import certificates and private keys from PKCS #12 blob. + // Get the default module. + // The returned pointer must be stored in a scoped_refptr<CryptoModule>. + CryptoModule* GetDefaultModule() const; + + // Import certificates and private keys from PKCS #12 blob into the module. // Returns OK or a network error code such as ERR_PKCS12_IMPORT_BAD_PASSWORD // or ERR_PKCS12_IMPORT_ERROR. - int ImportFromPKCS12(const std::string& data, const string16& password); + int ImportFromPKCS12(net::CryptoModule* module, + const std::string& data, + const string16& password); // Export the given certificates and private keys into a PKCS #12 blob, // storing into |output|. diff --git a/net/base/cert_database_nss.cc b/net/base/cert_database_nss.cc index a32a7a3..db2c47a 100644 --- a/net/base/cert_database_nss.cc +++ b/net/base/cert_database_nss.cc @@ -12,7 +12,9 @@ #include "base/logging.h" #include "base/nss_util.h" +#include "base/nss_util_internal.h" #include "base/scoped_ptr.h" +#include "net/base/crypto_module.h" #include "net/base/net_errors.h" #include "net/base/x509_certificate.h" #include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h" @@ -106,9 +108,23 @@ void CertDatabase::ListCerts(CertificateList* certs) { CERT_DestroyCertList(cert_list); } +CryptoModule* CertDatabase::GetDefaultModule() const { + CryptoModule* module = + CryptoModule::CreateFromHandle(base::GetDefaultNSSKeySlot()); + // The module is already referenced when returned from GetDefaultNSSKeymodule, + // so we need to deref it once. + PK11_FreeSlot(module->os_module_handle()); + + return module; +} + int CertDatabase::ImportFromPKCS12( - const std::string& data, const string16& password) { - return psm::nsPKCS12Blob_Import(data.data(), data.size(), password); + net::CryptoModule* module, + const std::string& data, + const string16& password) { + return psm::nsPKCS12Blob_Import(module->os_module_handle(), + data.data(), data.size(), + password); } int CertDatabase::ExportToPKCS12( diff --git a/net/base/cert_database_nss_unittest.cc b/net/base/cert_database_nss_unittest.cc index 8e69104..468c870 100644 --- a/net/base/cert_database_nss_unittest.cc +++ b/net/base/cert_database_nss_unittest.cc @@ -20,6 +20,7 @@ #include "net/base/cert_database.h" #include "net/base/cert_status_flags.h" #include "net/base/cert_verify_result.h" +#include "net/base/crypto_module.h" #include "net/base/net_errors.h" #include "net/base/x509_certificate.h" #include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h" @@ -107,21 +108,21 @@ class CertDatabaseNSSTest : public testing::Test { ASSERT_TRUE(temp_db_dir_.CreateUniqueTempDir()); ASSERT_TRUE( base::OpenTestNSSDB(temp_db_dir_.path(), "CertDatabaseNSSTest db")); - slot_.reset(base::GetDefaultNSSKeySlot()); + slot_ = cert_db_.GetDefaultModule(); // Test db should be empty at start of test. - EXPECT_EQ(0U, ListCertsInSlot(slot_.get()).size()); + EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size()); } virtual void TearDown() { // Don't try to cleanup if the setup failed. - ASSERT_TRUE(slot_.get()); + ASSERT_TRUE(slot_->os_module_handle()); - EXPECT_TRUE(CleanupSlotContents(slot_.get())); - EXPECT_EQ(0U, ListCertsInSlot(slot_.get()).size()); + EXPECT_TRUE(CleanupSlotContents(slot_->os_module_handle())); + EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size()); } protected: - base::ScopedPK11Slot slot_; + scoped_refptr<CryptoModule> slot_; CertDatabase cert_db_; private: @@ -142,18 +143,22 @@ TEST_F(CertDatabaseNSSTest, ImportFromPKCS12WrongPassword) { std::string pkcs12_data = ReadTestFile("client.p12"); EXPECT_EQ(ERR_PKCS12_IMPORT_BAD_PASSWORD, - cert_db_.ImportFromPKCS12(pkcs12_data, ASCIIToUTF16(""))); + cert_db_.ImportFromPKCS12(slot_, + pkcs12_data, + ASCIIToUTF16(""))); // Test db should still be empty. - EXPECT_EQ(0U, ListCertsInSlot(slot_.get()).size()); + EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size()); } TEST_F(CertDatabaseNSSTest, ImportFromPKCS12AndExportAgain) { std::string pkcs12_data = ReadTestFile("client.p12"); - EXPECT_EQ(OK, cert_db_.ImportFromPKCS12(pkcs12_data, ASCIIToUTF16("12345"))); + EXPECT_EQ(OK, cert_db_.ImportFromPKCS12(slot_, + pkcs12_data, + ASCIIToUTF16("12345"))); - CertificateList cert_list = ListCertsInSlot(slot_.get()); + CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); scoped_refptr<X509Certificate> cert(cert_list[0]); @@ -184,7 +189,7 @@ TEST_F(CertDatabaseNSSTest, ImportCACert_SSLTrust) { EXPECT_EQ(0U, failed.size()); - CertificateList cert_list = ListCertsInSlot(slot_.get()); + CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); scoped_refptr<X509Certificate> cert(cert_list[0]); EXPECT_EQ("Test CA", cert->subject().common_name); @@ -216,7 +221,7 @@ TEST_F(CertDatabaseNSSTest, ImportCACert_EmailTrust) { EXPECT_EQ(0U, failed.size()); - CertificateList cert_list = ListCertsInSlot(slot_.get()); + CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); scoped_refptr<X509Certificate> cert(cert_list[0]); EXPECT_EQ("Test CA", cert->subject().common_name); @@ -247,7 +252,7 @@ TEST_F(CertDatabaseNSSTest, ImportCACert_ObjSignTrust) { EXPECT_EQ(0U, failed.size()); - CertificateList cert_list = ListCertsInSlot(slot_.get()); + CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); scoped_refptr<X509Certificate> cert(cert_list[0]); EXPECT_EQ("Test CA", cert->subject().common_name); @@ -282,7 +287,7 @@ TEST_F(CertDatabaseNSSTest, ImportCA_NotCACert) { EXPECT_EQ(certs[0], failed[0].certificate); EXPECT_EQ(ERR_IMPORT_CA_CERT_NOT_CA, failed[0].net_error); - EXPECT_EQ(0U, ListCertsInSlot(slot_.get()).size()); + EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size()); } TEST_F(CertDatabaseNSSTest, ImportCACertHierarchy) { @@ -305,7 +310,7 @@ TEST_F(CertDatabaseNSSTest, ImportCACertHierarchy) { EXPECT_EQ("www.us.army.mil", failed[0].certificate->subject().common_name); EXPECT_EQ(ERR_IMPORT_CA_CERT_NOT_CA, failed[0].net_error); - CertificateList cert_list = ListCertsInSlot(slot_.get()); + CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(2U, cert_list.size()); EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name); EXPECT_EQ("DOD CA-17", cert_list[1]->subject().common_name); @@ -322,7 +327,7 @@ TEST_F(CertDatabaseNSSTest, ImportCACertHierarchyDupeRoot) { &failed)); EXPECT_EQ(0U, failed.size()); - CertificateList cert_list = ListCertsInSlot(slot_.get()); + CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name); @@ -342,7 +347,7 @@ TEST_F(CertDatabaseNSSTest, ImportCACertHierarchyDupeRoot) { EXPECT_EQ("www.us.army.mil", failed[1].certificate->subject().common_name); EXPECT_EQ(ERR_IMPORT_CA_CERT_NOT_CA, failed[1].net_error); - cert_list = ListCertsInSlot(slot_.get()); + cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(2U, cert_list.size()); EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name); EXPECT_EQ("DOD CA-17", cert_list[1]->subject().common_name); @@ -364,7 +369,7 @@ TEST_F(CertDatabaseNSSTest, ImportCACertHierarchyUntrusted) { // SEC_ERROR_UNTRUSTED_ISSUER EXPECT_EQ(ERR_FAILED, failed[0].net_error); - CertificateList cert_list = ListCertsInSlot(slot_.get()); + CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name); } @@ -383,7 +388,7 @@ TEST_F(CertDatabaseNSSTest, ImportCACertHierarchyTree) { EXPECT_EQ(0U, failed.size()); - CertificateList cert_list = ListCertsInSlot(slot_.get()); + CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(3U, cert_list.size()); EXPECT_EQ("DOD CA-13", cert_list[0]->subject().common_name); EXPECT_EQ("DoD Root CA 2", cert_list[1]->subject().common_name); @@ -413,7 +418,7 @@ TEST_F(CertDatabaseNSSTest, ImportCACertNotHierarchy) { EXPECT_EQ("DOD CA-17", failed[1].certificate->subject().common_name); EXPECT_EQ(ERR_FAILED, failed[1].net_error); - CertificateList cert_list = ListCertsInSlot(slot_.get()); + CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); EXPECT_EQ("Test CA", cert_list[0]->subject().common_name); } @@ -433,7 +438,7 @@ TEST_F(CertDatabaseNSSTest, ImportServerCert) { EXPECT_EQ(0U, failed.size()); - CertificateList cert_list = ListCertsInSlot(slot_.get()); + CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(2U, cert_list.size()); scoped_refptr<X509Certificate> goog_cert(cert_list[0]); scoped_refptr<X509Certificate> thawte_cert(cert_list[1]); @@ -461,7 +466,7 @@ TEST_F(CertDatabaseNSSTest, ImportServerCert_SelfSigned) { EXPECT_EQ(0U, failed.size()); - CertificateList cert_list = ListCertsInSlot(slot_.get()); + CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle()); ASSERT_EQ(1U, cert_list.size()); scoped_refptr<X509Certificate> puny_cert(cert_list[0]); diff --git a/net/base/crypto_module.h b/net/base/crypto_module.h new file mode 100644 index 0000000..d02bb71 --- /dev/null +++ b/net/base/crypto_module.h @@ -0,0 +1,51 @@ +// Copyright (c) 2010 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_BASE_CRYPTO_MODULE_H_ +#define NET_BASE_CRYPTO_MODULE_H_ +#pragma once + +#include <string> +#include <vector> + +#include "base/ref_counted.h" + +#if defined(USE_NSS) +typedef struct PK11SlotInfoStr PK11SlotInfo; +#endif + +namespace net { + +class CryptoModule; + +typedef std::vector<scoped_refptr<CryptoModule> > CryptoModuleList; + +class CryptoModule : public base::RefCountedThreadSafe<CryptoModule> { + public: +#if defined(USE_NSS) + typedef PK11SlotInfo* OSModuleHandle; +#else + typedef void* OSModuleHandle; +#endif + + OSModuleHandle os_module_handle() const { return module_handle_; } + + std::string GetTokenName() const; + + static CryptoModule* CreateFromHandle(OSModuleHandle handle); + + private: + friend class base::RefCountedThreadSafe<CryptoModule>; + + explicit CryptoModule(OSModuleHandle handle); + ~CryptoModule(); + + OSModuleHandle module_handle_; + + DISALLOW_COPY_AND_ASSIGN(CryptoModule); +}; + +} // namespace net + +#endif // NET_BASE_CRYPTO_MODULE_H_ diff --git a/net/base/crypto_module_nss.cc b/net/base/crypto_module_nss.cc new file mode 100644 index 0000000..df52ae9 --- /dev/null +++ b/net/base/crypto_module_nss.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2010 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/base/crypto_module.h" + +#include <pk11pub.h> + +namespace net { + +std::string CryptoModule::GetTokenName() const { + return PK11_GetTokenName(module_handle_); +} + +// static +CryptoModule* CryptoModule::CreateFromHandle(OSModuleHandle handle) { + return new CryptoModule(handle); +} + +CryptoModule::CryptoModule(OSModuleHandle handle) : module_handle_(handle) { + PK11_ReferenceSlot(module_handle_); +} + +CryptoModule::~CryptoModule() { + PK11_FreeSlot(module_handle_); +} + +} // namespace net diff --git a/net/base/keygen_handler.cc b/net/base/keygen_handler.cc new file mode 100644 index 0000000..771ad73 --- /dev/null +++ b/net/base/keygen_handler.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2010 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/base/keygen_handler.h" + +#if defined(USE_NSS) +#include "base/crypto/pk11_blocking_password_delegate.h" +#endif + +namespace net { + +// The constructor and destructor must be defined in a .cc file so that +// PK11BlockingPasswordDelegate can be forward-declared on platforms which use +// NSS. + +KeygenHandler::KeygenHandler(int key_size_in_bits, + const std::string& challenge, + const GURL& url) + : key_size_in_bits_(key_size_in_bits), + challenge_(challenge), + url_(url), + stores_key_(true) { +} + +KeygenHandler::~KeygenHandler() { +} + +} // namespace net diff --git a/net/base/keygen_handler.h b/net/base/keygen_handler.h index 5ca6027..d12d084 100644 --- a/net/base/keygen_handler.h +++ b/net/base/keygen_handler.h @@ -8,8 +8,16 @@ #include <string> +#include "base/scoped_ptr.h" +#include "build/build_config.h" #include "googleurl/src/gurl.h" +#if defined(USE_NSS) +namespace base { +class PK11BlockingPasswordDelegate; +}; +#endif // defined(USE_NSS) + namespace net { // This class handles keypair generation for generating client @@ -22,9 +30,10 @@ class KeygenHandler { // Creates a handler that will generate a key with the given key size and // incorporate the |challenge| into the Netscape SPKAC structure. The request // for the key originated from |url|. - inline KeygenHandler(int key_size_in_bits, - const std::string& challenge, - const GURL& url); + KeygenHandler(int key_size_in_bits, + const std::string& challenge, + const GURL& url); + ~KeygenHandler(); // Actually generates the key-pair and the cert request (SPKAC), and returns // a base64-encoded string suitable for use as the form value of <keygen>. @@ -33,22 +42,25 @@ class KeygenHandler { // Exposed only for unit tests. void set_stores_key(bool store) { stores_key_ = store;} +#if defined(USE_NSS) + // Register the password delegate to be used if the token is unauthenticated. + // GenKeyAndSignChallenge runs on a worker thread, so using the blocking + // password callback is okay here. + // Takes ownership of the delegate. + void set_pk11_password_delegate(base::PK11BlockingPasswordDelegate* delegate); +#endif // defined(USE_NSS) + private: int key_size_in_bits_; // key size in bits (usually 2048) std::string challenge_; // challenge string sent by server GURL url_; // the URL that requested the key bool stores_key_; // should the generated key-pair be stored persistently? +#if defined(USE_NSS) + // The callback for requesting a password to the PKCS#11 token. + scoped_ptr<base::PK11BlockingPasswordDelegate> pk11_password_delegate_; +#endif // defined(USE_NSS) }; -KeygenHandler::KeygenHandler(int key_size_in_bits, - const std::string& challenge, - const GURL& url) - : key_size_in_bits_(key_size_in_bits), - challenge_(challenge), - url_(url), - stores_key_(true) { -} - } // namespace net #endif // NET_BASE_KEYGEN_HANDLER_H_ diff --git a/net/base/keygen_handler_nss.cc b/net/base/keygen_handler_nss.cc index 215244c..a3505c4 100644 --- a/net/base/keygen_handler_nss.cc +++ b/net/base/keygen_handler_nss.cc @@ -4,6 +4,11 @@ #include "net/base/keygen_handler.h" +#include "base/crypto/pk11_blocking_password_delegate.h" +#include "base/crypto/scoped_nss_types.h" +#include "base/logging.h" +#include "base/nss_util.h" +#include "base/nss_util_internal.h" #include "net/third_party/mozilla_security_manager/nsKeygenHandler.h" // PSM = Mozilla's Personal Security Manager. @@ -12,8 +17,30 @@ namespace psm = mozilla_security_manager; namespace net { std::string KeygenHandler::GenKeyAndSignChallenge() { + // Ensure NSS is initialized. + base::EnsureNSSInit(); + + // TODO(mattm): allow choosing which slot to generate and store the key? + base::ScopedPK11Slot slot(base::GetDefaultNSSKeySlot()); + if (!slot.get()) { + LOG(ERROR) << "Couldn't get internal key slot!"; + return std::string(); + } + + // Authenticate to the token. + if (SECSuccess != PK11_Authenticate(slot.get(), PR_TRUE, + pk11_password_delegate_.get())) { + LOG(ERROR) << "Couldn't authenticate to internal key slot!"; + return std::string(); + } + return psm::GenKeyAndSignChallenge(key_size_in_bits_, challenge_, url_, - stores_key_); + slot.get(), stores_key_); +} + +void KeygenHandler::set_pk11_password_delegate( + base::PK11BlockingPasswordDelegate* delegate) { + pk11_password_delegate_.reset(delegate); } } // namespace net diff --git a/net/net.gyp b/net/net.gyp index 649544a..485ac38 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -54,6 +54,8 @@ 'base/cookie_policy.h', 'base/cookie_store.cc', 'base/cookie_store.h', + 'base/crypto_module.h', + 'base/crypto_module_nss.cc', 'base/data_url.cc', 'base/data_url.h', 'base/directory_lister.cc', @@ -96,6 +98,7 @@ 'base/host_resolver_proc.h', 'base/io_buffer.cc', 'base/io_buffer.h', + 'base/keygen_handler.cc', 'base/keygen_handler.h', 'base/keygen_handler_mac.cc', 'base/keygen_handler_nss.cc', @@ -257,6 +260,7 @@ { # else: OS is not in the above list 'sources!': [ 'base/cert_database_nss.cc', + 'base/crypto_module_nss.cc', 'base/keygen_handler_nss.cc', 'base/test_root_certs_nss.cc', 'base/x509_certificate_nss.cc', @@ -274,6 +278,7 @@ [ 'use_openssl==1', { 'sources!': [ 'base/cert_database_nss.cc', + 'base/crypto_module_nss.cc', 'base/dnssec_keyset.cc', 'base/dnssec_keyset.h', 'base/keygen_handler_nss.cc', diff --git a/net/third_party/mozilla_security_manager/nsKeygenHandler.cpp b/net/third_party/mozilla_security_manager/nsKeygenHandler.cpp index e1aa2e6..c00e58a 100644 --- a/net/third_party/mozilla_security_manager/nsKeygenHandler.cpp +++ b/net/third_party/mozilla_security_manager/nsKeygenHandler.cpp @@ -49,9 +49,7 @@ #include "base/base64.h" #include "base/logging.h" -#include "base/nss_util_internal.h" #include "base/nss_util.h" -#include "base/string_util.h" #include "googleurl/src/gurl.h" namespace { @@ -97,13 +95,13 @@ namespace mozilla_security_manager { std::string GenKeyAndSignChallenge(int key_size_in_bits, const std::string& challenge, const GURL& url, + PK11SlotInfo* slot, bool stores_key) { // Key pair generation mechanism - only RSA is supported at present. PRUint32 keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; // from nss/pkcs11t.h // Temporary structures used for generating the result // in the right format. - PK11SlotInfo *slot = NULL; PK11RSAGenParams rsaKeyGenParams; // Keygen parameters. SECOidTag algTag; // used by SEC_DerSignData(). SECKEYPrivateKey *privateKey = NULL; @@ -120,16 +118,6 @@ std::string GenKeyAndSignChallenge(int key_size_in_bits, std::string result_blob; // the result. - // Ensure NSS is initialized. - base::EnsureNSSInit(); - - slot = base::GetDefaultNSSKeySlot(); - if (!slot) { - LOG(ERROR) << "Couldn't get Internal key slot!"; - isSuccess = false; - goto failure; - } - switch (keyGenMechanism) { case CKM_RSA_PKCS_KEY_PAIR_GEN: rsaKeyGenParams.keySizeInBits = key_size_in_bits; @@ -146,15 +134,6 @@ std::string GenKeyAndSignChallenge(int key_size_in_bits, goto failure; } - // Need to make sure that the token was initialized. - // Assume a null password. - sec_rv = PK11_Authenticate(slot, PR_TRUE, NULL); - if (SECSuccess != sec_rv) { - LOG(ERROR) << "Couldn't initialze PK11 token!"; - isSuccess = false; - goto failure; - } - VLOG(1) << "Creating key pair..."; { base::AutoNSSWriteLock lock; @@ -275,9 +254,6 @@ std::string GenKeyAndSignChallenge(int key_size_in_bits, if (arena) { PORT_FreeArena(arena, PR_TRUE); } - if (slot != NULL) { - PK11_FreeSlot(slot); - } return (isSuccess ? result_blob : std::string()); } diff --git a/net/third_party/mozilla_security_manager/nsKeygenHandler.h b/net/third_party/mozilla_security_manager/nsKeygenHandler.h index ae1f5a3..4c20e56 100644 --- a/net/third_party/mozilla_security_manager/nsKeygenHandler.h +++ b/net/third_party/mozilla_security_manager/nsKeygenHandler.h @@ -43,6 +43,7 @@ #include <string> class GURL; +typedef struct PK11SlotInfoStr PK11SlotInfo; namespace mozilla_security_manager { @@ -55,10 +56,12 @@ namespace mozilla_security_manager { // key_size_in_bits: key size in bits (usually 2048) // challenge: challenge string sent by server // url: the URL which requested the SPKAC +// slot: a slot to generate the key in, should be authenticated // stores_key: should the generated key pair be stored persistently? std::string GenKeyAndSignChallenge(int key_size_in_bits, const std::string& challenge, const GURL& url, + PK11SlotInfo* slot, bool stores_key); } // namespace mozilla_security_manager diff --git a/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp b/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp index cf2b0cf..fcaff60 100644 --- a/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp +++ b/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp @@ -262,17 +262,13 @@ void EnsurePKCS12Init() { } // Based on nsPKCS12Blob::ImportFromFile. -int nsPKCS12Blob_Import(const char* pkcs12_data, +int nsPKCS12Blob_Import(PK11SlotInfo* slot, + const char* pkcs12_data, size_t pkcs12_len, const string16& password) { - base::ScopedPK11Slot slot(base::GetDefaultNSSKeySlot()); - if (!slot.get()) { - LOG(ERROR) << "Couldn't get Internal key slot!"; - return net::ERR_PKCS12_IMPORT_FAILED; - } int rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, false, - slot.get()); + slot); // When the user entered a zero length password: // An empty password should be represented as an empty @@ -283,7 +279,7 @@ int nsPKCS12Blob_Import(const char* pkcs12_data, // without giving a user prompt when trying the different empty password flavors. if (rv == net::ERR_PKCS12_IMPORT_BAD_PASSWORD && password.size() == 0) { rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, true, - slot.get()); + slot); } return rv; } diff --git a/net/third_party/mozilla_security_manager/nsPKCS12Blob.h b/net/third_party/mozilla_security_manager/nsPKCS12Blob.h index 95b8d46..f625b20 100644 --- a/net/third_party/mozilla_security_manager/nsPKCS12Blob.h +++ b/net/third_party/mozilla_security_manager/nsPKCS12Blob.h @@ -45,6 +45,7 @@ #include "base/ref_counted.h" typedef struct CERTCertificateStr CERTCertificate; +typedef struct PK11SlotInfoStr PK11SlotInfo; namespace net { class X509Certificate; typedef std::vector<scoped_refptr<X509Certificate> > CertificateList; @@ -55,9 +56,10 @@ namespace mozilla_security_manager { // Initialize NSS PKCS#12 libs. void EnsurePKCS12Init(); -// Import certificate from PKCS#12 blob. +// Import certificate from PKCS#12 blob into the slot. // Returns a net error code. -int nsPKCS12Blob_Import(const char* pkcs12_data, +int nsPKCS12Blob_Import(PK11SlotInfo* slot, + const char* pkcs12_data, size_t pkcs12_len, const string16& password); |