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 "net/cert/nss_cert_database_chromeos.h" |
| 6 |
| 7 #include <map> |
| 8 |
| 9 #include <cert.h> |
| 10 #include <pk11pub.h> |
| 11 |
| 12 #include "base/bind.h" |
| 13 #include "base/lazy_instance.h" |
| 14 #include "base/stl_util.h" |
| 15 #include "crypto/nss_util.h" |
| 16 #include "crypto/nss_util_internal.h" |
| 17 #include "net/base/crypto_module.h" |
| 18 #include "net/cert/x509_certificate.h" |
| 19 #include "net/cert/x509_util_nss.h" |
| 20 |
| 21 namespace net { |
| 22 |
| 23 namespace { |
| 24 |
| 25 scoped_refptr<const X509Certificate> FindCertInSlot(const X509Certificate* cert, |
| 26 PK11SlotInfo* slot) { |
| 27 CERTCertificate* slot_cert_handle = PK11_FindCertFromDERCert( |
| 28 slot, cert->os_cert_handle(), NULL); |
| 29 if (slot_cert_handle) { |
| 30 scoped_refptr<X509Certificate> slot_cert( |
| 31 X509Certificate::CreateFromHandle(slot_cert_handle, |
| 32 X509Certificate::OSCertHandles())); |
| 33 CERT_DestroyCertificate(slot_cert_handle); |
| 34 return slot_cert; |
| 35 } |
| 36 return NULL; |
| 37 } |
| 38 |
| 39 } // namespace |
| 40 |
| 41 class NSSCertDatabaseChromeOS::Manager { |
| 42 public: |
| 43 ~Manager() { |
| 44 STLDeleteValues(&user_db_map_); |
| 45 } |
| 46 |
| 47 NSSCertDatabase* GetPrimaryDB() { |
| 48 LOG(WARNING) |
| 49 << "Using primary NSSCertDatabase. Consider using GetForUser instead."; |
| 50 return primary_db_; |
| 51 } |
| 52 |
| 53 void GetForUser(const std::string& username_hash, |
| 54 const base::Callback<void(NSSCertDatabase*)>& callback) { |
| 55 UserDBMap::iterator it = user_db_map_.find(username_hash); |
| 56 if (it != user_db_map_.end()) { |
| 57 // DB already exists for this user. |
| 58 it->second->OnReady(callback); |
| 59 return; |
| 60 } |
| 61 |
| 62 crypto::ScopedPK11Slot public_slot( |
| 63 crypto::GetPublicSlotForChromeOSUser(username_hash)); |
| 64 if (!public_slot) { |
| 65 // Invalid user, or someone called us before InitializeNSSForChromeOSUser |
| 66 // got called for the user. |
| 67 callback.Run(NULL); |
| 68 return; |
| 69 } |
| 70 |
| 71 NSSCertDatabaseChromeOS* db = NULL; |
| 72 if (primary_db_owner_) { |
| 73 crypto::ScopedPK11Slot primary_public_slot(primary_db_->GetPublicSlot()); |
| 74 if (public_slot.get() == primary_public_slot.get()) { |
| 75 VLOG(1) << "Got username for primary: " << username_hash; |
| 76 // We now know what username corrosponds to the primary user. Put the |
| 77 // primary_db_ into the user_db_map_, which will now own it. |
| 78 db = primary_db_owner_.release(); |
| 79 } |
| 80 } |
| 81 |
| 82 if (!db) { |
| 83 // Create DB for new user. |
| 84 VLOG(1) << "Create DB for: " << username_hash; |
| 85 db = new NSSCertDatabaseChromeOS(public_slot.Pass()); |
| 86 crypto::OnPrivateSlotReadyForChromeOSUser( |
| 87 username_hash, |
| 88 base::Bind(&NSSCertDatabaseChromeOS::SetPrivateSlot, |
| 89 base::Unretained(db))); |
| 90 } |
| 91 |
| 92 user_db_map_[username_hash] = db; |
| 93 db->OnReady(callback); |
| 94 } |
| 95 private: |
| 96 friend struct base::DefaultLazyInstanceTraits<Manager>; |
| 97 |
| 98 Manager() |
| 99 : primary_db_(new NSSCertDatabaseChromeOS( |
| 100 crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot()))) { |
| 101 primary_db_owner_.reset(primary_db_); |
| 102 crypto::OnTPMReady(base::Bind(&Manager::SetPrimaryPrivateSlot, |
| 103 base::Unretained(primary_db_))); |
| 104 } |
| 105 |
| 106 static void SetPrimaryPrivateSlot(NSSCertDatabaseChromeOS* db) { |
| 107 db->SetPrivateSlot(crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot())); |
| 108 } |
| 109 |
| 110 NSSCertDatabaseChromeOS* primary_db_; |
| 111 scoped_ptr<NSSCertDatabaseChromeOS> primary_db_owner_; |
| 112 typedef std::map<std::string, NSSCertDatabaseChromeOS*> UserDBMap; |
| 113 UserDBMap user_db_map_; |
| 114 }; |
| 115 |
| 116 namespace { |
| 117 base::LazyInstance<NSSCertDatabaseChromeOS::Manager>::Leaky |
| 118 g_nss_cert_database_chromeos_manager = LAZY_INSTANCE_INITIALIZER; |
| 119 } // namespace |
| 120 |
| 121 // On ChromeOS NSSCertDatabase::GetInstance is defined to return the primary |
| 122 // user's NSSCertDatabaseChromeOS object. |
| 123 // static |
| 124 NSSCertDatabase* NSSCertDatabase::GetInstance() { |
| 125 return g_nss_cert_database_chromeos_manager.Get().GetPrimaryDB(); |
| 126 } |
| 127 |
| 128 // static |
| 129 void NSSCertDatabaseChromeOS::GetForUser( |
| 130 const std::string& username_hash, |
| 131 const base::Callback<void(NSSCertDatabase*)>& callback) { |
| 132 g_nss_cert_database_chromeos_manager.Get().GetForUser(username_hash, |
| 133 callback); |
| 134 } |
| 135 |
| 136 NSSCertDatabaseChromeOS::NSSCertDatabaseChromeOS( |
| 137 crypto::ScopedPK11Slot public_slot) |
| 138 : ready_(false), public_slot_(public_slot.Pass()) { |
| 139 VLOG(1) << __func__ << " pub:" << PK11_GetModuleID(public_slot_.get()) << ":" |
| 140 << PK11_GetSlotID(public_slot_.get()); |
| 141 } |
| 142 |
| 143 NSSCertDatabaseChromeOS::~NSSCertDatabaseChromeOS() {} |
| 144 |
| 145 void NSSCertDatabaseChromeOS::ListCerts(CertificateList* certs) { |
| 146 if (!ready_) { |
| 147 LOG(WARNING) << __func__ << " called before initialization complete"; |
| 148 certs->clear(); |
| 149 return; |
| 150 } |
| 151 NSSCertDatabase::ListCerts(certs); |
| 152 |
| 153 size_t pre_size = certs->size(); |
| 154 certs->erase( |
| 155 std::remove_if(certs->begin(), certs->end(), |
| 156 NSSProfileFilterChromeOS::Predicate(profile_filter_)), |
| 157 certs->end()); |
| 158 VLOG(1) << "filtered " << pre_size - certs->size() << " of " << pre_size |
| 159 << " certs"; |
| 160 } |
| 161 |
| 162 crypto::ScopedPK11Slot NSSCertDatabaseChromeOS::GetPublicSlot() const { |
| 163 return crypto::ScopedPK11Slot( |
| 164 public_slot_ ? PK11_ReferenceSlot(public_slot_.get()) : NULL); |
| 165 } |
| 166 |
| 167 crypto::ScopedPK11Slot NSSCertDatabaseChromeOS::GetPrivateSlot() const { |
| 168 return crypto::ScopedPK11Slot( |
| 169 private_slot_ ? PK11_ReferenceSlot(private_slot_.get()) : NULL); |
| 170 } |
| 171 |
| 172 void NSSCertDatabaseChromeOS::ListModules(CryptoModuleList* modules, |
| 173 bool need_rw) const { |
| 174 if (!ready_) { |
| 175 LOG(WARNING) << __func__ << " called before initialization complete"; |
| 176 modules->clear(); |
| 177 return; |
| 178 } |
| 179 NSSCertDatabase::ListModules(modules, need_rw); |
| 180 |
| 181 size_t pre_size = modules->size(); |
| 182 modules->erase( |
| 183 std::remove_if(modules->begin(), modules->end(), |
| 184 NSSProfileFilterChromeOS::Predicate(profile_filter_)), |
| 185 modules->end()); |
| 186 VLOG(1) << "filtered " << pre_size - modules->size() << " of " << pre_size |
| 187 << " modules"; |
| 188 } |
| 189 |
| 190 NSSCertDatabase::TrustBits NSSCertDatabaseChromeOS::GetCertTrust( |
| 191 const X509Certificate* cert, |
| 192 CertType type) const { |
| 193 return NSSCertDatabase::GetCertTrust(ResolveCert(cert, false), type); |
| 194 } |
| 195 |
| 196 bool NSSCertDatabaseChromeOS::IsUntrusted(const X509Certificate* cert) const { |
| 197 return NSSCertDatabase::IsUntrusted(ResolveCert(cert, false)); |
| 198 } |
| 199 |
| 200 #if 0 |
| 201 bool NSSCertDatabaseChromeOS::SetCertTrust(const X509Certificate* cert, |
| 202 CertType type, |
| 203 TrustBits trust_bits) { |
| 204 // XXX handle case of cert not existing in any of this profile's rw slots |
| 205 if (!FindCertInSlot(cert, public_slot_.get())) { |
| 206 // Copy cert to our slot so we can set trust . . . |
| 207 // XXX this doesn't actually help. |
| 208 SECStatus srv = PK11_ImportCert( |
| 209 public_slot_.get(), |
| 210 cert->os_cert_handle(), |
| 211 CK_INVALID_HANDLE, |
| 212 x509_util::GetUniqueNicknameForSlot(cert->GetDefaultNickname(type), |
| 213 &cert->os_cert_handle()->derSubject, |
| 214 public_slot_.get()).c_str(), |
| 215 PR_FALSE /* includeTrust (unused) */); |
| 216 if (srv != SECSuccess) { |
| 217 LOG(ERROR) << "copying cert to our slot failed with error " |
| 218 << PORT_GetError(); |
| 219 return false; |
| 220 } |
| 221 VLOG(1) << "copied cert to our slot"; |
| 222 } |
| 223 // |
| 224 // XXX this doesn't work: if cert is in multiple rw slots nss will just set |
| 225 // the trust on the "first" one - we could return false instead of setting |
| 226 // trust in wrong slot? (but how do we know which slot nss would have felt |
| 227 // like setting the trust in? is it always the lowest numbered slotid?) |
| 228 return NSSCertDatabase::SetCertTrust( |
| 229 ResolveCert(cert, true), type, trust_bits); |
| 230 } |
| 231 #endif |
| 232 |
| 233 void NSSCertDatabaseChromeOS::SetPrivateSlot( |
| 234 crypto::ScopedPK11Slot private_slot) { |
| 235 DCHECK(!ready_); |
| 236 if (!private_slot) |
| 237 LOG(WARNING) << "initializing with NULL private_slot."; |
| 238 private_slot_ = private_slot.Pass(); |
| 239 profile_filter_.Init(GetPublicSlot(), GetPrivateSlot()); |
| 240 ready_ = true; |
| 241 |
| 242 for (ReadyCallbackList::iterator i = ready_callback_list_.begin(); |
| 243 i != ready_callback_list_.end(); |
| 244 ++i) { |
| 245 (*i).Run(this); |
| 246 } |
| 247 ready_callback_list_.clear(); |
| 248 } |
| 249 |
| 250 void NSSCertDatabaseChromeOS::OnReady( |
| 251 const base::Callback<void(NSSCertDatabase*)>& callback) { |
| 252 if (ready_) |
| 253 callback.Run(this); |
| 254 else |
| 255 ready_callback_list_.push_back(callback); |
| 256 } |
| 257 |
| 258 scoped_refptr<const X509Certificate> NSSCertDatabaseChromeOS::ResolveCert( |
| 259 const X509Certificate* cert, bool need_rw) const { |
| 260 // If we have a copy in our slot, use that. |
| 261 scoped_refptr<const X509Certificate> pub_slot_cert( |
| 262 FindCertInSlot(cert, public_slot_.get())); |
| 263 if (pub_slot_cert) { |
| 264 VLOG(1) << "found pub slot cert"; |
| 265 return pub_slot_cert; |
| 266 } |
| 267 |
| 268 CryptoModuleList module_list; |
| 269 ListModules(&module_list, need_rw); |
| 270 for (CryptoModuleList::iterator i = module_list.begin(); |
| 271 i != module_list.end(); |
| 272 ++i) { |
| 273 if ((*i)->os_module_handle() == public_slot_.get()) |
| 274 continue; // We already checked this slot above. |
| 275 scoped_refptr<const X509Certificate> slot_cert( |
| 276 FindCertInSlot(cert, (*i)->os_module_handle())); |
| 277 if (slot_cert) { |
| 278 VLOG(1) << "found cert in other slot: " |
| 279 << PK11_GetModuleID((*i)->os_module_handle()) << ":" |
| 280 << PK11_GetSlotID((*i)->os_module_handle()); |
| 281 return slot_cert; |
| 282 } |
| 283 } |
| 284 |
| 285 // Otherwise, use whatever NSS decides as the default. |
| 286 // XXX should we return NULL here? NOTREACHED? |
| 287 LOG(WARNING) << "using default slot cert"; |
| 288 return cert; |
| 289 } |
| 290 |
| 291 } // namespace net |
OLD | NEW |