Bug 1929922 - Extending NSS with LoadModuleFromFunction functionality r=keeler,nss-reviewers
authorAnna Weine <anna.weine@mozilla.com>
Tue, 17 Dec 2024 09:31:02 +0000 (7 months ago)
changeset 17116 5112ad554ce61f2bc78e2671a26bac4deb559552
parent 17115 a25bcb7b76094cf1331efc366d249f6428b0d911
child 17117 857d9ec3ba5a28262565cf306b006df9f6cb3e37
push id4675
push usernkulatova@mozilla.com
push dateTue, 17 Dec 2024 09:33:08 +0000 (7 months ago)
reviewerskeeler, nss-reviewers
bugs1929922
Bug 1929922 - Extending NSS with LoadModuleFromFunction functionality r=keeler,nss-reviewers Differential Revision: https://phabricator.services.mozilla.com/D230392
automation/abi-check/expected-report-libnss3.so.txt
gtests/pk11_gtest/pk11_module_unittest.cc
lib/nss/nss.def
lib/pk11wrap/pk11load.c
lib/pk11wrap/pk11pars.c
lib/pk11wrap/secmod.h
lib/pk11wrap/secmodi.h
--- 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 */