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)
GetDefaultConfigDirectory()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.
InitDefaultRootCerts()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:
NSPRInitSingleton()66 NSPRInitSingleton() {
67 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
68 }
69
~NSPRInitSingleton()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:
NSSInitSingleton()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
~NSSInitSingleton()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
EnsureNSPRInit()166 void EnsureNSPRInit() {
167 Singleton<NSPRInitSingleton>::get();
168 }
169
EnsureNSSInit()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).
PRTimeToBaseTime(PRTime prtime)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