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