summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-13 01:48:43 +0000
committermattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-13 01:48:43 +0000
commit88b9db7d713a9e156fa66694844c4d98ee48d875 (patch)
treea06b93ff3319bc3512051372ecadd693ebeeaf80
parent6fd024b93e4a708a767c1892e5091e3585a5c72f (diff)
downloadchromium_src-88b9db7d713a9e156fa66694844c4d98ee48d875.zip
chromium_src-88b9db7d713a9e156fa66694844c4d98ee48d875.tar.gz
chromium_src-88b9db7d713a9e156fa66694844c4d98ee48d875.tar.bz2
NSS: PKCS 11 password prompt.
This was based off of davidben's WIP cl http://codereview.chromium.org/3186021/show. BUG=42073 TEST=add password to NSS DB with "certutil -d sql:.pki/nssdb -W", try client auth, <keygen>, cert manager Review URL: http://codereview.chromium.org/5686002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71281 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/base.gypi1
-rw-r--r--base/crypto/pk11_blocking_password_delegate.h34
-rw-r--r--base/nss_util.cc23
-rw-r--r--chrome/app/generated_resources.grd26
-rw-r--r--chrome/browser/certificate_manager_model.cc5
-rw-r--r--chrome/browser/certificate_manager_model.h3
-rw-r--r--chrome/browser/dom_ui/options/certificate_manager_handler.cc37
-rw-r--r--chrome/browser/dom_ui/options/certificate_manager_handler.h20
-rw-r--r--chrome/browser/gtk/pk11_password_dialog.cc218
-rw-r--r--chrome/browser/gtk/ssl_client_certificate_selector.cc23
-rw-r--r--chrome/browser/renderer_host/render_message_filter.cc10
-rw-r--r--chrome/browser/ui/pk11_password_dialog.h69
-rw-r--r--chrome/browser/ui/pk11_password_dialog_nss.cc123
-rw-r--r--chrome/chrome_browser.gypi8
-rw-r--r--net/base/cert_database.h11
-rw-r--r--net/base/cert_database_nss.cc20
-rw-r--r--net/base/cert_database_nss_unittest.cc49
-rw-r--r--net/base/crypto_module.h51
-rw-r--r--net/base/crypto_module_nss.cc28
-rw-r--r--net/base/keygen_handler.cc29
-rw-r--r--net/base/keygen_handler.h36
-rw-r--r--net/base/keygen_handler_nss.cc29
-rw-r--r--net/net.gyp5
-rw-r--r--net/third_party/mozilla_security_manager/nsKeygenHandler.cpp26
-rw-r--r--net/third_party/mozilla_security_manager/nsKeygenHandler.h3
-rw-r--r--net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp12
-rw-r--r--net/third_party/mozilla_security_manager/nsPKCS12Blob.h6
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);