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
18namespace {
19
20bool 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.
29class 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
50SlotUnlocker::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
63void 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
94void 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
125void SlotUnlocker::Done() {
126  DCHECK_EQ(current_, modules_.size());
127  callback_->Run();
128  delete this;
129}
130
131}  // namespace
132
133namespace browser {
134
135void 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
149void 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