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 3 // found in the LICENSE file. 4 5 #include "base/nss_util.h" 6 7 #include <nss.h> 8 #include <plarena.h> 9 #include <prerror.h> 10 #include <prinit.h> 11 #include <prtime.h> 12 #include <pk11pub.h> 13 #include <secmod.h> 14 15 #include "base/file_util.h" 16 #include "base/logging.h" 17 #include "base/singleton.h" 18 #include "base/string_util.h" 19 20 // On some platforms, we use NSS for SSL only -- we don't use NSS for crypto 21 // or certificate verification, and we don't use the NSS certificate and key 22 // databases. 23 #if defined(OS_WIN) 24 #define USE_NSS_FOR_SSL_ONLY 1 25 #endif 26 27 namespace { 28 29 #if !defined(USE_NSS_FOR_SSL_ONLY) 30 std::string GetDefaultConfigDirectory() { 31 const char* home = getenv("HOME"); 32 if (home == NULL) { 33 LOG(ERROR) << "$HOME is not set."; 34 return ""; 35 } 36 FilePath dir(home); 37 dir = dir.AppendASCII(".pki").AppendASCII("nssdb"); 38 if (!file_util::CreateDirectory(dir)) { 39 LOG(ERROR) << "Failed to create ~/.pki/nssdb directory."; 40 return ""; 41 } 42 return dir.value(); 43 } 44 45 // Load nss's built-in root certs. 46 SECMODModule *InitDefaultRootCerts() { 47 const char* kModulePath = "libnssckbi.so"; 48 char modparams[1024]; 49 snprintf(modparams, sizeof(modparams), 50 "name=\"Root Certs\" library=\"%s\"", kModulePath); 51 SECMODModule *root = SECMOD_LoadUserModule(modparams, NULL, PR_FALSE); 52 if (root) 53 return root; 54 55 // Aw, snap. Can't find/load root cert shared library. 56 // This will make it hard to talk to anybody via https. 57 NOTREACHED(); 58 return NULL; 59 } 60 #endif // !defined(USE_NSS_FOR_SSL_ONLY) 61 62 // A singleton to initialize/deinitialize NSPR. 63 // Separate from the NSS singleton because we initialize NSPR on the UI thread. 64 class NSPRInitSingleton { 65 public: 66 NSPRInitSingleton() { 67 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 68 } 69 70 ~NSPRInitSingleton() { 71 PL_ArenaFinish(); 72 PRStatus prstatus = PR_Cleanup(); 73 if (prstatus != PR_SUCCESS) { 74 LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?"; 75 } 76 } 77 }; 78 79 class NSSInitSingleton { 80 public: 81 NSSInitSingleton() : root_(NULL) { 82 base::EnsureNSPRInit(); 83 84 // We *must* have NSS >= 3.12.3. See bug 26448. 85 COMPILE_ASSERT( 86 (NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH >= 3) || 87 (NSS_VMAJOR == 3 && NSS_VMINOR > 12) || 88 (NSS_VMAJOR > 3), 89 nss_version_check_failed); 90 // Also check the run-time NSS version. 91 // NSS_VersionCheck is a >= check, not strict equality. 92 CHECK(NSS_VersionCheck("3.12.3")) << "We depend on NSS >= 3.12.3. " 93 "If NSS is up to date, please " 94 "update NSPR to the latest version."; 95 96 SECStatus status = SECFailure; 97 #if defined(USE_NSS_FOR_SSL_ONLY) 98 // Use the system certificate store, so initialize NSS without database. 99 status = NSS_NoDB_Init(NULL); 100 if (status != SECSuccess) { 101 LOG(ERROR) << "Error initializing NSS without a persistent " 102 "database: NSS error code " << PR_GetError(); 103 } 104 #else 105 std::string database_dir = GetDefaultConfigDirectory(); 106 if (!database_dir.empty()) { 107 // Initialize with a persistant database (~/.pki/nssdb). 108 // Use "sql:" which can be shared by multiple processes safely. 109 std::string nss_config_dir = 110 StringPrintf("sql:%s", database_dir.c_str()); 111 status = NSS_InitReadWrite(nss_config_dir.c_str()); 112 if (status != SECSuccess) { 113 LOG(ERROR) << "Error initializing NSS with a persistent " 114 "database (" << nss_config_dir 115 << "): NSS error code " << PR_GetError(); 116 } 117 } 118 if (status != SECSuccess) { 119 LOG(WARNING) << "Initialize NSS without a persistent database " 120 "(~/.pki/nssdb)."; 121 status = NSS_NoDB_Init(NULL); 122 if (status != SECSuccess) { 123 LOG(ERROR) << "Error initializing NSS without a persistent " 124 "database: NSS error code " << PR_GetError(); 125 } 126 } 127 128 // If we haven't initialized the password for the NSS databases, 129 // initialize an empty-string password so that we don't need to 130 // log in. 131 PK11SlotInfo* slot = PK11_GetInternalKeySlot(); 132 if (slot) { 133 if (PK11_NeedUserInit(slot)) 134 PK11_InitPin(slot, NULL, NULL); 135 PK11_FreeSlot(slot); 136 } 137 138 root_ = InitDefaultRootCerts(); 139 #endif // defined(USE_NSS_FOR_SSL_ONLY) 140 } 141 142 ~NSSInitSingleton() { 143 if (root_) { 144 SECMOD_UnloadUserModule(root_); 145 SECMOD_DestroyModule(root_); 146 root_ = NULL; 147 } 148 149 SECStatus status = NSS_Shutdown(); 150 if (status != SECSuccess) { 151 // We LOG(INFO) because this failure is relatively harmless 152 // (leaking, but we're shutting down anyway). 153 LOG(INFO) << "NSS_Shutdown failed; see " 154 "http://code.google.com/p/chromium/issues/detail?id=4609"; 155 } 156 } 157 158 private: 159 SECMODModule *root_; 160 }; 161 162 } // namespace 163 164 namespace base { 165 166 void EnsureNSPRInit() { 167 Singleton<NSPRInitSingleton>::get(); 168 } 169 170 void EnsureNSSInit() { 171 Singleton<NSSInitSingleton>::get(); 172 } 173 174 // TODO(port): Implement this more simply. We can convert by subtracting an 175 // offset (the difference between NSPR's and base::Time's epochs). 176 Time PRTimeToBaseTime(PRTime prtime) { 177 PRExplodedTime prxtime; 178 PR_ExplodeTime(prtime, PR_GMTParameters, &prxtime); 179 180 base::Time::Exploded exploded; 181 exploded.year = prxtime.tm_year; 182 exploded.month = prxtime.tm_month + 1; 183 exploded.day_of_week = prxtime.tm_wday; 184 exploded.day_of_month = prxtime.tm_mday; 185 exploded.hour = prxtime.tm_hour; 186 exploded.minute = prxtime.tm_min; 187 exploded.second = prxtime.tm_sec; 188 exploded.millisecond = prxtime.tm_usec / 1000; 189 190 return Time::FromUTCExploded(exploded); 191 } 192 193 } // namespace base 194