summaryrefslogtreecommitdiffstats
path: root/chrome/browser
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 /chrome/browser
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
Diffstat (limited to 'chrome/browser')
-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
9 files changed, 494 insertions, 14 deletions
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