1// Copyright (c) 2011 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 "chrome/browser/importer/nss_decryptor_win.h"
6
7#include "base/file_path.h"
8#include "base/sys_string_conversions.h"
9
10namespace {
11
12typedef BOOL (WINAPI* SetDllDirectoryFunc)(LPCTSTR lpPathName);
13
14// A helper class whose destructor calls SetDllDirectory(NULL) to undo the
15// effects of a previous SetDllDirectory call.
16class SetDllDirectoryCaller {
17 public:
18  explicit SetDllDirectoryCaller() : func_(NULL) { }
19
20  ~SetDllDirectoryCaller() {
21    if (func_)
22      func_(NULL);
23  }
24
25  // Sets the SetDllDirectory function pointer to activates this object.
26  void set_func(SetDllDirectoryFunc func) { func_ = func; }
27
28 private:
29  SetDllDirectoryFunc func_;
30};
31
32}  // namespace
33
34// static
35const wchar_t NSSDecryptor::kNSS3Library[] = L"nss3.dll";
36const wchar_t NSSDecryptor::kSoftokn3Library[] = L"softokn3.dll";
37const wchar_t NSSDecryptor::kPLDS4Library[] = L"plds4.dll";
38const wchar_t NSSDecryptor::kNSPR4Library[] = L"nspr4.dll";
39
40bool NSSDecryptor::Init(const FilePath& dll_path, const FilePath& db_path) {
41  // We call SetDllDirectory to work around a Purify bug (GetModuleHandle
42  // fails inside Purify under certain conditions).  SetDllDirectory only
43  // exists on Windows XP SP1 or later, so we look up its address at run time.
44  HMODULE kernel32_dll = GetModuleHandle(L"kernel32.dll");
45  if (kernel32_dll == NULL)
46    return false;
47  SetDllDirectoryFunc set_dll_directory =
48      (SetDllDirectoryFunc)GetProcAddress(kernel32_dll, "SetDllDirectoryW");
49  SetDllDirectoryCaller caller;
50
51  if (set_dll_directory != NULL) {
52    if (!set_dll_directory(dll_path.value().c_str()))
53      return false;
54    caller.set_func(set_dll_directory);
55    nss3_dll_ = LoadLibrary(kNSS3Library);
56    if (nss3_dll_ == NULL)
57      return false;
58  } else {
59    // Fall back on LoadLibraryEx if SetDllDirectory isn't available.  We
60    // actually prefer this method because it doesn't change the DLL search
61    // path, which is a process-wide property.
62    FilePath path = dll_path.Append(kNSS3Library);
63    nss3_dll_ = LoadLibraryEx(path.value().c_str(), NULL,
64                              LOAD_WITH_ALTERED_SEARCH_PATH);
65    if (nss3_dll_ == NULL)
66      return false;
67
68    // Firefox 2 uses NSS 3.11.  Firefox 3 uses NSS 3.12.  NSS 3.12 has two
69    // changes in its DLLs:
70    // 1. nss3.dll is not linked with softokn3.dll at build time, but rather
71    //    loads softokn3.dll using LoadLibrary in NSS_Init.
72    // 2. softokn3.dll has a new dependency sqlite3.dll.
73    // NSS_Init's LoadLibrary call has trouble finding sqlite3.dll.  To help
74    // it out, we preload softokn3.dll using LoadLibraryEx with the
75    // LOAD_WITH_ALTERED_SEARCH_PATH flag.  This helps because LoadLibrary
76    // doesn't load a DLL again if it's already loaded.  This workaround is
77    // harmless for NSS 3.11.
78    path = FilePath(dll_path).Append(kSoftokn3Library);
79    softokn3_dll_ = LoadLibraryEx(path.value().c_str(), NULL,
80                                  LOAD_WITH_ALTERED_SEARCH_PATH);
81    if (softokn3_dll_ == NULL) {
82      Free();
83      return false;
84    }
85  }
86  HMODULE plds4_dll = GetModuleHandle(kPLDS4Library);
87  HMODULE nspr4_dll = GetModuleHandle(kNSPR4Library);
88
89  return InitNSS(db_path, plds4_dll, nspr4_dll);
90}
91
92NSSDecryptor::NSSDecryptor()
93    : NSS_Init(NULL), NSS_Shutdown(NULL), PK11_GetInternalKeySlot(NULL),
94      PK11_CheckUserPassword(NULL), PK11_FreeSlot(NULL),
95      PK11_Authenticate(NULL), PK11SDR_Decrypt(NULL), SECITEM_FreeItem(NULL),
96      PL_ArenaFinish(NULL), PR_Cleanup(NULL),
97      nss3_dll_(NULL), softokn3_dll_(NULL),
98      is_nss_initialized_(false) {
99}
100
101NSSDecryptor::~NSSDecryptor() {
102  Free();
103}
104
105bool NSSDecryptor::InitNSS(const FilePath& db_path,
106                           base::NativeLibrary plds4_dll,
107                           base::NativeLibrary nspr4_dll) {
108  // NSPR DLLs are already loaded now.
109  if (plds4_dll == NULL || nspr4_dll == NULL) {
110    Free();
111    return false;
112  }
113
114  // Gets the function address.
115  NSS_Init = (NSSInitFunc)
116      base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "NSS_Init");
117  NSS_Shutdown = (NSSShutdownFunc)
118      base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "NSS_Shutdown");
119  PK11_GetInternalKeySlot = (PK11GetInternalKeySlotFunc)
120      base::GetFunctionPointerFromNativeLibrary(nss3_dll_,
121                                                "PK11_GetInternalKeySlot");
122  PK11_FreeSlot = (PK11FreeSlotFunc)
123      base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11_FreeSlot");
124  PK11_Authenticate = (PK11AuthenticateFunc)
125      base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11_Authenticate");
126  PK11SDR_Decrypt = (PK11SDRDecryptFunc)
127      base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11SDR_Decrypt");
128  SECITEM_FreeItem = (SECITEMFreeItemFunc)
129      base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "SECITEM_FreeItem");
130  PL_ArenaFinish = (PLArenaFinishFunc)
131      base::GetFunctionPointerFromNativeLibrary(plds4_dll, "PL_ArenaFinish");
132  PR_Cleanup = (PRCleanupFunc)
133      base::GetFunctionPointerFromNativeLibrary(nspr4_dll, "PR_Cleanup");
134
135  if (NSS_Init == NULL || NSS_Shutdown == NULL ||
136      PK11_GetInternalKeySlot == NULL || PK11_FreeSlot == NULL ||
137      PK11_Authenticate == NULL || PK11SDR_Decrypt == NULL ||
138      SECITEM_FreeItem == NULL || PL_ArenaFinish == NULL ||
139      PR_Cleanup == NULL) {
140    Free();
141    return false;
142  }
143
144  SECStatus result = NSS_Init(base::SysWideToNativeMB(db_path.value()).c_str());
145  if (result != SECSuccess) {
146    Free();
147    return false;
148  }
149
150  is_nss_initialized_ = true;
151  return true;
152}
153
154void NSSDecryptor::Free() {
155  if (is_nss_initialized_) {
156    NSS_Shutdown();
157    PL_ArenaFinish();
158    PR_Cleanup();
159    is_nss_initialized_ = false;
160  }
161  if (softokn3_dll_ != NULL)
162    base::UnloadNativeLibrary(softokn3_dll_);
163  if (nss3_dll_ != NULL)
164    base::UnloadNativeLibrary(nss3_dll_);
165  NSS_Init = NULL;
166  NSS_Shutdown = NULL;
167  PK11_GetInternalKeySlot = NULL;
168  PK11_FreeSlot = NULL;
169  PK11_Authenticate = NULL;
170  PK11SDR_Decrypt = NULL;
171  SECITEM_FreeItem = NULL;
172  PL_ArenaFinish = NULL;
173  PR_Cleanup = NULL;
174  nss3_dll_ = NULL;
175  softokn3_dll_ = NULL;
176}
177