blob: 384cc0da9c3be2c1d5d0d16b41edc79253946b90 [file] [log] [blame]
[email protected]76b90d312010-08-03 03:00:501// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]bc1e07c72008-09-16 14:32:442// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]1b1a264a2010-01-14 22:36:355#include "base/nss_util.h"
[email protected]dcce6cf2010-04-29 17:50:066#include "base/nss_util_internal.h"
[email protected]bc1e07c72008-09-16 14:32:447
[email protected]bc1e07c72008-09-16 14:32:448#include <nss.h>
[email protected]c72f16a2009-03-19 16:02:319#include <plarena.h>
[email protected]6e7e8062009-04-13 17:35:0910#include <prerror.h>
[email protected]c72f16a2009-03-19 16:02:3111#include <prinit.h>
[email protected]1b1a264a2010-01-14 22:36:3512#include <prtime.h>
[email protected]f079def2009-06-11 18:48:2913#include <pk11pub.h>
[email protected]ea224582008-12-07 20:25:4614#include <secmod.h>
[email protected]b43c97c2008-10-22 19:50:5815
[email protected]a8b58f42010-07-14 20:21:3516#if defined(OS_LINUX)
[email protected]1f59edcb2010-11-04 00:32:2517#include <linux/nfs_fs.h>
[email protected]a8b58f42010-07-14 20:21:3518#include <sys/vfs.h>
19#endif
20
[email protected]ea224582008-12-07 20:25:4621#include "base/file_util.h"
[email protected]f615bda32010-11-21 01:04:5622#include "base/lazy_instance.h"
[email protected]c1444fe2008-09-17 09:42:5123#include "base/logging.h"
[email protected]f1633932010-08-17 23:05:2824#include "base/stringprintf.h"
[email protected]34b99632011-01-01 01:01:0625#include "base/threading/thread_restrictions.h"
[email protected]bc1e07c72008-09-16 14:32:4426
[email protected]a8b58f42010-07-14 20:21:3527// USE_NSS means we use NSS for everything crypto-related. If USE_NSS is not
28// defined, such as on Mac and Windows, we use NSS for SSL only -- we don't
29// use NSS for crypto or certificate verification, and we don't use the NSS
30// certificate and key databases.
[email protected]69138472010-06-25 22:44:4831#if defined(USE_NSS)
[email protected]3f1f8412011-01-19 03:01:2332#include "base/crypto/crypto_module_blocking_password_delegate.h"
[email protected]76b90d312010-08-03 03:00:5033#include "base/environment.h"
[email protected]69138472010-06-25 22:44:4834#include "base/lock.h"
35#include "base/scoped_ptr.h"
[email protected]a8b58f42010-07-14 20:21:3536#endif // defined(USE_NSS)
[email protected]abd4aba2010-01-27 19:36:2237
[email protected]f1633932010-08-17 23:05:2838namespace base {
39
[email protected]bc1e07c72008-09-16 14:32:4440namespace {
41
[email protected]a8b58f42010-07-14 20:21:3542#if defined(USE_NSS)
43FilePath GetDefaultConfigDirectory() {
44 FilePath dir = file_util::GetHomeDir();
45 if (dir.empty()) {
46 LOG(ERROR) << "Failed to get home directory.";
47 return dir;
[email protected]86913342009-05-25 02:14:3448 }
[email protected]86913342009-05-25 02:14:3449 dir = dir.AppendASCII(".pki").AppendASCII("nssdb");
50 if (!file_util::CreateDirectory(dir)) {
51 LOG(ERROR) << "Failed to create ~/.pki/nssdb directory.";
[email protected]a8b58f42010-07-14 20:21:3552 dir.clear();
[email protected]86913342009-05-25 02:14:3453 }
[email protected]a8b58f42010-07-14 20:21:3554 return dir;
[email protected]86913342009-05-25 02:14:3455}
56
[email protected]dcce6cf2010-04-29 17:50:0657// On non-chromeos platforms, return the default config directory.
58// On chromeos, return a read-only directory with fake root CA certs for testing
59// (which will not exist on non-testing images). These root CA certs are used
60// by the local Google Accounts server mock we use when testing our login code.
61// If this directory is not present, NSS_Init() will fail. It is up to the
62// caller to failover to NSS_NoDB_Init() at that point.
[email protected]a8b58f42010-07-14 20:21:3563FilePath GetInitialConfigDirectory() {
[email protected]dcce6cf2010-04-29 17:50:0664#if defined(OS_CHROMEOS)
[email protected]a8b58f42010-07-14 20:21:3565 static const FilePath::CharType kReadOnlyCertDB[] =
66 FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb");
67 return FilePath(kReadOnlyCertDB);
[email protected]dcce6cf2010-04-29 17:50:0668#else
69 return GetDefaultConfigDirectory();
70#endif // defined(OS_CHROMEOS)
71}
72
[email protected]88b9db72011-01-13 01:48:4373// This callback for NSS forwards all requests to a caller-specified
[email protected]3f1f8412011-01-19 03:01:2374// CryptoModuleBlockingPasswordDelegate object.
75char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) {
76 base::CryptoModuleBlockingPasswordDelegate* delegate =
77 reinterpret_cast<base::CryptoModuleBlockingPasswordDelegate*>(arg);
[email protected]88b9db72011-01-13 01:48:4378 if (delegate) {
79 bool cancelled = false;
80 std::string password = delegate->RequestPassword(PK11_GetTokenName(slot),
81 retry != PR_FALSE,
82 &cancelled);
83 if (cancelled)
84 return NULL;
85 char* result = PORT_Strdup(password.c_str());
86 password.replace(0, password.size(), password.size(), 0);
87 return result;
88 }
89 DLOG(ERROR) << "PK11 password requested with NULL arg";
90 return NULL;
91}
92
[email protected]a8b58f42010-07-14 20:21:3593// NSS creates a local cache of the sqlite database if it detects that the
94// filesystem the database is on is much slower than the local disk. The
95// detection doesn't work with the latest versions of sqlite, such as 3.6.22
96// (NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=578561). So we set
97// the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's
98// detection when database_dir is on NFS. See http://crbug.com/48585.
99//
100// TODO(wtc): port this function to other USE_NSS platforms. It is defined
101// only for OS_LINUX simply because the statfs structure is OS-specific.
[email protected]ac3d5972011-01-13 20:33:45102//
103// Because this function sets an environment variable it must be run before we
104// go multi-threaded.
[email protected]a8b58f42010-07-14 20:21:35105void UseLocalCacheOfNSSDatabaseIfNFS(const FilePath& database_dir) {
106#if defined(OS_LINUX)
107 struct statfs buf;
108 if (statfs(database_dir.value().c_str(), &buf) == 0) {
109 if (buf.f_type == NFS_SUPER_MAGIC) {
[email protected]f615bda32010-11-21 01:04:56110 scoped_ptr<Environment> env(Environment::Create());
[email protected]a8b58f42010-07-14 20:21:35111 const char* use_cache_env_var = "NSS_SDB_USE_CACHE";
[email protected]9432ade2010-08-04 23:43:20112 if (!env->HasVar(use_cache_env_var))
[email protected]c87bcf002010-08-06 01:03:37113 env->SetVar(use_cache_env_var, "yes");
[email protected]a8b58f42010-07-14 20:21:35114 }
115 }
116#endif // defined(OS_LINUX)
117}
118
[email protected]ea224582008-12-07 20:25:46119// Load nss's built-in root certs.
120SECMODModule *InitDefaultRootCerts() {
121 const char* kModulePath = "libnssckbi.so";
122 char modparams[1024];
123 snprintf(modparams, sizeof(modparams),
[email protected]829296f2010-01-27 02:58:03124 "name=\"Root Certs\" library=\"%s\"", kModulePath);
[email protected]ea224582008-12-07 20:25:46125 SECMODModule *root = SECMOD_LoadUserModule(modparams, NULL, PR_FALSE);
126 if (root)
127 return root;
128
129 // Aw, snap. Can't find/load root cert shared library.
130 // This will make it hard to talk to anybody via https.
131 NOTREACHED();
132 return NULL;
133}
[email protected]a8b58f42010-07-14 20:21:35134#endif // defined(USE_NSS)
[email protected]ea224582008-12-07 20:25:46135
[email protected]730fb132009-09-02 22:50:25136// A singleton to initialize/deinitialize NSPR.
137// Separate from the NSS singleton because we initialize NSPR on the UI thread.
[email protected]f615bda32010-11-21 01:04:56138// Now that we're leaking the singleton, we could merge back with the NSS
139// singleton.
[email protected]730fb132009-09-02 22:50:25140class NSPRInitSingleton {
[email protected]f615bda32010-11-21 01:04:56141 private:
142 friend struct DefaultLazyInstanceTraits<NSPRInitSingleton>;
143
[email protected]730fb132009-09-02 22:50:25144 NSPRInitSingleton() {
145 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
146 }
147
[email protected]f615bda32010-11-21 01:04:56148 // NOTE(willchan): We don't actually execute this code since we leak NSS to
149 // prevent non-joinable threads from using NSS after it's already been shut
150 // down.
[email protected]730fb132009-09-02 22:50:25151 ~NSPRInitSingleton() {
[email protected]829296f2010-01-27 02:58:03152 PL_ArenaFinish();
[email protected]730fb132009-09-02 22:50:25153 PRStatus prstatus = PR_Cleanup();
154 if (prstatus != PR_SUCCESS) {
155 LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?";
156 }
157 }
158};
159
[email protected]f615bda32010-11-21 01:04:56160LazyInstance<NSPRInitSingleton, LeakyLazyInstanceTraits<NSPRInitSingleton> >
161 g_nspr_singleton(LINKER_INITIALIZED);
162
[email protected]bc1e07c72008-09-16 14:32:44163class NSSInitSingleton {
164 public:
[email protected]f615bda32010-11-21 01:04:56165#if defined(OS_CHROMEOS)
166 void OpenPersistentNSSDB() {
167 if (!chromeos_user_logged_in_) {
168 chromeos_user_logged_in_ = true;
169 real_db_slot_ = OpenUserDB(GetDefaultConfigDirectory(),
170 "Real NSS database");
171 }
172 }
173#endif // defined(OS_CHROMEOS)
174
175 bool OpenTestNSSDB(const FilePath& path, const char* description) {
176 test_db_slot_ = OpenUserDB(path, description);
177 return !!test_db_slot_;
178 }
179
180 void CloseTestNSSDB() {
181 if (test_db_slot_) {
182 SECStatus status = SECMOD_CloseUserDB(test_db_slot_);
183 if (status != SECSuccess)
184 LOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
185 PK11_FreeSlot(test_db_slot_);
186 test_db_slot_ = NULL;
187 }
188 }
189
190 PK11SlotInfo* GetDefaultKeySlot() {
191 if (test_db_slot_)
192 return PK11_ReferenceSlot(test_db_slot_);
193 if (real_db_slot_)
194 return PK11_ReferenceSlot(real_db_slot_);
195 return PK11_GetInternalKeySlot();
196 }
197
198#if defined(USE_NSS)
199 Lock* write_lock() {
200 return &write_lock_;
201 }
202#endif // defined(USE_NSS)
203
204 private:
205 friend struct DefaultLazyInstanceTraits<NSSInitSingleton>;
206
[email protected]dcce6cf2010-04-29 17:50:06207 NSSInitSingleton()
208 : real_db_slot_(NULL),
[email protected]bb639032010-08-12 19:49:40209 test_db_slot_(NULL),
[email protected]dcce6cf2010-04-29 17:50:06210 root_(NULL),
211 chromeos_user_logged_in_(false) {
[email protected]f615bda32010-11-21 01:04:56212 EnsureNSPRInit();
[email protected]730fb132009-09-02 22:50:25213
[email protected]dc36c9c2010-01-20 20:45:00214 // We *must* have NSS >= 3.12.3. See bug 26448.
215 COMPILE_ASSERT(
216 (NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH >= 3) ||
217 (NSS_VMAJOR == 3 && NSS_VMINOR > 12) ||
218 (NSS_VMAJOR > 3),
219 nss_version_check_failed);
220 // Also check the run-time NSS version.
221 // NSS_VersionCheck is a >= check, not strict equality.
[email protected]1b8082d2010-02-19 12:21:48222 if (!NSS_VersionCheck("3.12.3")) {
223 // It turns out many people have misconfigured NSS setups, where
224 // their run-time NSPR doesn't match the one their NSS was compiled
225 // against. So rather than aborting, complain loudly.
226 LOG(ERROR) << "NSS_VersionCheck(\"3.12.3\") failed. "
227 "We depend on NSS >= 3.12.3, and this error is not fatal "
228 "only because many people have busted NSS setups (for "
229 "example, using the wrong version of NSPR). "
230 "Please upgrade to the latest NSS and NSPR, and if you "
231 "still get this error, contact your distribution "
232 "maintainer.";
233 }
[email protected]dc36c9c2010-01-20 20:45:00234
[email protected]897f5202009-09-08 17:40:27235 SECStatus status = SECFailure;
[email protected]a8b58f42010-07-14 20:21:35236#if !defined(USE_NSS)
[email protected]abd4aba2010-01-27 19:36:22237 // Use the system certificate store, so initialize NSS without database.
238 status = NSS_NoDB_Init(NULL);
239 if (status != SECSuccess) {
240 LOG(ERROR) << "Error initializing NSS without a persistent "
241 "database: NSS error code " << PR_GetError();
242 }
243#else
[email protected]a8b58f42010-07-14 20:21:35244 FilePath database_dir = GetInitialConfigDirectory();
[email protected]86913342009-05-25 02:14:34245 if (!database_dir.empty()) {
[email protected]ac3d5972011-01-13 20:33:45246 // This duplicates the work which should have been done in
247 // EarlySetupForNSSInit. However, this function is idempotent so there's
248 // no harm done.
[email protected]a8b58f42010-07-14 20:21:35249 UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
250
[email protected]dcce6cf2010-04-29 17:50:06251 // Initialize with a persistent database (likely, ~/.pki/nssdb).
[email protected]86913342009-05-25 02:14:34252 // Use "sql:" which can be shared by multiple processes safely.
[email protected]105d1682009-10-23 22:03:18253 std::string nss_config_dir =
[email protected]a8b58f42010-07-14 20:21:35254 StringPrintf("sql:%s", database_dir.value().c_str());
[email protected]dcce6cf2010-04-29 17:50:06255#if defined(OS_CHROMEOS)
256 status = NSS_Init(nss_config_dir.c_str());
257#else
[email protected]105d1682009-10-23 22:03:18258 status = NSS_InitReadWrite(nss_config_dir.c_str());
[email protected]dcce6cf2010-04-29 17:50:06259#endif
[email protected]897f5202009-09-08 17:40:27260 if (status != SECSuccess) {
261 LOG(ERROR) << "Error initializing NSS with a persistent "
[email protected]105d1682009-10-23 22:03:18262 "database (" << nss_config_dir
263 << "): NSS error code " << PR_GetError();
[email protected]897f5202009-09-08 17:40:27264 }
[email protected]86913342009-05-25 02:14:34265 }
[email protected]6e7e8062009-04-13 17:35:09266 if (status != SECSuccess) {
[email protected]897f5202009-09-08 17:40:27267 LOG(WARNING) << "Initialize NSS without a persistent database "
268 "(~/.pki/nssdb).";
269 status = NSS_NoDB_Init(NULL);
270 if (status != SECSuccess) {
271 LOG(ERROR) << "Error initializing NSS without a persistent "
272 "database: NSS error code " << PR_GetError();
[email protected]dbc13b52010-04-16 00:59:56273 return;
[email protected]897f5202009-09-08 17:40:27274 }
[email protected]6e7e8062009-04-13 17:35:09275 }
[email protected]ea224582008-12-07 20:25:46276
[email protected]3f1f8412011-01-19 03:01:23277 PK11_SetPasswordFunc(PKCS11PasswordFunc);
[email protected]88b9db72011-01-13 01:48:43278
[email protected]f079def2009-06-11 18:48:29279 // If we haven't initialized the password for the NSS databases,
280 // initialize an empty-string password so that we don't need to
281 // log in.
282 PK11SlotInfo* slot = PK11_GetInternalKeySlot();
283 if (slot) {
[email protected]69138472010-06-25 22:44:48284 // PK11_InitPin may write to the keyDB, but no other thread can use NSS
285 // yet, so we don't need to lock.
[email protected]f079def2009-06-11 18:48:29286 if (PK11_NeedUserInit(slot))
287 PK11_InitPin(slot, NULL, NULL);
288 PK11_FreeSlot(slot);
289 }
290
[email protected]ea224582008-12-07 20:25:46291 root_ = InitDefaultRootCerts();
[email protected]a8b58f42010-07-14 20:21:35292#endif // !defined(USE_NSS)
[email protected]bc1e07c72008-09-16 14:32:44293 }
294
[email protected]f615bda32010-11-21 01:04:56295 // NOTE(willchan): We don't actually execute this code since we leak NSS to
296 // prevent non-joinable threads from using NSS after it's already been shut
297 // down.
[email protected]bc1e07c72008-09-16 14:32:44298 ~NSSInitSingleton() {
[email protected]dcce6cf2010-04-29 17:50:06299 if (real_db_slot_) {
300 SECMOD_CloseUserDB(real_db_slot_);
301 PK11_FreeSlot(real_db_slot_);
302 real_db_slot_ = NULL;
303 }
[email protected]bb639032010-08-12 19:49:40304 CloseTestNSSDB();
[email protected]ea224582008-12-07 20:25:46305 if (root_) {
306 SECMOD_UnloadUserModule(root_);
307 SECMOD_DestroyModule(root_);
308 root_ = NULL;
309 }
310
[email protected]c1444fe2008-09-17 09:42:51311 SECStatus status = NSS_Shutdown();
[email protected]f8adef52009-08-04 17:52:06312 if (status != SECSuccess) {
[email protected]b026e35d2010-10-19 02:31:03313 // We VLOG(1) because this failure is relatively harmless (leaking, but
314 // we're shutting down anyway).
315 VLOG(1) << "NSS_Shutdown failed; see "
316 "http://code.google.com/p/chromium/issues/detail?id=4609";
[email protected]f8adef52009-08-04 17:52:06317 }
[email protected]bc1e07c72008-09-16 14:32:44318 }
[email protected]c72f16a2009-03-19 16:02:31319
[email protected]bb639032010-08-12 19:49:40320 static PK11SlotInfo* OpenUserDB(const FilePath& path,
321 const char* description) {
322 const std::string modspec =
323 StringPrintf("configDir='sql:%s' tokenDescription='%s'",
324 path.value().c_str(), description);
325 PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str());
326 if (db_slot) {
327 if (PK11_NeedUserInit(db_slot))
328 PK11_InitPin(db_slot, NULL, NULL);
329 }
330 else {
331 LOG(ERROR) << "Error opening persistent database (" << modspec
332 << "): NSS error code " << PR_GetError();
333 }
334 return db_slot;
335 }
336
[email protected]dcce6cf2010-04-29 17:50:06337 PK11SlotInfo* real_db_slot_; // Overrides internal key slot if non-NULL.
[email protected]bb639032010-08-12 19:49:40338 PK11SlotInfo* test_db_slot_; // Overrides internal key slot and real_db_slot_
[email protected]ea224582008-12-07 20:25:46339 SECMODModule *root_;
[email protected]dcce6cf2010-04-29 17:50:06340 bool chromeos_user_logged_in_;
[email protected]69138472010-06-25 22:44:48341#if defined(USE_NSS)
[email protected]f615bda32010-11-21 01:04:56342 // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011
343 // is fixed, we will no longer need the lock.
344 Lock write_lock_;
[email protected]a8b58f42010-07-14 20:21:35345#endif // defined(USE_NSS)
[email protected]bc1e07c72008-09-16 14:32:44346};
347
[email protected]f615bda32010-11-21 01:04:56348LazyInstance<NSSInitSingleton, LeakyLazyInstanceTraits<NSSInitSingleton> >
349 g_nss_singleton(LINKER_INITIALIZED);
350
[email protected]bc1e07c72008-09-16 14:32:44351} // namespace
352
[email protected]ac3d5972011-01-13 20:33:45353#if defined(USE_NSS)
354void EarlySetupForNSSInit() {
355 FilePath database_dir = GetInitialConfigDirectory();
356 if (!database_dir.empty())
357 UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
358}
359#endif
360
[email protected]730fb132009-09-02 22:50:25361void EnsureNSPRInit() {
[email protected]f615bda32010-11-21 01:04:56362 g_nspr_singleton.Get();
[email protected]730fb132009-09-02 22:50:25363}
364
[email protected]bc1e07c72008-09-16 14:32:44365void EnsureNSSInit() {
[email protected]a8e4b5a82010-10-27 00:05:47366 // Initializing SSL causes us to do blocking IO.
367 // Temporarily allow it until we fix
368 // http://code.google.com/p/chromium/issues/detail?id=59847
[email protected]f615bda32010-11-21 01:04:56369 ThreadRestrictions::ScopedAllowIO allow_io;
370 g_nss_singleton.Get();
[email protected]bc1e07c72008-09-16 14:32:44371}
372
[email protected]f61c3972010-12-23 09:54:15373bool CheckNSSVersion(const char* version) {
374 return !!NSS_VersionCheck(version);
375}
376
[email protected]69138472010-06-25 22:44:48377#if defined(USE_NSS)
[email protected]bb639032010-08-12 19:49:40378bool OpenTestNSSDB(const FilePath& path, const char* description) {
[email protected]f615bda32010-11-21 01:04:56379 return g_nss_singleton.Get().OpenTestNSSDB(path, description);
[email protected]bb639032010-08-12 19:49:40380}
381
382void CloseTestNSSDB() {
[email protected]f615bda32010-11-21 01:04:56383 g_nss_singleton.Get().CloseTestNSSDB();
[email protected]bb639032010-08-12 19:49:40384}
385
[email protected]69138472010-06-25 22:44:48386Lock* GetNSSWriteLock() {
[email protected]f615bda32010-11-21 01:04:56387 return g_nss_singleton.Get().write_lock();
[email protected]69138472010-06-25 22:44:48388}
389
390AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) {
391 // May be NULL if the lock is not needed in our version of NSS.
392 if (lock_)
393 lock_->Acquire();
394}
395
396AutoNSSWriteLock::~AutoNSSWriteLock() {
397 if (lock_) {
398 lock_->AssertAcquired();
399 lock_->Release();
400 }
401}
402#endif // defined(USE_NSS)
403
[email protected]dcce6cf2010-04-29 17:50:06404#if defined(OS_CHROMEOS)
405void OpenPersistentNSSDB() {
[email protected]f615bda32010-11-21 01:04:56406 g_nss_singleton.Get().OpenPersistentNSSDB();
[email protected]dcce6cf2010-04-29 17:50:06407}
408#endif
409
[email protected]1b1a264a2010-01-14 22:36:35410// TODO(port): Implement this more simply. We can convert by subtracting an
411// offset (the difference between NSPR's and base::Time's epochs).
412Time PRTimeToBaseTime(PRTime prtime) {
413 PRExplodedTime prxtime;
414 PR_ExplodeTime(prtime, PR_GMTParameters, &prxtime);
415
[email protected]f615bda32010-11-21 01:04:56416 Time::Exploded exploded;
[email protected]1b1a264a2010-01-14 22:36:35417 exploded.year = prxtime.tm_year;
418 exploded.month = prxtime.tm_month + 1;
419 exploded.day_of_week = prxtime.tm_wday;
420 exploded.day_of_month = prxtime.tm_mday;
421 exploded.hour = prxtime.tm_hour;
422 exploded.minute = prxtime.tm_min;
423 exploded.second = prxtime.tm_sec;
424 exploded.millisecond = prxtime.tm_usec / 1000;
425
426 return Time::FromUTCExploded(exploded);
427}
428
[email protected]dcce6cf2010-04-29 17:50:06429PK11SlotInfo* GetDefaultNSSKeySlot() {
[email protected]f615bda32010-11-21 01:04:56430 return g_nss_singleton.Get().GetDefaultKeySlot();
[email protected]dcce6cf2010-04-29 17:50:06431}
432
[email protected]bc1e07c72008-09-16 14:32:44433} // namespace base