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_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