OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 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/net/nss_slot_factory.h" |
| 6 |
| 7 #include <map> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/file_util.h" |
| 11 #include "base/files/file_path.h" |
| 12 #include "base/lazy_instance.h" |
| 13 #include "base/location.h" |
| 14 #include "base/logging.h" |
| 15 #include "base/memory/weak_ptr.h" |
| 16 #include "base/stl_util.h" |
| 17 #include "chrome/browser/profiles/profile_io_data.h" |
| 18 #include "content/public/browser/browser_thread.h" |
| 19 #include "content/public/browser/resource_context.h" |
| 20 #include "crypto/nss_util.h" |
| 21 #include "crypto/nss_util_internal.h" |
| 22 #include "net/cert/nss_cert_database.h" |
| 23 |
| 24 #if defined(OS_CHROMEOS) |
| 25 #include "chromeos/dbus/cryptohome_client.h" |
| 26 #include "chromeos/dbus/dbus_thread_manager.h" |
| 27 #include "chrome/browser/chromeos/net/nss_cert_database_cros.h" |
| 28 #endif |
| 29 |
| 30 using content::BrowserThread; |
| 31 |
| 32 namespace { |
| 33 |
| 34 #if defined(OS_CHROMEOS) |
| 35 void OnPkcs11GetTpmTokenInfoOnUIThread( |
| 36 const base::Callback<void(int)>& callback, |
| 37 chromeos::DBusMethodCallStatus call_status, |
| 38 const std::string& label, |
| 39 const std::string& user_pin, |
| 40 int slot) { |
| 41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 42 LOG(WARNING) << __func__ << " " << label << " " << slot; |
| 43 BrowserThread::PostTask( |
| 44 BrowserThread::IO, FROM_HERE, base::Bind(callback, slot)); |
| 45 } |
| 46 |
| 47 void GetTpmInfoForUserOnUIThread(const std::string& user_name, |
| 48 const base::Callback<void(int)>& callback) { |
| 49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 50 LOG(WARNING) << __func__ << " " << user_name; |
| 51 chromeos::DBusThreadManager::Get() |
| 52 ->GetCryptohomeClient() |
| 53 ->Pkcs11GetTpmTokenInfoForUser( |
| 54 user_name, |
| 55 base::Bind(&OnPkcs11GetTpmTokenInfoOnUIThread, callback)); |
| 56 } |
| 57 |
| 58 // NSSSlotService initializes and holds the public and private key slots for a |
| 59 // given profile. |
| 60 class NSSSlotService { |
| 61 public: |
| 62 NSSSlotService(const base::FilePath& user_dir, |
| 63 const std::string& user_name); |
| 64 virtual ~NSSSlotService(); |
| 65 |
| 66 crypto::ScopedPK11Slot GetPublicNSSKeySlot(); |
| 67 crypto::ScopedPK11Slot GetPrivateNSSKeySlot(); |
| 68 void OnPrivateNSSKeySlotForResourceContextReady( |
| 69 const base::Callback<void(crypto::ScopedPK11Slot)>& callback); |
| 70 void OnNSSCertDatabaseForResourceContextReady( |
| 71 const base::Callback<void(net::NSSCertDatabase*)>& callback); |
| 72 |
| 73 private: |
| 74 void OnGotTpmSlot(int slot); |
| 75 void StartTpmSlotInitializion(); |
| 76 |
| 77 base::FilePath user_dir_; |
| 78 std::string user_name_; |
| 79 crypto::ScopedPK11Slot public_slot_; |
| 80 crypto::ScopedPK11Slot private_slot_; |
| 81 scoped_ptr<chromeos::NSSCertDatabaseCros> cert_db_; |
| 82 |
| 83 std::vector<base::Callback<void(crypto::ScopedPK11Slot)> > |
| 84 tpm_ready_callback_list_; |
| 85 std::vector<base::Callback<void(net::NSSCertDatabase*)> > |
| 86 cert_db_ready_callback_list_; |
| 87 base::WeakPtrFactory<NSSSlotService> weak_ptr_factory_; |
| 88 |
| 89 DISALLOW_COPY_AND_ASSIGN(NSSSlotService); |
| 90 }; |
| 91 |
| 92 // XXX |
| 93 // If the profile directory is /home/chronos/u-xxxxxx, the nss db will be at |
| 94 // /home/chronos/u-xxxxxx/.pki/nssdb. You can get the profile directory for a |
| 95 // given username if you need it by calling libchromeos' GetUserPath(). |
| 96 // |
| 97 NSSSlotService::NSSSlotService(const base::FilePath& user_dir, |
| 98 const std::string& user_name) |
| 99 : user_dir_(user_dir), |
| 100 user_name_(user_name), |
| 101 weak_ptr_factory_(this) { |
| 102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 103 // XXX should we do something to check that we aren't reopening the same |
| 104 // database used by the default public slot? |
| 105 public_slot_.reset(crypto::OpenPersistentNSSDBForPath(user_dir_)); |
| 106 LOG(WARNING) << "pub: " << public_slot_.get(); |
| 107 if (crypto::IsTPMTokenEnabledForNSS()) { |
| 108 if (crypto::IsTPMTokenReady()) { |
| 109 StartTpmSlotInitializion(); |
| 110 } else { |
| 111 LOG(WARNING) << "waiting for tpm ready ..."; |
| 112 crypto::OnTPMReady(base::Bind(&NSSSlotService::StartTpmSlotInitializion, |
| 113 weak_ptr_factory_.GetWeakPtr())); |
| 114 } |
| 115 } else { |
| 116 LOG(WARNING) << "using software private slot"; |
| 117 private_slot_ = GetPublicNSSKeySlot(); |
| 118 } |
| 119 crypto::DumpNSSSlotInfos(); |
| 120 } |
| 121 |
| 122 NSSSlotService::~NSSSlotService() { |
| 123 LOG(WARNING) << __func__; |
| 124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 125 // Don't close when NSS is < 3.15.1, because it would require an additional |
| 126 // sleep for 1 second after closing the database, due to |
| 127 // http://bugzil.la/875601. |
| 128 if (NSS_VersionCheck("3.15.1")) { |
| 129 if (public_slot_) { |
| 130 LOG(WARNING) << "closing nssdb"; |
| 131 SECStatus status = SECMOD_CloseUserDB(public_slot_.get()); |
| 132 if (status != SECSuccess) |
| 133 PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError(); |
| 134 } |
| 135 } |
| 136 } |
| 137 |
| 138 crypto::ScopedPK11Slot NSSSlotService::GetPublicNSSKeySlot() { |
| 139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 140 return crypto::ScopedPK11Slot( |
| 141 public_slot_ ? PK11_ReferenceSlot(public_slot_.get()) : NULL); |
| 142 } |
| 143 |
| 144 crypto::ScopedPK11Slot NSSSlotService::GetPrivateNSSKeySlot() { |
| 145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 146 return crypto::ScopedPK11Slot( |
| 147 private_slot_ ? PK11_ReferenceSlot(private_slot_.get()) : NULL); |
| 148 } |
| 149 |
| 150 void NSSSlotService::OnPrivateNSSKeySlotForResourceContextReady( |
| 151 const base::Callback<void(crypto::ScopedPK11Slot)>& callback) { |
| 152 VLOG(1) << __func__; |
| 153 if (private_slot_) |
| 154 callback.Run(GetPrivateNSSKeySlot()); |
| 155 else |
| 156 tpm_ready_callback_list_.push_back(callback); |
| 157 } |
| 158 |
| 159 void NSSSlotService::OnNSSCertDatabaseForResourceContextReady( |
| 160 const base::Callback<void(net::NSSCertDatabase*)>& callback) { |
| 161 if (cert_db_) |
| 162 callback.Run(cert_db_.get()); |
| 163 else |
| 164 cert_db_ready_callback_list_.push_back(callback); |
| 165 } |
| 166 |
| 167 void NSSSlotService::OnGotTpmSlot(int slot) { |
| 168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 169 LOG(WARNING) << __func__ << " " << slot; |
| 170 if (slot >= 0) { |
| 171 private_slot_.reset(crypto::GetTPMSlotForId(slot)); |
| 172 LOG(WARNING) << " private_slot_ " << private_slot_.get(); |
| 173 cert_db_.reset(new chromeos::NSSCertDatabaseCros(GetPublicNSSKeySlot(), |
| 174 GetPrivateNSSKeySlot())); |
| 175 |
| 176 for (std::vector<base::Callback<void(crypto::ScopedPK11Slot)> >::iterator |
| 177 i = tpm_ready_callback_list_.begin(); |
| 178 i != tpm_ready_callback_list_.end(); |
| 179 ++i) { |
| 180 (*i).Run(GetPrivateNSSKeySlot()); |
| 181 } |
| 182 tpm_ready_callback_list_.clear(); |
| 183 |
| 184 for (std::vector<base::Callback<void(net::NSSCertDatabase*)> >::iterator |
| 185 i = cert_db_ready_callback_list_.begin(); |
| 186 i != cert_db_ready_callback_list_.end(); |
| 187 ++i) { |
| 188 (*i).Run(cert_db_.get()); |
| 189 } |
| 190 cert_db_ready_callback_list_.clear(); |
| 191 } |
| 192 crypto::DumpNSSSlotInfos(); |
| 193 } |
| 194 |
| 195 void NSSSlotService::StartTpmSlotInitializion() { |
| 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 197 LOG(WARNING) << __func__; |
| 198 |
| 199 BrowserThread::PostTask( |
| 200 BrowserThread::UI, |
| 201 FROM_HERE, |
| 202 base::Bind(&GetTpmInfoForUserOnUIThread, |
| 203 user_name_, |
| 204 base::Bind(&NSSSlotService::OnGotTpmSlot, |
| 205 weak_ptr_factory_.GetWeakPtr()))); |
| 206 } |
| 207 |
| 208 class NSSSlotFactory { |
| 209 public: |
| 210 NSSSlotService* GetService(content::ResourceContext* context); |
| 211 |
| 212 ~NSSSlotFactory(); |
| 213 |
| 214 private: |
| 215 friend struct base::DefaultLazyInstanceTraits<NSSSlotFactory>; |
| 216 |
| 217 std::map<std::string, NSSSlotService*> service_map_; |
| 218 }; |
| 219 |
| 220 base::LazyInstance<NSSSlotFactory>::Leaky |
| 221 g_nss_slot_factory = LAZY_INSTANCE_INITIALIZER; |
| 222 |
| 223 NSSSlotService* NSSSlotFactory::GetService(content::ResourceContext* context) { |
| 224 ProfileIOData* io_data = ProfileIOData::FromResourceContext(context); |
| 225 std::string user_name = io_data->user_name(); |
| 226 if (user_name.empty()) |
| 227 return NULL; |
| 228 |
| 229 std::map<std::string, NSSSlotService*>::iterator i = |
| 230 service_map_.find(user_name); |
| 231 if (i != service_map_.end()) |
| 232 return (*i).second; |
| 233 |
| 234 NSSSlotService* service = |
| 235 new NSSSlotService(io_data->profile_path(), io_data->user_name()); |
| 236 service_map_[user_name] = service; |
| 237 return service; |
| 238 } |
| 239 |
| 240 // XXX this will be run on UI thread due to lazyinstance atexit thingy? |
| 241 // XXX made lazyinstance leaky ... so it won't be run at all, like nss_util |
| 242 NSSSlotFactory::~NSSSlotFactory() { |
| 243 LOG(WARNING) << __func__; |
| 244 STLDeleteValues(&service_map_); |
| 245 } |
| 246 #endif // defined(OS_CHROMEOS) |
| 247 |
| 248 void PassTPMToCallback( |
| 249 const base::Callback<void(crypto::ScopedPK11Slot)>& callback) { |
| 250 callback.Run(crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot())); |
| 251 } |
| 252 |
| 253 } // namespace |
| 254 |
| 255 crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext( |
| 256 content::ResourceContext* context) { |
| 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 258 #if defined(OS_CHROMEOS) |
| 259 NSSSlotService* service = g_nss_slot_factory.Get().GetService(context); |
| 260 if (service) |
| 261 return service->GetPublicNSSKeySlot(); |
| 262 #endif |
| 263 return crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot()); |
| 264 } |
| 265 |
| 266 crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext( |
| 267 content::ResourceContext* context) { |
| 268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 269 #if defined(OS_CHROMEOS) |
| 270 NSSSlotService* service = g_nss_slot_factory.Get().GetService(context); |
| 271 if (service) |
| 272 return service->GetPrivateNSSKeySlot(); |
| 273 #endif |
| 274 return crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot()); |
| 275 } |
| 276 |
| 277 void OnPrivateNSSKeySlotForResourceContextReady( |
| 278 content::ResourceContext* context, |
| 279 const base::Callback<void(crypto::ScopedPK11Slot)>& callback) { |
| 280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 281 #if defined(OS_CHROMEOS) |
| 282 NSSSlotService* service = g_nss_slot_factory.Get().GetService(context); |
| 283 if (service) { |
| 284 service->OnPrivateNSSKeySlotForResourceContextReady(callback); |
| 285 return; |
| 286 } |
| 287 crypto::OnTPMReady(base::Bind(&PassTPMToCallback, callback)); |
| 288 #else |
| 289 PassTPMToCallback(callback); |
| 290 #endif |
| 291 } |
| 292 |
| 293 void GetNSSCertDatabaseForResourceContext( |
| 294 content::ResourceContext* context, |
| 295 const base::Callback<void(net::NSSCertDatabase*)>& callback) { |
| 296 #if defined(OS_CHROMEOS) |
| 297 NSSSlotService* service = g_nss_slot_factory.Get().GetService(context); |
| 298 if (service) { |
| 299 service->OnNSSCertDatabaseForResourceContextReady(callback); |
| 300 return; |
| 301 } |
| 302 #else |
| 303 callback.Run(NSSCertDatabase::GetInstance()); |
| 304 #endif |
| 305 } |
OLD | NEW |