| OLD | NEW |
| 1 // Copyright (c) 2008-2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2008-2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/nss_util.h" | 5 #include "base/nss_util.h" |
| 6 #include "base/nss_util_internal.h" | 6 #include "base/nss_util_internal.h" |
| 7 | 7 |
| 8 #include <nss.h> | 8 #include <nss.h> |
| 9 #include <plarena.h> | 9 #include <plarena.h> |
| 10 #include <prerror.h> | 10 #include <prerror.h> |
| 11 #include <prinit.h> | 11 #include <prinit.h> |
| 12 #include <prtime.h> | 12 #include <prtime.h> |
| 13 #include <pk11pub.h> | 13 #include <pk11pub.h> |
| 14 #include <secmod.h> | 14 #include <secmod.h> |
| 15 | 15 |
| 16 #if defined(OS_LINUX) |
| 17 #include <linux/magic.h> |
| 18 #include <sys/vfs.h> |
| 19 #endif |
| 20 |
| 16 #include "base/file_util.h" | 21 #include "base/file_util.h" |
| 17 #include "base/logging.h" | 22 #include "base/logging.h" |
| 18 #include "base/singleton.h" | 23 #include "base/singleton.h" |
| 19 #include "base/string_util.h" | 24 #include "base/string_util.h" |
| 20 | 25 |
| 26 // USE_NSS means we use NSS for everything crypto-related. If USE_NSS is not |
| 27 // defined, such as on Mac and Windows, we use NSS for SSL only -- we don't |
| 28 // use NSS for crypto or certificate verification, and we don't use the NSS |
| 29 // certificate and key databases. |
| 21 #if defined(USE_NSS) | 30 #if defined(USE_NSS) |
| 31 #include "base/env_var.h" |
| 22 #include "base/lock.h" | 32 #include "base/lock.h" |
| 23 #include "base/scoped_ptr.h" | 33 #include "base/scoped_ptr.h" |
| 24 #endif // defined(USE_NSS) | 34 #endif // defined(USE_NSS) |
| 25 | |
| 26 // On some platforms, we use NSS for SSL only -- we don't use NSS for crypto | |
| 27 // or certificate verification, and we don't use the NSS certificate and key | |
| 28 // databases. | |
| 29 #if defined(OS_MACOSX) || defined(OS_WIN) | |
| 30 #define USE_NSS_FOR_SSL_ONLY 1 | |
| 31 #endif | |
| 32 | 35 |
| 33 namespace { | 36 namespace { |
| 34 | 37 |
| 35 #if !defined(USE_NSS_FOR_SSL_ONLY) | 38 #if defined(USE_NSS) |
| 36 std::string GetDefaultConfigDirectory() { | 39 FilePath GetDefaultConfigDirectory() { |
| 37 FilePath home = file_util::GetHomeDir(); | 40 FilePath dir = file_util::GetHomeDir(); |
| 38 if (home.empty()) { | 41 if (dir.empty()) { |
| 39 LOG(ERROR) << "$HOME is not set."; | 42 LOG(ERROR) << "Failed to get home directory."; |
| 40 return std::string(); | 43 return dir; |
| 41 } | 44 } |
| 42 FilePath dir(home); | |
| 43 dir = dir.AppendASCII(".pki").AppendASCII("nssdb"); | 45 dir = dir.AppendASCII(".pki").AppendASCII("nssdb"); |
| 44 if (!file_util::CreateDirectory(dir)) { | 46 if (!file_util::CreateDirectory(dir)) { |
| 45 LOG(ERROR) << "Failed to create ~/.pki/nssdb directory."; | 47 LOG(ERROR) << "Failed to create ~/.pki/nssdb directory."; |
| 46 return std::string(); | 48 dir.clear(); |
| 47 } | 49 } |
| 48 return dir.value(); | 50 return dir; |
| 49 } | 51 } |
| 50 | 52 |
| 51 // On non-chromeos platforms, return the default config directory. | 53 // On non-chromeos platforms, return the default config directory. |
| 52 // On chromeos, return a read-only directory with fake root CA certs for testing | 54 // On chromeos, return a read-only directory with fake root CA certs for testing |
| 53 // (which will not exist on non-testing images). These root CA certs are used | 55 // (which will not exist on non-testing images). These root CA certs are used |
| 54 // by the local Google Accounts server mock we use when testing our login code. | 56 // by the local Google Accounts server mock we use when testing our login code. |
| 55 // If this directory is not present, NSS_Init() will fail. It is up to the | 57 // If this directory is not present, NSS_Init() will fail. It is up to the |
| 56 // caller to failover to NSS_NoDB_Init() at that point. | 58 // caller to failover to NSS_NoDB_Init() at that point. |
| 57 std::string GetInitialConfigDirectory() { | 59 FilePath GetInitialConfigDirectory() { |
| 58 #if defined(OS_CHROMEOS) | 60 #if defined(OS_CHROMEOS) |
| 59 static const char kReadOnlyCertDB[] = "/etc/fake_root_ca/nssdb"; | 61 static const FilePath::CharType kReadOnlyCertDB[] = |
| 60 return std::string(kReadOnlyCertDB); | 62 FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb"); |
| 63 return FilePath(kReadOnlyCertDB); |
| 61 #else | 64 #else |
| 62 return GetDefaultConfigDirectory(); | 65 return GetDefaultConfigDirectory(); |
| 63 #endif // defined(OS_CHROMEOS) | 66 #endif // defined(OS_CHROMEOS) |
| 64 } | 67 } |
| 65 | 68 |
| 69 // NSS creates a local cache of the sqlite database if it detects that the |
| 70 // filesystem the database is on is much slower than the local disk. The |
| 71 // detection doesn't work with the latest versions of sqlite, such as 3.6.22 |
| 72 // (NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=578561). So we set |
| 73 // the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's |
| 74 // detection when database_dir is on NFS. See http://crbug.com/48585. |
| 75 // |
| 76 // TODO(wtc): port this function to other USE_NSS platforms. It is defined |
| 77 // only for OS_LINUX simply because the statfs structure is OS-specific. |
| 78 void UseLocalCacheOfNSSDatabaseIfNFS(const FilePath& database_dir) { |
| 79 #if defined(OS_LINUX) |
| 80 struct statfs buf; |
| 81 if (statfs(database_dir.value().c_str(), &buf) == 0) { |
| 82 if (buf.f_type == NFS_SUPER_MAGIC) { |
| 83 scoped_ptr<base::EnvVarGetter> env(base::EnvVarGetter::Create()); |
| 84 const char* use_cache_env_var = "NSS_SDB_USE_CACHE"; |
| 85 if (!env->HasEnv(use_cache_env_var)) |
| 86 env->SetEnv(use_cache_env_var, "yes"); |
| 87 } |
| 88 } |
| 89 #endif // defined(OS_LINUX) |
| 90 } |
| 91 |
| 66 // Load nss's built-in root certs. | 92 // Load nss's built-in root certs. |
| 67 SECMODModule *InitDefaultRootCerts() { | 93 SECMODModule *InitDefaultRootCerts() { |
| 68 const char* kModulePath = "libnssckbi.so"; | 94 const char* kModulePath = "libnssckbi.so"; |
| 69 char modparams[1024]; | 95 char modparams[1024]; |
| 70 snprintf(modparams, sizeof(modparams), | 96 snprintf(modparams, sizeof(modparams), |
| 71 "name=\"Root Certs\" library=\"%s\"", kModulePath); | 97 "name=\"Root Certs\" library=\"%s\"", kModulePath); |
| 72 SECMODModule *root = SECMOD_LoadUserModule(modparams, NULL, PR_FALSE); | 98 SECMODModule *root = SECMOD_LoadUserModule(modparams, NULL, PR_FALSE); |
| 73 if (root) | 99 if (root) |
| 74 return root; | 100 return root; |
| 75 | 101 |
| 76 // Aw, snap. Can't find/load root cert shared library. | 102 // Aw, snap. Can't find/load root cert shared library. |
| 77 // This will make it hard to talk to anybody via https. | 103 // This will make it hard to talk to anybody via https. |
| 78 NOTREACHED(); | 104 NOTREACHED(); |
| 79 return NULL; | 105 return NULL; |
| 80 } | 106 } |
| 81 #endif // !defined(USE_NSS_FOR_SSL_ONLY) | 107 #endif // defined(USE_NSS) |
| 82 | 108 |
| 83 // A singleton to initialize/deinitialize NSPR. | 109 // A singleton to initialize/deinitialize NSPR. |
| 84 // Separate from the NSS singleton because we initialize NSPR on the UI thread. | 110 // Separate from the NSS singleton because we initialize NSPR on the UI thread. |
| 85 class NSPRInitSingleton { | 111 class NSPRInitSingleton { |
| 86 public: | 112 public: |
| 87 NSPRInitSingleton() { | 113 NSPRInitSingleton() { |
| 88 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); | 114 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); |
| 89 } | 115 } |
| 90 | 116 |
| 91 ~NSPRInitSingleton() { | 117 ~NSPRInitSingleton() { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 120 LOG(ERROR) << "NSS_VersionCheck(\"3.12.3\") failed. " | 146 LOG(ERROR) << "NSS_VersionCheck(\"3.12.3\") failed. " |
| 121 "We depend on NSS >= 3.12.3, and this error is not fatal " | 147 "We depend on NSS >= 3.12.3, and this error is not fatal " |
| 122 "only because many people have busted NSS setups (for " | 148 "only because many people have busted NSS setups (for " |
| 123 "example, using the wrong version of NSPR). " | 149 "example, using the wrong version of NSPR). " |
| 124 "Please upgrade to the latest NSS and NSPR, and if you " | 150 "Please upgrade to the latest NSS and NSPR, and if you " |
| 125 "still get this error, contact your distribution " | 151 "still get this error, contact your distribution " |
| 126 "maintainer."; | 152 "maintainer."; |
| 127 } | 153 } |
| 128 | 154 |
| 129 SECStatus status = SECFailure; | 155 SECStatus status = SECFailure; |
| 130 #if defined(USE_NSS_FOR_SSL_ONLY) | 156 #if !defined(USE_NSS) |
| 131 // Use the system certificate store, so initialize NSS without database. | 157 // Use the system certificate store, so initialize NSS without database. |
| 132 status = NSS_NoDB_Init(NULL); | 158 status = NSS_NoDB_Init(NULL); |
| 133 if (status != SECSuccess) { | 159 if (status != SECSuccess) { |
| 134 LOG(ERROR) << "Error initializing NSS without a persistent " | 160 LOG(ERROR) << "Error initializing NSS without a persistent " |
| 135 "database: NSS error code " << PR_GetError(); | 161 "database: NSS error code " << PR_GetError(); |
| 136 } | 162 } |
| 137 #else | 163 #else |
| 138 std::string database_dir = GetInitialConfigDirectory(); | 164 FilePath database_dir = GetInitialConfigDirectory(); |
| 139 if (!database_dir.empty()) { | 165 if (!database_dir.empty()) { |
| 166 UseLocalCacheOfNSSDatabaseIfNFS(database_dir); |
| 167 |
| 140 // Initialize with a persistent database (likely, ~/.pki/nssdb). | 168 // Initialize with a persistent database (likely, ~/.pki/nssdb). |
| 141 // Use "sql:" which can be shared by multiple processes safely. | 169 // Use "sql:" which can be shared by multiple processes safely. |
| 142 std::string nss_config_dir = | 170 std::string nss_config_dir = |
| 143 StringPrintf("sql:%s", database_dir.c_str()); | 171 StringPrintf("sql:%s", database_dir.value().c_str()); |
| 144 #if defined(OS_CHROMEOS) | 172 #if defined(OS_CHROMEOS) |
| 145 status = NSS_Init(nss_config_dir.c_str()); | 173 status = NSS_Init(nss_config_dir.c_str()); |
| 146 #else | 174 #else |
| 147 status = NSS_InitReadWrite(nss_config_dir.c_str()); | 175 status = NSS_InitReadWrite(nss_config_dir.c_str()); |
| 148 #endif | 176 #endif |
| 149 if (status != SECSuccess) { | 177 if (status != SECSuccess) { |
| 150 LOG(ERROR) << "Error initializing NSS with a persistent " | 178 LOG(ERROR) << "Error initializing NSS with a persistent " |
| 151 "database (" << nss_config_dir | 179 "database (" << nss_config_dir |
| 152 << "): NSS error code " << PR_GetError(); | 180 << "): NSS error code " << PR_GetError(); |
| 153 } | 181 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 174 PK11_InitPin(slot, NULL, NULL); | 202 PK11_InitPin(slot, NULL, NULL); |
| 175 PK11_FreeSlot(slot); | 203 PK11_FreeSlot(slot); |
| 176 } | 204 } |
| 177 | 205 |
| 178 // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011 | 206 // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011 |
| 179 // is fixed, we will no longer need the lock. We should detect this and not | 207 // is fixed, we will no longer need the lock. We should detect this and not |
| 180 // initialize a Lock here. | 208 // initialize a Lock here. |
| 181 write_lock_.reset(new Lock()); | 209 write_lock_.reset(new Lock()); |
| 182 | 210 |
| 183 root_ = InitDefaultRootCerts(); | 211 root_ = InitDefaultRootCerts(); |
| 184 #endif // defined(USE_NSS_FOR_SSL_ONLY) | 212 #endif // !defined(USE_NSS) |
| 185 } | 213 } |
| 186 | 214 |
| 187 ~NSSInitSingleton() { | 215 ~NSSInitSingleton() { |
| 188 if (real_db_slot_) { | 216 if (real_db_slot_) { |
| 189 SECMOD_CloseUserDB(real_db_slot_); | 217 SECMOD_CloseUserDB(real_db_slot_); |
| 190 PK11_FreeSlot(real_db_slot_); | 218 PK11_FreeSlot(real_db_slot_); |
| 191 real_db_slot_ = NULL; | 219 real_db_slot_ = NULL; |
| 192 } | 220 } |
| 193 if (root_) { | 221 if (root_) { |
| 194 SECMOD_UnloadUserModule(root_); | 222 SECMOD_UnloadUserModule(root_); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 205 } | 233 } |
| 206 } | 234 } |
| 207 | 235 |
| 208 #if defined(OS_CHROMEOS) | 236 #if defined(OS_CHROMEOS) |
| 209 void OpenPersistentNSSDB() { | 237 void OpenPersistentNSSDB() { |
| 210 if (!chromeos_user_logged_in_) { | 238 if (!chromeos_user_logged_in_) { |
| 211 chromeos_user_logged_in_ = true; | 239 chromeos_user_logged_in_ = true; |
| 212 | 240 |
| 213 const std::string modspec = | 241 const std::string modspec = |
| 214 StringPrintf("configDir='%s' tokenDescription='Real NSS database'", | 242 StringPrintf("configDir='%s' tokenDescription='Real NSS database'", |
| 215 GetDefaultConfigDirectory().c_str()); | 243 GetDefaultConfigDirectory().value().c_str()); |
| 216 real_db_slot_ = SECMOD_OpenUserDB(modspec.c_str()); | 244 real_db_slot_ = SECMOD_OpenUserDB(modspec.c_str()); |
| 217 if (real_db_slot_ == NULL) { | 245 if (real_db_slot_ == NULL) { |
| 218 LOG(ERROR) << "Error opening persistent database (" << modspec | 246 LOG(ERROR) << "Error opening persistent database (" << modspec |
| 219 << "): NSS error code " << PR_GetError(); | 247 << "): NSS error code " << PR_GetError(); |
| 220 } else { | 248 } else { |
| 221 if (PK11_NeedUserInit(real_db_slot_)) | 249 if (PK11_NeedUserInit(real_db_slot_)) |
| 222 PK11_InitPin(real_db_slot_, NULL, NULL); | 250 PK11_InitPin(real_db_slot_, NULL, NULL); |
| 223 } | 251 } |
| 224 } | 252 } |
| 225 } | 253 } |
| 226 #endif // defined(OS_CHROMEOS) | 254 #endif // defined(OS_CHROMEOS) |
| 227 | 255 |
| 228 PK11SlotInfo* GetDefaultKeySlot() { | 256 PK11SlotInfo* GetDefaultKeySlot() { |
| 229 if (real_db_slot_) | 257 if (real_db_slot_) |
| 230 return PK11_ReferenceSlot(real_db_slot_); | 258 return PK11_ReferenceSlot(real_db_slot_); |
| 231 return PK11_GetInternalKeySlot(); | 259 return PK11_GetInternalKeySlot(); |
| 232 } | 260 } |
| 233 | 261 |
| 234 #if defined(USE_NSS) | 262 #if defined(USE_NSS) |
| 235 Lock* write_lock() { | 263 Lock* write_lock() { |
| 236 return write_lock_.get(); | 264 return write_lock_.get(); |
| 237 } | 265 } |
| 238 #endif // defined(USE_NSS) | 266 #endif // defined(USE_NSS) |
| 239 | 267 |
| 240 private: | 268 private: |
| 241 PK11SlotInfo* real_db_slot_; // Overrides internal key slot if non-NULL. | 269 PK11SlotInfo* real_db_slot_; // Overrides internal key slot if non-NULL. |
| 242 SECMODModule *root_; | 270 SECMODModule *root_; |
| 243 bool chromeos_user_logged_in_; | 271 bool chromeos_user_logged_in_; |
| 244 #if defined(USE_NSS) | 272 #if defined(USE_NSS) |
| 245 scoped_ptr<Lock> write_lock_; | 273 scoped_ptr<Lock> write_lock_; |
| 246 #endif // defined(USE_NSS) | 274 #endif // defined(USE_NSS) |
| 247 }; | 275 }; |
| 248 | 276 |
| 249 } // namespace | 277 } // namespace |
| 250 | 278 |
| 251 namespace base { | 279 namespace base { |
| 252 | 280 |
| 253 void EnsureNSPRInit() { | 281 void EnsureNSPRInit() { |
| 254 Singleton<NSPRInitSingleton>::get(); | 282 Singleton<NSPRInitSingleton>::get(); |
| 255 } | 283 } |
| 256 | 284 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 300 exploded.millisecond = prxtime.tm_usec / 1000; | 328 exploded.millisecond = prxtime.tm_usec / 1000; |
| 301 | 329 |
| 302 return Time::FromUTCExploded(exploded); | 330 return Time::FromUTCExploded(exploded); |
| 303 } | 331 } |
| 304 | 332 |
| 305 PK11SlotInfo* GetDefaultNSSKeySlot() { | 333 PK11SlotInfo* GetDefaultNSSKeySlot() { |
| 306 return Singleton<NSSInitSingleton>::get()->GetDefaultKeySlot(); | 334 return Singleton<NSSInitSingleton>::get()->GetDefaultKeySlot(); |
| 307 } | 335 } |
| 308 | 336 |
| 309 } // namespace base | 337 } // namespace base |
| OLD | NEW |