1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/crypto_module_password_dialog_nss.h" 6 7 #include <pk11pub.h> 8 9 #include "base/bind.h" 10 #include "base/logging.h" 11 #include "content/public/browser/browser_thread.h" 12 #include "net/base/crypto_module.h" 13 #include "net/cert/x509_certificate.h" 14 15 using content::BrowserThread; 16 17 namespace { 18 19 bool ShouldShowDialog(const net::CryptoModule* module) { 20 // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc. 21 return (PK11_NeedLogin(module->os_module_handle()) && 22 !PK11_IsLoggedIn(module->os_module_handle(), NULL /* wincx */)); 23 } 24 25 // Basically an asynchronous implementation of NSS's PK11_DoPassword. 26 // Note: This currently handles only the simple case. See the TODOs in 27 // GotPassword for what is yet unimplemented. 28 class SlotUnlocker { 29 public: 30 SlotUnlocker(const net::CryptoModuleList& modules, 31 chrome::CryptoModulePasswordReason reason, 32 const net::HostPortPair& server, 33 gfx::NativeWindow parent, 34 const base::Closure& callback); 35 36 void Start(); 37 38 private: 39 void GotPassword(const std::string& password); 40 void Done(); 41 42 size_t current_; 43 net::CryptoModuleList modules_; 44 chrome::CryptoModulePasswordReason reason_; 45 net::HostPortPair server_; 46 gfx::NativeWindow parent_; 47 base::Closure callback_; 48 PRBool retry_; 49 }; 50 51 SlotUnlocker::SlotUnlocker(const net::CryptoModuleList& modules, 52 chrome::CryptoModulePasswordReason reason, 53 const net::HostPortPair& server, 54 gfx::NativeWindow parent, 55 const base::Closure& callback) 56 : current_(0), 57 modules_(modules), 58 reason_(reason), 59 server_(server), 60 parent_(parent), 61 callback_(callback), 62 retry_(PR_FALSE) { 63 DCHECK_CURRENTLY_ON(BrowserThread::UI); 64 } 65 66 void SlotUnlocker::Start() { 67 DCHECK_CURRENTLY_ON(BrowserThread::UI); 68 69 for (; current_ < modules_.size(); ++current_) { 70 if (ShouldShowDialog(modules_[current_].get())) { 71 ShowCryptoModulePasswordDialog( 72 modules_[current_]->GetTokenName(), 73 retry_, 74 reason_, 75 server_.host(), 76 parent_, 77 base::Bind(&SlotUnlocker::GotPassword, base::Unretained(this))); 78 return; 79 } 80 } 81 Done(); 82 } 83 84 void SlotUnlocker::GotPassword(const std::string& password) { 85 // TODO(mattm): PK11_DoPassword has something about PK11_Global.verifyPass. 86 // Do we need it? 87 // http://mxr.mozilla.org/mozilla/source/security/nss/lib/pk11wrap/pk11auth.c#577 88 89 if (password.empty()) { 90 // User cancelled entering password. Oh well. 91 ++current_; 92 Start(); 93 return; 94 } 95 96 // TODO(mattm): handle protectedAuthPath 97 SECStatus rv = PK11_CheckUserPassword(modules_[current_]->os_module_handle(), 98 password.c_str()); 99 if (rv == SECWouldBlock) { 100 // Incorrect password. Try again. 101 retry_ = PR_TRUE; 102 Start(); 103 return; 104 } 105 106 // TODO(mattm): PK11_DoPassword calls nssTrustDomain_UpdateCachedTokenCerts on 107 // non-friendly slots. How important is that? 108 109 // Correct password (SECSuccess) or too many attempts/other failure 110 // (SECFailure). Either way we're done with this slot. 111 ++current_; 112 Start(); 113 } 114 115 void SlotUnlocker::Done() { 116 DCHECK_EQ(current_, modules_.size()); 117 callback_.Run(); 118 delete this; 119 } 120 121 } // namespace 122 123 namespace chrome { 124 125 void UnlockSlotsIfNecessary(const net::CryptoModuleList& modules, 126 chrome::CryptoModulePasswordReason reason, 127 const net::HostPortPair& server, 128 gfx::NativeWindow parent, 129 const base::Closure& callback) { 130 DCHECK_CURRENTLY_ON(BrowserThread::UI); 131 for (size_t i = 0; i < modules.size(); ++i) { 132 if (ShouldShowDialog(modules[i].get())) { 133 (new SlotUnlocker(modules, reason, server, parent, callback))->Start(); 134 return; 135 } 136 } 137 callback.Run(); 138 } 139 140 void UnlockCertSlotIfNecessary(net::X509Certificate* cert, 141 chrome::CryptoModulePasswordReason reason, 142 const net::HostPortPair& server, 143 gfx::NativeWindow parent, 144 const base::Closure& callback) { 145 net::CryptoModuleList modules; 146 modules.push_back(net::CryptoModule::CreateFromHandle( 147 cert->os_cert_handle()->slot)); 148 UnlockSlotsIfNecessary(modules, reason, server, parent, callback); 149 } 150 151 } // namespace chrome 152