Bug 1929922 - Extending NSS with LoadModuleFromFunction functionality r=keeler,nss-reviewers
Differential Revision:
https://phabricator.services.mozilla.com/D230392
--- a/automation/abi-check/expected-report-libnss3.so.txt
+++ b/automation/abi-check/expected-report-libnss3.so.txt
@@ -0,0 +1,3 @@
+1 Added function:
+
+ 'function SECMODModule* SECMOD_LoadUserModuleWithFunction(const char*, CK_C_GetFunctionList)' {SECMOD_LoadUserModuleWithFunction@@NSS_3.107}
--- a/gtests/pk11_gtest/pk11_module_unittest.cc
+++ b/gtests/pk11_gtest/pk11_module_unittest.cc
@@ -150,9 +150,89 @@ class Pkcs11NonAsciiTest : public ::test
};
TEST_F(Pkcs11NonAsciiTest, LoadUnload) {
ScopedSECMODModule module(SECMOD_FindModule("Pkcs11NonAsciiTest"));
EXPECT_NE(nullptr, module);
}
#endif // defined(_WIN32)
+class Pkcs11ModuleLoadFunctionTest : public ::testing::Test {
+ public:
+ Pkcs11ModuleLoadFunctionTest() { library = NULL; };
+
+ void TearDown() override {
+ if (library != NULL) {
+ PR_UnloadLibrary(library);
+ }
+ }
+ PRLibrary *library;
+};
+
+CK_RV NotSuppoted_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV SupportedButNull(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) {
+ ppFunctionList = NULL;
+ return CKR_OK;
+}
+
+TEST_F(Pkcs11ModuleLoadFunctionTest, LoadModuleWithNullFunc) {
+ ScopedSECMODModule userModule(
+ SECMOD_LoadUserModuleWithFunction("LoadFunctionModule", NULL));
+ EXPECT_NE(userModule, nullptr);
+ EXPECT_FALSE(userModule->loaded);
+}
+
+TEST_F(Pkcs11ModuleLoadFunctionTest, LoadModuleWithUnsupportedFunc) {
+ ScopedSECMODModule userModule(SECMOD_LoadUserModuleWithFunction(
+ "LoadFunctionModule", &NotSuppoted_GetFunctionList));
+ EXPECT_FALSE(userModule->loaded);
+}
+
+TEST_F(Pkcs11ModuleLoadFunctionTest, LoadModuleWithEmptyFunctionList) {
+ ScopedSECMODModule userModule(SECMOD_LoadUserModuleWithFunction(
+ "LoadFunctionModule", &SupportedButNull));
+ EXPECT_NE(userModule, nullptr);
+ EXPECT_FALSE(userModule->loaded);
+}
+
+TEST_F(Pkcs11ModuleLoadFunctionTest, SuccessLoadModuleWithFunction) {
+ library = PR_LoadLibrary(DLL_PREFIX "pkcs11testmodule." DLL_SUFFIX);
+ EXPECT_NE(nullptr, library);
+
+ CK_C_GetFunctionList fentry = NULL;
+ fentry = (CK_C_GetFunctionList)PR_FindSymbol(library, "C_GetFunctionList");
+ EXPECT_NE(nullptr, fentry);
+
+ ScopedSECMODModule userModule(
+ SECMOD_LoadUserModuleWithFunction("LoadFunctionModule", fentry));
+ EXPECT_NE(nullptr, userModule);
+ EXPECT_EQ(userModule->loaded, PR_TRUE);
+
+ /* We can find the module*/
+ ScopedSECMODModule module(SECMOD_FindModule("LoadFunctionModule"));
+ EXPECT_NE(nullptr, module);
+
+ CK_INFO info;
+ EXPECT_EQ(SECSuccess, PK11_GetModInfo(userModule.get(), &info));
+ /* See pkcs11testmodule.cpp */
+ CK_VERSION expectedCryptokiVersion = {2, 2};
+ CK_VERSION expectedLibraryVersion = {0, 0};
+ EXPECT_EQ(info.cryptokiVersion.minor, expectedCryptokiVersion.minor);
+ EXPECT_EQ(info.cryptokiVersion.major, expectedCryptokiVersion.major);
+
+ EXPECT_EQ(
+ 0, PORT_Memcmp((char *)info.manufacturerID, "Test PKCS11 Manufacturer ID",
+ sizeof("Test PKCS11 Manufacturer ID") - 1));
+ EXPECT_EQ(info.flags, 0UL);
+
+ EXPECT_EQ(0,
+ PORT_Memcmp((char *)info.libraryDescription, "Test PKCS11 Library",
+ sizeof("Test PKCS11 Library") - 1));
+ EXPECT_EQ(info.libraryVersion.minor, expectedLibraryVersion.minor);
+ EXPECT_EQ(info.libraryVersion.major, expectedLibraryVersion.major);
+
+ EXPECT_EQ(SECSuccess, SECMOD_UnloadUserModule(userModule.get()));
+}
+
} // namespace nss_test
--- a/lib/nss/nss.def
+++ b/lib/nss/nss.def
@@ -1265,8 +1265,14 @@ SEC_GetMgfTypeByOidTag;
SEC_PKCS5GetCryptoFromAlgTag;
SEC_PKCS5GetHashAlgorithm;
SEC_PKCS5GetHashFromAlgTag;
SECKEY_EnforceKeySize;
SECKEY_PrivateKeyStrengthInBits;
;+ local:
;+ *;
;+};
+;+NSS_3.107 { # NSS 3.107 release
+;+ global:
+SECMOD_LoadUserModuleWithFunction;
+;+ local:
+;+ *;
+;+};
--- a/lib/pk11wrap/pk11load.c
+++ b/lib/pk11wrap/pk11load.c
@@ -382,41 +382,29 @@ softoken_LoadDSO(void)
}
#else
CK_RV NSC_GetInterface(CK_UTF8CHAR_PTR pInterfaceName,
CK_VERSION_PTR pVersion,
CK_INTERFACE_PTR_PTR *ppInterface, CK_FLAGS flags);
char **NSC_ModuleDBFunc(unsigned long function, char *parameters, void *args);
#endif
-/*
- * load a new module into our address space and initialize it.
- */
SECStatus
-secmod_LoadPKCS11Module(SECMODModule *mod, SECMODModule **oldModule)
+secmod_DetermineModuleFunctionList(SECMODModule *mod)
{
PRLibrary *library = NULL;
CK_C_GetInterface ientry = NULL;
CK_C_GetFunctionList fentry = NULL;
- CK_INFO info;
- CK_ULONG slotCount = 0;
- SECStatus rv;
- PRBool alreadyLoaded = PR_FALSE;
char *disableUnload = NULL;
#ifndef NSS_STATIC_SOFTOKEN
const char *nss_interface;
const char *nss_function;
#endif
CK_INTERFACE_PTR interface;
- if (mod->loaded)
- return SECSuccess;
-
- mod->fipsIndicator = NULL;
-
/* internal modules get loaded from their internal list */
if (mod->internal && (mod->dllName == NULL)) {
#ifdef NSS_STATIC_SOFTOKEN
ientry = (CK_C_GetInterface)NSC_GetInterface;
#else
/*
* Loads softoken as a dynamic library,
* even though the rest of NSS assumes this as the "internal" module.
@@ -548,16 +536,35 @@ secmod_LoadPKCS11Module(SECMODModule *mo
#ifdef DEBUG_MODULE
modToDBG = PR_GetEnvSecure("NSS_DEBUG_PKCS11_MODULE");
if (modToDBG && strcmp(mod->commonName, modToDBG) == 0) {
mod->functionList = (void *)nss_InsertDeviceLog(
(CK_FUNCTION_LIST_3_0_PTR)mod->functionList);
}
#endif
+ return SECSuccess;
+
+fail:
+ mod->functionList = NULL;
+ disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
+ if (library && !disableUnload) {
+ PR_UnloadLibrary(library);
+ }
+ return SECFailure;
+}
+
+SECStatus
+secmod_InitializeModuleAndGetSlotInfo(SECMODModule *mod, SECMODModule **oldModule)
+{
+ CK_INFO info;
+ CK_ULONG slotCount = 0;
+ SECStatus rv;
+ PRBool alreadyLoaded = PR_FALSE;
+
/* This test operation makes sure our locking system is
* consistent even if we are using non-thread safe tokens by
* simulating unsafe tokens with safe ones. */
mod->isThreadSafe = !PR_GetEnvSecure("NSS_FORCE_TOKEN_LOCK");
/* Now we initialize the module */
rv = secmod_ModuleInit(mod, oldModule, &alreadyLoaded);
if (rv != SECSuccess) {
@@ -638,21 +645,88 @@ secmod_LoadPKCS11Module(SECMODModule *mo
mod->moduleID = nextModuleID++;
return SECSuccess;
fail2:
if (enforceAlreadyInitializedError || (!alreadyLoaded)) {
PK11_GETTAB(mod)->C_Finalize(NULL);
}
fail:
mod->functionList = NULL;
- disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
- if (library && !disableUnload) {
- PR_UnloadLibrary(library);
+ return SECFailure;
+}
+
+/*
+ * load a new module into our address space and initialize it.
+ */
+SECStatus
+secmod_LoadPKCS11Module(SECMODModule *mod, SECMODModule **oldModule)
+{
+ SECStatus rv = SECFailure;
+ if (mod->loaded) {
+ return SECSuccess;
+ }
+
+ mod->fipsIndicator = NULL;
+
+ rv = secmod_DetermineModuleFunctionList(mod);
+ if (rv != SECSuccess) { // The error code is set up by secmod_DetermineModuleFunctionList.
+ return rv;
+ }
+
+ if (mod->loaded == PR_TRUE) {
+ return SECSuccess;
+ }
+
+ rv = secmod_InitializeModuleAndGetSlotInfo(mod, oldModule);
+ if (rv != SECSuccess) { // The error code is set up by secmod_InitializeModuleAndGetSlotInfo
+ return rv;
}
- return SECFailure;
+
+ return SECSuccess;
+}
+
+/*
+ * load a new module using provided fentry function
+ */
+SECStatus
+secmod_LoadPKCS11ModuleFromFunction(SECMODModule *mod, SECMODModule **oldModule,
+ CK_C_GetFunctionList fentry)
+{
+ SECStatus rv = SECFailure;
+ CK_RV crv;
+ if (mod->loaded) {
+ return SECSuccess;
+ }
+
+ mod->fipsIndicator = NULL;
+
+ if (!fentry) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ crv = fentry((CK_FUNCTION_LIST_PTR *)&mod->functionList);
+ if (crv != CKR_OK) {
+ mod->functionList = NULL;
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ if (mod->functionList == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ mod->flags = 0;
+ rv = secmod_InitializeModuleAndGetSlotInfo(mod, oldModule);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ return SECSuccess;
}
SECStatus
SECMOD_UnloadModule(SECMODModule *mod)
{
PRLibrary *library;
char *disableUnload = NULL;
@@ -687,19 +761,19 @@ SECMOD_UnloadModule(SECMODModule *mod)
}
loadSoftokenOnce = pristineCallOnce;
}
#endif
return SECSuccess;
}
library = (PRLibrary *)mod->library;
- /* paranoia */
+ /* if no library, then we should not unload it */
if (library == NULL) {
- return SECFailure;
+ return SECSuccess;
}
disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
if (!disableUnload) {
PR_UnloadLibrary(library);
}
return SECSuccess;
}
--- a/lib/pk11wrap/pk11pars.c
+++ b/lib/pk11wrap/pk11pars.c
@@ -2235,16 +2235,82 @@ loser:
if (module->loaded) {
SECMOD_UnloadModule(module);
}
SECMOD_AddModuleToUnloadList(module);
}
return module;
}
+SECMODModule *
+SECMOD_LoadModuleWithFunction(const char *moduleName, CK_C_GetFunctionList fentry)
+{
+ SECMODModule *module = NULL;
+ SECMODModule *oldModule = NULL;
+ SECStatus rv;
+
+ /* initialize the underlying module structures */
+ SECMOD_Init();
+
+ module = secmod_NewModule();
+ if (module == NULL) {
+ goto loser;
+ }
+
+ module->commonName = PORT_ArenaStrdup(module->arena, moduleName ? moduleName : "");
+ module->internal = PR_FALSE;
+ module->isFIPS = PR_FALSE;
+ /* if the system FIPS mode is enabled, force FIPS to be on */
+ if (SECMOD_GetSystemFIPSEnabled()) {
+ module->isFIPS = PR_TRUE;
+ }
+
+ module->isCritical = PR_FALSE;
+ /* new field */
+ module->trustOrder = NSSUTIL_DEFAULT_TRUST_ORDER;
+ /* new field */
+ module->cipherOrder = NSSUTIL_DEFAULT_CIPHER_ORDER;
+ /* new field */
+ module->isModuleDB = PR_FALSE;
+ module->moduleDBOnly = PR_FALSE;
+
+ module->ssl[0] = 0;
+ module->ssl[1] = 0;
+
+ secmod_PrivateModuleCount++;
+
+ /* load it */
+ rv = secmod_LoadPKCS11ModuleFromFunction(module, &oldModule, fentry);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* if we just reload an old module, no need to add it to any lists.
+ * we simple release all our references */
+ if (oldModule) {
+ /* This module already exists, don't link it anywhere. This
+ * will probably destroy this module */
+ SECMOD_DestroyModule(module);
+ return oldModule;
+ }
+
+ SECMOD_AddModuleToList(module);
+ /* handle any additional work here */
+ return module;
+
+loser:
+ if (module) {
+ if (module->loaded) {
+ SECMOD_UnloadModule(module);
+ }
+ SECMOD_AddModuleToUnloadList(module);
+ }
+ return module;
+}
+
/*
* load a PKCS#11 module and add it to the default NSS trust domain
*/
SECMODModule *
SECMOD_LoadUserModule(char *modulespec, SECMODModule *parent, PRBool recurse)
{
SECStatus rv = SECSuccess;
SECMODModule *newmod = SECMOD_LoadModule(modulespec, parent, recurse);
@@ -2257,16 +2323,35 @@ SECMOD_LoadUserModule(char *modulespec,
if (SECSuccess != rv) {
SECMOD_DestroyModule(newmod);
return NULL;
}
}
return newmod;
}
+SECMODModule *
+SECMOD_LoadUserModuleWithFunction(const char *moduleName, CK_C_GetFunctionList fentry)
+{
+ SECStatus rv = SECSuccess;
+ SECMODModule *newmod = SECMOD_LoadModuleWithFunction(moduleName, fentry);
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+
+ if (newmod) {
+ SECMOD_GetReadLock(moduleLock);
+ rv = STAN_AddModuleToDefaultTrustDomain(newmod);
+ SECMOD_ReleaseReadLock(moduleLock);
+ if (SECSuccess != rv) {
+ SECMOD_DestroyModule(newmod);
+ return NULL;
+ }
+ }
+ return newmod;
+}
+
/*
* remove the PKCS#11 module from the default NSS trust domain, call
* C_Finalize, and destroy the module structure
*/
SECStatus
SECMOD_UnloadUserModule(SECMODModule *mod)
{
SECStatus rv = SECSuccess;
--- a/lib/pk11wrap/secmod.h
+++ b/lib/pk11wrap/secmod.h
@@ -1,16 +1,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _SECMOD_H_
#define _SECMOD_H_
#include "seccomon.h"
#include "secmodt.h"
#include "prinrval.h"
+#include "pkcs11.h"
/* These mechanisms flags are visible to all other libraries. */
/* They must be converted to internal SECMOD_*_FLAG */
/* if used inside the functions of the security library */
#define PUBLIC_MECH_RSA_FLAG 0x00000001ul
#define PUBLIC_MECH_DSA_FLAG 0x00000002ul
#define PUBLIC_MECH_RC2_FLAG 0x00000004ul
#define PUBLIC_MECH_RC4_FLAG 0x00000008ul
@@ -55,16 +56,19 @@ SEC_BEGIN_PROTOS
/* Initialization */
extern SECMODModule *SECMOD_LoadModule(char *moduleSpec, SECMODModule *parent,
PRBool recurse);
extern SECMODModule *SECMOD_LoadUserModule(char *moduleSpec, SECMODModule *parent,
PRBool recurse);
+extern SECMODModule *SECMOD_LoadUserModuleWithFunction(const char *moduleName,
+ CK_C_GetFunctionList fentry);
+
SECStatus SECMOD_UnloadUserModule(SECMODModule *mod);
SECMODModule *SECMOD_CreateModule(const char *lib, const char *name,
const char *param, const char *nss);
SECMODModule *SECMOD_CreateModuleEx(const char *lib, const char *name,
const char *param, const char *nss,
const char *config);
/*
--- a/lib/pk11wrap/secmodi.h
+++ b/lib/pk11wrap/secmodi.h
@@ -52,16 +52,18 @@ extern SECMODModuleList *SECMOD_NewModul
extern SECMODModuleList *SECMOD_DestroyModuleListElement(SECMODModuleList *);
extern void SECMOD_DestroyModuleList(SECMODModuleList *);
extern SECStatus SECMOD_AddModule(SECMODModule *newModule);
extern unsigned long SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags);
/* Library functions */
SECStatus secmod_LoadPKCS11Module(SECMODModule *, SECMODModule **oldModule);
+SECStatus secmod_LoadPKCS11ModuleFromFunction(SECMODModule *, SECMODModule **oldModule, CK_C_GetFunctionList f);
+
SECStatus SECMOD_UnloadModule(SECMODModule *);
void SECMOD_SetInternalModule(SECMODModule *);
PRBool secmod_IsInternalKeySlot(SECMODModule *);
void secmod_SetInternalKeySlotFlag(SECMODModule *mod, PRBool val);
/* tools for checking if we are loading the same database twice */
typedef struct SECMODConfigListStr SECMODConfigList;
/* collect all the databases in a given spec */