Home | History | Annotate | Download | only in base
      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