// Copyright (c) 2011 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/crypto_module_password_dialog_nss.h" #include #include "base/bind.h" #include "base/logging.h" #include "content/public/browser/browser_thread.h" #include "net/base/crypto_module.h" #include "net/cert/x509_certificate.h" using content::BrowserThread; namespace { bool ShouldShowDialog(const net::CryptoModule* module) { // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc. return (PK11_NeedLogin(module->os_module_handle()) && !PK11_IsLoggedIn(module->os_module_handle(), NULL /* wincx */)); } // 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(const net::CryptoModuleList& modules, chrome::CryptoModulePasswordReason reason, const net::HostPortPair& server, gfx::NativeWindow parent, const base::Closure& callback); void Start(); private: void GotPassword(const std::string& password); void Done(); size_t current_; net::CryptoModuleList modules_; chrome::CryptoModulePasswordReason reason_; net::HostPortPair server_; gfx::NativeWindow parent_; base::Closure callback_; PRBool retry_; }; SlotUnlocker::SlotUnlocker(const net::CryptoModuleList& modules, chrome::CryptoModulePasswordReason reason, const net::HostPortPair& server, gfx::NativeWindow parent, const base::Closure& callback) : current_(0), modules_(modules), reason_(reason), server_(server), parent_(parent), callback_(callback), retry_(PR_FALSE) { DCHECK_CURRENTLY_ON(BrowserThread::UI); } void SlotUnlocker::Start() { DCHECK_CURRENTLY_ON(BrowserThread::UI); for (; current_ < modules_.size(); ++current_) { if (ShouldShowDialog(modules_[current_].get())) { ShowCryptoModulePasswordDialog( modules_[current_]->GetTokenName(), retry_, reason_, server_.host(), parent_, base::Bind(&SlotUnlocker::GotPassword, base::Unretained(this))); return; } } Done(); } void SlotUnlocker::GotPassword(const std::string& 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.empty()) { // User cancelled entering password. Oh well. ++current_; Start(); return; } // TODO(mattm): handle protectedAuthPath SECStatus rv = PK11_CheckUserPassword(modules_[current_]->os_module_handle(), password.c_str()); 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 with this slot. ++current_; Start(); } void SlotUnlocker::Done() { DCHECK_EQ(current_, modules_.size()); callback_.Run(); delete this; } } // namespace namespace chrome { void UnlockSlotsIfNecessary(const net::CryptoModuleList& modules, chrome::CryptoModulePasswordReason reason, const net::HostPortPair& server, gfx::NativeWindow parent, const base::Closure& callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); for (size_t i = 0; i < modules.size(); ++i) { if (ShouldShowDialog(modules[i].get())) { (new SlotUnlocker(modules, reason, server, parent, callback))->Start(); return; } } callback.Run(); } void UnlockCertSlotIfNecessary(net::X509Certificate* cert, chrome::CryptoModulePasswordReason reason, const net::HostPortPair& server, gfx::NativeWindow parent, const base::Closure& callback) { net::CryptoModuleList modules; modules.push_back(net::CryptoModule::CreateFromHandle( cert->os_cert_handle()->slot)); UnlockSlotsIfNecessary(modules, reason, server, parent, callback); } } // namespace chrome