// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/importer/nss_decryptor_win.h" #include "base/file_path.h" #include "base/sys_string_conversions.h" namespace { typedef BOOL (WINAPI* SetDllDirectoryFunc)(LPCTSTR lpPathName); // A helper class whose destructor calls SetDllDirectory(NULL) to undo the // effects of a previous SetDllDirectory call. class SetDllDirectoryCaller { public: explicit SetDllDirectoryCaller() : func_(NULL) { } ~SetDllDirectoryCaller() { if (func_) func_(NULL); } // Sets the SetDllDirectory function pointer to activates this object. void set_func(SetDllDirectoryFunc func) { func_ = func; } private: SetDllDirectoryFunc func_; }; } // namespace // static const wchar_t NSSDecryptor::kNSS3Library[] = L"nss3.dll"; const wchar_t NSSDecryptor::kSoftokn3Library[] = L"softokn3.dll"; const wchar_t NSSDecryptor::kPLDS4Library[] = L"plds4.dll"; const wchar_t NSSDecryptor::kNSPR4Library[] = L"nspr4.dll"; bool NSSDecryptor::Init(const FilePath& dll_path, const FilePath& db_path) { // We call SetDllDirectory to work around a Purify bug (GetModuleHandle // fails inside Purify under certain conditions). SetDllDirectory only // exists on Windows XP SP1 or later, so we look up its address at run time. HMODULE kernel32_dll = GetModuleHandle(L"kernel32.dll"); if (kernel32_dll == NULL) return false; SetDllDirectoryFunc set_dll_directory = (SetDllDirectoryFunc)GetProcAddress(kernel32_dll, "SetDllDirectoryW"); SetDllDirectoryCaller caller; if (set_dll_directory != NULL) { if (!set_dll_directory(dll_path.value().c_str())) return false; caller.set_func(set_dll_directory); nss3_dll_ = LoadLibrary(kNSS3Library); if (nss3_dll_ == NULL) return false; } else { // Fall back on LoadLibraryEx if SetDllDirectory isn't available. We // actually prefer this method because it doesn't change the DLL search // path, which is a process-wide property. FilePath path = dll_path.Append(kNSS3Library); nss3_dll_ = LoadLibraryEx(path.value().c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if (nss3_dll_ == NULL) return false; // Firefox 2 uses NSS 3.11. Firefox 3 uses NSS 3.12. NSS 3.12 has two // changes in its DLLs: // 1. nss3.dll is not linked with softokn3.dll at build time, but rather // loads softokn3.dll using LoadLibrary in NSS_Init. // 2. softokn3.dll has a new dependency sqlite3.dll. // NSS_Init's LoadLibrary call has trouble finding sqlite3.dll. To help // it out, we preload softokn3.dll using LoadLibraryEx with the // LOAD_WITH_ALTERED_SEARCH_PATH flag. This helps because LoadLibrary // doesn't load a DLL again if it's already loaded. This workaround is // harmless for NSS 3.11. path = FilePath(dll_path).Append(kSoftokn3Library); softokn3_dll_ = LoadLibraryEx(path.value().c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if (softokn3_dll_ == NULL) { Free(); return false; } } HMODULE plds4_dll = GetModuleHandle(kPLDS4Library); HMODULE nspr4_dll = GetModuleHandle(kNSPR4Library); return InitNSS(db_path, plds4_dll, nspr4_dll); } NSSDecryptor::NSSDecryptor() : NSS_Init(NULL), NSS_Shutdown(NULL), PK11_GetInternalKeySlot(NULL), PK11_CheckUserPassword(NULL), PK11_FreeSlot(NULL), PK11_Authenticate(NULL), PK11SDR_Decrypt(NULL), SECITEM_FreeItem(NULL), PL_ArenaFinish(NULL), PR_Cleanup(NULL), nss3_dll_(NULL), softokn3_dll_(NULL), is_nss_initialized_(false) { } NSSDecryptor::~NSSDecryptor() { Free(); } bool NSSDecryptor::InitNSS(const FilePath& db_path, base::NativeLibrary plds4_dll, base::NativeLibrary nspr4_dll) { // NSPR DLLs are already loaded now. if (plds4_dll == NULL || nspr4_dll == NULL) { Free(); return false; } // Gets the function address. NSS_Init = (NSSInitFunc) base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "NSS_Init"); NSS_Shutdown = (NSSShutdownFunc) base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "NSS_Shutdown"); PK11_GetInternalKeySlot = (PK11GetInternalKeySlotFunc) base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11_GetInternalKeySlot"); PK11_FreeSlot = (PK11FreeSlotFunc) base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11_FreeSlot"); PK11_Authenticate = (PK11AuthenticateFunc) base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11_Authenticate"); PK11SDR_Decrypt = (PK11SDRDecryptFunc) base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11SDR_Decrypt"); SECITEM_FreeItem = (SECITEMFreeItemFunc) base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "SECITEM_FreeItem"); PL_ArenaFinish = (PLArenaFinishFunc) base::GetFunctionPointerFromNativeLibrary(plds4_dll, "PL_ArenaFinish"); PR_Cleanup = (PRCleanupFunc) base::GetFunctionPointerFromNativeLibrary(nspr4_dll, "PR_Cleanup"); if (NSS_Init == NULL || NSS_Shutdown == NULL || PK11_GetInternalKeySlot == NULL || PK11_FreeSlot == NULL || PK11_Authenticate == NULL || PK11SDR_Decrypt == NULL || SECITEM_FreeItem == NULL || PL_ArenaFinish == NULL || PR_Cleanup == NULL) { Free(); return false; } SECStatus result = NSS_Init(base::SysWideToNativeMB(db_path.value()).c_str()); if (result != SECSuccess) { Free(); return false; } is_nss_initialized_ = true; return true; } void NSSDecryptor::Free() { if (is_nss_initialized_) { NSS_Shutdown(); PL_ArenaFinish(); PR_Cleanup(); is_nss_initialized_ = false; } if (softokn3_dll_ != NULL) base::UnloadNativeLibrary(softokn3_dll_); if (nss3_dll_ != NULL) base::UnloadNativeLibrary(nss3_dll_); NSS_Init = NULL; NSS_Shutdown = NULL; PK11_GetInternalKeySlot = NULL; PK11_FreeSlot = NULL; PK11_Authenticate = NULL; PK11SDR_Decrypt = NULL; SECITEM_FreeItem = NULL; PL_ArenaFinish = NULL; PR_Cleanup = NULL; nss3_dll_ = NULL; softokn3_dll_ = NULL; }