Home | History | Annotate | Download | only in ui
      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