• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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