michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: /* michael@0: * The following code handles the storage of PKCS 11 modules used by the michael@0: * NSS. This file is written to abstract away how the modules are michael@0: * stored so we can deside that later. michael@0: */ michael@0: #include "sftkdb.h" michael@0: #include "sftkdbti.h" michael@0: #include "sdb.h" michael@0: #include "prsystem.h" michael@0: #include "prprf.h" michael@0: #include "prenv.h" michael@0: #include "lgglue.h" michael@0: #include "secerr.h" michael@0: #include "softoken.h" michael@0: michael@0: static LGOpenFunc legacy_glue_open = NULL; michael@0: static LGReadSecmodFunc legacy_glue_readSecmod = NULL; michael@0: static LGReleaseSecmodFunc legacy_glue_releaseSecmod = NULL; michael@0: static LGDeleteSecmodFunc legacy_glue_deleteSecmod = NULL; michael@0: static LGAddSecmodFunc legacy_glue_addSecmod = NULL; michael@0: static LGShutdownFunc legacy_glue_shutdown = NULL; michael@0: michael@0: /* michael@0: * The following 3 functions duplicate the work done by bl_LoadLibrary. michael@0: * We should make bl_LoadLibrary a global and replace the call to michael@0: * sftkdb_LoadLibrary(const char *libname) with it. michael@0: */ michael@0: #ifdef XP_UNIX michael@0: #include michael@0: #define LG_MAX_LINKS 20 michael@0: static char * michael@0: sftkdb_resolvePath(const char *orig) michael@0: { michael@0: int count = 0; michael@0: int len =0; michael@0: int ret = -1; michael@0: char *resolved = NULL; michael@0: char *source = NULL; michael@0: michael@0: len = 1025; /* MAX PATH +1*/ michael@0: if (strlen(orig)+1 > len) { michael@0: /* PATH TOO LONG */ michael@0: return NULL; michael@0: } michael@0: resolved = PORT_Alloc(len); michael@0: if (!resolved) { michael@0: return NULL; michael@0: } michael@0: source = PORT_Alloc(len); michael@0: if (!source) { michael@0: goto loser; michael@0: } michael@0: PORT_Strcpy(source, orig); michael@0: /* Walk down all the links */ michael@0: while ( count++ < LG_MAX_LINKS) { michael@0: char *tmp; michael@0: /* swap our previous sorce out with resolved */ michael@0: /* read it */ michael@0: ret = readlink(source, resolved, len-1); michael@0: if (ret < 0) { michael@0: break; michael@0: } michael@0: resolved[ret] = 0; michael@0: tmp = source; source = resolved; resolved = tmp; michael@0: } michael@0: if (count > 1) { michael@0: ret = 0; michael@0: } michael@0: loser: michael@0: if (resolved) { michael@0: PORT_Free(resolved); michael@0: } michael@0: if (ret < 0) { michael@0: if (source) { michael@0: PORT_Free(source); michael@0: source = NULL; michael@0: } michael@0: } michael@0: return source; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: static PRLibrary * michael@0: sftkdb_LoadFromPath(const char *path, const char *libname) michael@0: { michael@0: char *c; michael@0: int pathLen, nameLen, fullPathLen; michael@0: char *fullPathName = NULL; michael@0: PRLibSpec libSpec; michael@0: PRLibrary *lib = NULL; michael@0: michael@0: michael@0: /* strip of our parent's library name */ michael@0: c = strrchr(path, PR_GetDirectorySeparator()); michael@0: if (!c) { michael@0: return NULL; /* invalid path */ michael@0: } michael@0: pathLen = (c-path)+1; michael@0: nameLen = strlen(libname); michael@0: fullPathLen = pathLen + nameLen +1; michael@0: fullPathName = (char *)PORT_Alloc(fullPathLen); michael@0: if (fullPathName == NULL) { michael@0: return NULL; /* memory allocation error */ michael@0: } michael@0: PORT_Memcpy(fullPathName, path, pathLen); michael@0: PORT_Memcpy(fullPathName+pathLen, libname, nameLen); michael@0: fullPathName[fullPathLen-1] = 0; michael@0: michael@0: libSpec.type = PR_LibSpec_Pathname; michael@0: libSpec.value.pathname = fullPathName; michael@0: lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_LOCAL); michael@0: PORT_Free(fullPathName); michael@0: return lib; michael@0: } michael@0: michael@0: michael@0: static PRLibrary * michael@0: sftkdb_LoadLibrary(const char *libname) michael@0: { michael@0: PRLibrary *lib = NULL; michael@0: PRFuncPtr fn_addr; michael@0: char *parentLibPath = NULL; michael@0: michael@0: fn_addr = (PRFuncPtr) &sftkdb_LoadLibrary; michael@0: parentLibPath = PR_GetLibraryFilePathname(SOFTOKEN_LIB_NAME, fn_addr); michael@0: michael@0: if (!parentLibPath) { michael@0: goto done; michael@0: } michael@0: michael@0: lib = sftkdb_LoadFromPath(parentLibPath, libname); michael@0: #ifdef XP_UNIX michael@0: /* handle symbolic link case */ michael@0: if (!lib) { michael@0: char *trueParentLibPath = sftkdb_resolvePath(parentLibPath); michael@0: if (!trueParentLibPath) { michael@0: goto done; michael@0: } michael@0: lib = sftkdb_LoadFromPath(trueParentLibPath, libname); michael@0: PORT_Free(trueParentLibPath); michael@0: } michael@0: #endif michael@0: michael@0: done: michael@0: if (parentLibPath) { michael@0: PORT_Free(parentLibPath); michael@0: } michael@0: michael@0: /* still couldn't load it, try the generic path */ michael@0: if (!lib) { michael@0: PRLibSpec libSpec; michael@0: libSpec.type = PR_LibSpec_Pathname; michael@0: libSpec.value.pathname = libname; michael@0: lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_LOCAL); michael@0: } michael@0: michael@0: return lib; michael@0: } michael@0: michael@0: /* michael@0: * stub files for legacy db's to be able to encrypt and decrypt michael@0: * various keys and attributes. michael@0: */ michael@0: static SECStatus michael@0: sftkdb_encrypt_stub(PLArenaPool *arena, SDB *sdb, SECItem *plainText, michael@0: SECItem **cipherText) michael@0: { michael@0: SFTKDBHandle *handle = sdb->app_private; michael@0: SECStatus rv; michael@0: michael@0: if (handle == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* if we aren't the key handle, try the other handle */ michael@0: if (handle->type != SFTK_KEYDB_TYPE) { michael@0: handle = handle->peerDB; michael@0: } michael@0: michael@0: /* not a key handle */ michael@0: if (handle == NULL || handle->passwordLock == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: PZ_Lock(handle->passwordLock); michael@0: if (handle->passwordKey.data == NULL) { michael@0: PZ_Unlock(handle->passwordLock); michael@0: /* PORT_SetError */ michael@0: return SECFailure; michael@0: } michael@0: michael@0: rv = sftkdb_EncryptAttribute(arena, michael@0: handle->newKey?handle->newKey:&handle->passwordKey, michael@0: plainText, cipherText); michael@0: PZ_Unlock(handle->passwordLock); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * stub files for legacy db's to be able to encrypt and decrypt michael@0: * various keys and attributes. michael@0: */ michael@0: static SECStatus michael@0: sftkdb_decrypt_stub(SDB *sdb, SECItem *cipherText, SECItem **plainText) michael@0: { michael@0: SFTKDBHandle *handle = sdb->app_private; michael@0: SECStatus rv; michael@0: SECItem *oldKey = NULL; michael@0: michael@0: if (handle == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* if we aren't th handle, try the other handle */ michael@0: oldKey = handle->oldKey; michael@0: if (handle->type != SFTK_KEYDB_TYPE) { michael@0: handle = handle->peerDB; michael@0: } michael@0: michael@0: /* not a key handle */ michael@0: if (handle == NULL || handle->passwordLock == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: PZ_Lock(handle->passwordLock); michael@0: if (handle->passwordKey.data == NULL) { michael@0: PZ_Unlock(handle->passwordLock); michael@0: /* PORT_SetError */ michael@0: return SECFailure; michael@0: } michael@0: rv = sftkdb_DecryptAttribute( oldKey ? oldKey : &handle->passwordKey, michael@0: cipherText, plainText); michael@0: PZ_Unlock(handle->passwordLock); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static const char *LEGACY_LIB_NAME = michael@0: SHLIB_PREFIX"nssdbm"SHLIB_VERSION"."SHLIB_SUFFIX; michael@0: /* michael@0: * 2 bools to tell us if we've check the legacy library successfully or michael@0: * not. Initialize on startup to false by the C BSS segment; michael@0: */ michael@0: static PRBool legacy_glue_libCheckFailed; /* set if we failed the check */ michael@0: static PRBool legacy_glue_libCheckSucceeded; /* set if we passed the check */ michael@0: static PRLibrary *legacy_glue_lib = NULL; michael@0: static SECStatus michael@0: sftkdbLoad_Legacy(PRBool isFIPS) michael@0: { michael@0: PRLibrary *lib = NULL; michael@0: LGSetCryptFunc setCryptFunction = NULL; michael@0: michael@0: if (legacy_glue_lib) { michael@0: /* this check is necessary because it's possible we loaded the michael@0: * legacydb to read secmod.db, which told us whether we were in michael@0: * FIPS mode or not. */ michael@0: if (isFIPS && !legacy_glue_libCheckSucceeded) { michael@0: if (legacy_glue_libCheckFailed || michael@0: !BLAPI_SHVerify(LEGACY_LIB_NAME,(PRFuncPtr)legacy_glue_open)) { michael@0: legacy_glue_libCheckFailed = PR_TRUE; michael@0: /* don't clobber legacy glue to avoid race. just let it michael@0: * get cleared in shutdown */ michael@0: return SECFailure; michael@0: } michael@0: legacy_glue_libCheckSucceeded = PR_TRUE; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: lib = sftkdb_LoadLibrary(LEGACY_LIB_NAME); michael@0: if (lib == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: legacy_glue_open = (LGOpenFunc)PR_FindFunctionSymbol(lib, "legacy_Open"); michael@0: legacy_glue_readSecmod = (LGReadSecmodFunc) PR_FindFunctionSymbol(lib, michael@0: "legacy_ReadSecmodDB"); michael@0: legacy_glue_releaseSecmod = (LGReleaseSecmodFunc) PR_FindFunctionSymbol(lib, michael@0: "legacy_ReleaseSecmodDBData"); michael@0: legacy_glue_deleteSecmod = (LGDeleteSecmodFunc) PR_FindFunctionSymbol(lib, michael@0: "legacy_DeleteSecmodDB"); michael@0: legacy_glue_addSecmod = (LGAddSecmodFunc)PR_FindFunctionSymbol(lib, michael@0: "legacy_AddSecmodDB"); michael@0: legacy_glue_shutdown = (LGShutdownFunc) PR_FindFunctionSymbol(lib, michael@0: "legacy_Shutdown"); michael@0: setCryptFunction = (LGSetCryptFunc) PR_FindFunctionSymbol(lib, michael@0: "legacy_SetCryptFunctions"); michael@0: michael@0: if (!legacy_glue_open || !legacy_glue_readSecmod || michael@0: !legacy_glue_releaseSecmod || !legacy_glue_deleteSecmod || michael@0: !legacy_glue_addSecmod || !setCryptFunction) { michael@0: PR_UnloadLibrary(lib); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* verify the loaded library if we are in FIPS mode */ michael@0: if (isFIPS) { michael@0: if (!BLAPI_SHVerify(LEGACY_LIB_NAME,(PRFuncPtr)legacy_glue_open)) { michael@0: PR_UnloadLibrary(lib); michael@0: return SECFailure; michael@0: } michael@0: legacy_glue_libCheckSucceeded = PR_TRUE; michael@0: } michael@0: michael@0: setCryptFunction(sftkdb_encrypt_stub,sftkdb_decrypt_stub); michael@0: legacy_glue_lib = lib; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: CK_RV michael@0: sftkdbCall_open(const char *dir, const char *certPrefix, const char *keyPrefix, michael@0: int certVersion, int keyVersion, int flags, PRBool isFIPS, michael@0: SDB **certDB, SDB **keyDB) michael@0: { michael@0: SECStatus rv; michael@0: michael@0: rv = sftkdbLoad_Legacy(isFIPS); michael@0: if (rv != SECSuccess) { michael@0: return CKR_GENERAL_ERROR; michael@0: } michael@0: if (!legacy_glue_open) { michael@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); michael@0: return SECFailure; michael@0: } michael@0: return (*legacy_glue_open)(dir, certPrefix, keyPrefix, michael@0: certVersion, keyVersion, michael@0: flags, certDB, keyDB); michael@0: } michael@0: michael@0: char ** michael@0: sftkdbCall_ReadSecmodDB(const char *appName, const char *filename, michael@0: const char *dbname, char *params, PRBool rw) michael@0: { michael@0: SECStatus rv; michael@0: michael@0: rv = sftkdbLoad_Legacy(PR_FALSE); michael@0: if (rv != SECSuccess) { michael@0: return NULL; michael@0: } michael@0: if (!legacy_glue_readSecmod) { michael@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); michael@0: return NULL; michael@0: } michael@0: return (*legacy_glue_readSecmod)(appName, filename, dbname, params, rw); michael@0: } michael@0: michael@0: SECStatus michael@0: sftkdbCall_ReleaseSecmodDBData(const char *appName, michael@0: const char *filename, const char *dbname, michael@0: char **moduleSpecList, PRBool rw) michael@0: { michael@0: SECStatus rv; michael@0: michael@0: rv = sftkdbLoad_Legacy(PR_FALSE); michael@0: if (rv != SECSuccess) { michael@0: return rv; michael@0: } michael@0: if (!legacy_glue_releaseSecmod) { michael@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); michael@0: return SECFailure; michael@0: } michael@0: return (*legacy_glue_releaseSecmod)(appName, filename, dbname, michael@0: moduleSpecList, rw); michael@0: } michael@0: michael@0: SECStatus michael@0: sftkdbCall_DeleteSecmodDB(const char *appName, michael@0: const char *filename, const char *dbname, michael@0: char *args, PRBool rw) michael@0: { michael@0: SECStatus rv; michael@0: michael@0: rv = sftkdbLoad_Legacy(PR_FALSE); michael@0: if (rv != SECSuccess) { michael@0: return rv; michael@0: } michael@0: if (!legacy_glue_deleteSecmod) { michael@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); michael@0: return SECFailure; michael@0: } michael@0: return (*legacy_glue_deleteSecmod)(appName, filename, dbname, args, rw); michael@0: } michael@0: michael@0: SECStatus michael@0: sftkdbCall_AddSecmodDB(const char *appName, michael@0: const char *filename, const char *dbname, michael@0: char *module, PRBool rw) michael@0: { michael@0: SECStatus rv; michael@0: michael@0: rv = sftkdbLoad_Legacy(PR_FALSE); michael@0: if (rv != SECSuccess) { michael@0: return rv; michael@0: } michael@0: if (!legacy_glue_addSecmod) { michael@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); michael@0: return SECFailure; michael@0: } michael@0: return (*legacy_glue_addSecmod)(appName, filename, dbname, module, rw); michael@0: } michael@0: michael@0: CK_RV michael@0: sftkdbCall_Shutdown(void) michael@0: { michael@0: CK_RV crv = CKR_OK; michael@0: char *disableUnload = NULL; michael@0: if (!legacy_glue_lib) { michael@0: return CKR_OK; michael@0: } michael@0: if (legacy_glue_shutdown) { michael@0: #ifdef NO_FORK_CHECK michael@0: PRBool parentForkedAfterC_Initialize = PR_FALSE; michael@0: #endif michael@0: crv = (*legacy_glue_shutdown)(parentForkedAfterC_Initialize); michael@0: } michael@0: disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD"); michael@0: if (!disableUnload) { michael@0: PR_UnloadLibrary(legacy_glue_lib); michael@0: } michael@0: legacy_glue_lib = NULL; michael@0: legacy_glue_open = NULL; michael@0: legacy_glue_readSecmod = NULL; michael@0: legacy_glue_releaseSecmod = NULL; michael@0: legacy_glue_deleteSecmod = NULL; michael@0: legacy_glue_addSecmod = NULL; michael@0: legacy_glue_libCheckFailed = PR_FALSE; michael@0: legacy_glue_libCheckSucceeded = PR_FALSE; michael@0: return crv; michael@0: } michael@0: michael@0: