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.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 #if defined(OS_CHROMEOS) 16 #include "crypto/nss_util.h" 17 #endif 18 19 using content::BrowserThread; 20 21 namespace { 22 23 bool ShouldShowDialog(const net::CryptoModule* module) { 24 // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc. 25 return (PK11_NeedLogin(module->os_module_handle()) && 26 !PK11_IsLoggedIn(module->os_module_handle(), NULL /* wincx */)); 27 } 28 29 // Basically an asynchronous implementation of NSS's PK11_DoPassword. 30 // Note: This currently handles only the simple case. See the TODOs in 31 // GotPassword for what is yet unimplemented. 32 class SlotUnlocker { 33 public: 34 SlotUnlocker(const net::CryptoModuleList& modules, 35 chrome::CryptoModulePasswordReason reason, 36 const std::string& host, 37 const base::Closure& callback); 38 39 void Start(); 40 41 private: 42 void GotPassword(const char* password); 43 void Done(); 44 45 size_t current_; 46 net::CryptoModuleList modules_; 47 chrome::CryptoModulePasswordReason reason_; 48 std::string host_; 49 base::Closure callback_; 50 PRBool retry_; 51 }; 52 53 SlotUnlocker::SlotUnlocker(const net::CryptoModuleList& modules, 54 chrome::CryptoModulePasswordReason reason, 55 const std::string& host, 56 const base::Closure& callback) 57 : current_(0), 58 modules_(modules), 59 reason_(reason), 60 host_(host), 61 callback_(callback), 62 retry_(PR_FALSE) { 63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 64 } 65 66 void SlotUnlocker::Start() { 67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 68 69 for (; current_ < modules_.size(); ++current_) { 70 if (ShouldShowDialog(modules_[current_].get())) { 71 #if defined(OS_CHROMEOS) 72 if (crypto::IsTPMTokenReady()) { 73 std::string token_name; 74 std::string user_pin; 75 crypto::GetTPMTokenInfo(&token_name, &user_pin); 76 if (modules_[current_]->GetTokenName() == token_name) { 77 // The user PIN is a well known secret on this machine, and 78 // the user didn't set it, so we need to fetch the value and 79 // supply it for them here. 80 GotPassword(user_pin.c_str()); 81 return; 82 } 83 } 84 #endif 85 ShowCryptoModulePasswordDialog( 86 modules_[current_]->GetTokenName(), 87 retry_, 88 reason_, 89 host_, 90 base::Bind(&SlotUnlocker::GotPassword, base::Unretained(this))); 91 return; 92 } 93 } 94 Done(); 95 } 96 97 void SlotUnlocker::GotPassword(const char* password) { 98 // TODO(mattm): PK11_DoPassword has something about PK11_Global.verifyPass. 99 // Do we need it? 100 // http://mxr.mozilla.org/mozilla/source/security/nss/lib/pk11wrap/pk11auth.c#577 101 102 if (!password) { 103 // User cancelled entering password. Oh well. 104 ++current_; 105 Start(); 106 return; 107 } 108 109 // TODO(mattm): handle protectedAuthPath 110 SECStatus rv = PK11_CheckUserPassword(modules_[current_]->os_module_handle(), 111 password); 112 if (rv == SECWouldBlock) { 113 // Incorrect password. Try again. 114 retry_ = PR_TRUE; 115 Start(); 116 return; 117 } 118 119 // TODO(mattm): PK11_DoPassword calls nssTrustDomain_UpdateCachedTokenCerts on 120 // non-friendly slots. How important is that? 121 122 // Correct password (SECSuccess) or too many attempts/other failure 123 // (SECFailure). Either way we're done with this slot. 124 ++current_; 125 Start(); 126 } 127 128 void SlotUnlocker::Done() { 129 DCHECK_EQ(current_, modules_.size()); 130 callback_.Run(); 131 delete this; 132 } 133 134 } // namespace 135 136 namespace chrome { 137 138 void UnlockSlotsIfNecessary(const net::CryptoModuleList& modules, 139 chrome::CryptoModulePasswordReason reason, 140 const std::string& host, 141 const base::Closure& callback) { 142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 143 for (size_t i = 0; i < modules.size(); ++i) { 144 if (ShouldShowDialog(modules[i].get())) { 145 (new SlotUnlocker(modules, reason, host, callback))->Start(); 146 return; 147 } 148 } 149 callback.Run(); 150 } 151 152 void UnlockCertSlotIfNecessary(net::X509Certificate* cert, 153 chrome::CryptoModulePasswordReason reason, 154 const std::string& host, 155 const base::Closure& callback) { 156 net::CryptoModuleList modules; 157 modules.push_back(net::CryptoModule::CreateFromHandle( 158 cert->os_cert_handle()->slot)); 159 UnlockSlotsIfNecessary(modules, reason, host, callback); 160 } 161 162 } // namespace chrome 163