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 handles the loading, unloading and management of michael@0: * various PCKS #11 modules michael@0: */ michael@0: michael@0: #include michael@0: #include "pkcs11.h" michael@0: #include "seccomon.h" michael@0: #include "secmod.h" michael@0: #include "secmodi.h" michael@0: #include "secmodti.h" michael@0: #include "pki3hack.h" michael@0: #include "secerr.h" michael@0: michael@0: #include "utilpars.h" michael@0: michael@0: /* create a new module */ michael@0: static SECMODModule * michael@0: secmod_NewModule(void) michael@0: { michael@0: SECMODModule *newMod; michael@0: PLArenaPool *arena; michael@0: michael@0: michael@0: /* create an arena in which dllName and commonName can be michael@0: * allocated. michael@0: */ michael@0: arena = PORT_NewArena(512); michael@0: if (arena == NULL) { michael@0: return NULL; michael@0: } michael@0: michael@0: newMod = (SECMODModule *)PORT_ArenaAlloc(arena,sizeof (SECMODModule)); michael@0: if (newMod == NULL) { michael@0: PORT_FreeArena(arena,PR_FALSE); michael@0: return NULL; michael@0: } michael@0: michael@0: /* michael@0: * initialize of the fields of the module michael@0: */ michael@0: newMod->arena = arena; michael@0: newMod->internal = PR_FALSE; michael@0: newMod->loaded = PR_FALSE; michael@0: newMod->isFIPS = PR_FALSE; michael@0: newMod->dllName = NULL; michael@0: newMod->commonName = NULL; michael@0: newMod->library = NULL; michael@0: newMod->functionList = NULL; michael@0: newMod->slotCount = 0; michael@0: newMod->slots = NULL; michael@0: newMod->slotInfo = NULL; michael@0: newMod->slotInfoCount = 0; michael@0: newMod->refCount = 1; michael@0: newMod->ssl[0] = 0; michael@0: newMod->ssl[1] = 0; michael@0: newMod->libraryParams = NULL; michael@0: newMod->moduleDBFunc = NULL; michael@0: newMod->parent = NULL; michael@0: newMod->isCritical = PR_FALSE; michael@0: newMod->isModuleDB = PR_FALSE; michael@0: newMod->moduleDBOnly = PR_FALSE; michael@0: newMod->trustOrder = 0; michael@0: newMod->cipherOrder = 0; michael@0: newMod->evControlMask = 0; michael@0: newMod->refLock = PZ_NewLock(nssILockRefLock); michael@0: if (newMod->refLock == NULL) { michael@0: PORT_FreeArena(arena,PR_FALSE); michael@0: return NULL; michael@0: } michael@0: return newMod; michael@0: michael@0: } michael@0: michael@0: /* private flags for isModuleDB (field in SECMODModule). */ michael@0: /* The meaing of these flags is as follows: michael@0: * michael@0: * SECMOD_FLAG_MODULE_DB_IS_MODULE_DB - This is a module that accesses the michael@0: * database of other modules to load. Module DBs are loadable modules that michael@0: * tells NSS which PKCS #11 modules to load and when. These module DBs are michael@0: * chainable. That is, one module DB can load another one. NSS system init michael@0: * design takes advantage of this feature. In system NSS, a fixed system michael@0: * module DB loads the system defined libraries, then chains out to the michael@0: * traditional module DBs to load any system or user configured modules michael@0: * (like smart cards). This bit is the same as the already existing meaning michael@0: * of isModuleDB = PR_TRUE. None of the other module db flags should be set michael@0: * if this flag isn't on. michael@0: * michael@0: * SECMOD_FLAG_MODULE_DB_SKIP_FIRST - This flag tells NSS to skip the first michael@0: * PKCS #11 module presented by a module DB. This allows the OS to load a michael@0: * softoken from the system module, then ask the existing module DB code to michael@0: * load the other PKCS #11 modules in that module DB (skipping it's request michael@0: * to load softoken). This gives the system init finer control over the michael@0: * configuration of that softoken module. michael@0: * michael@0: * SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB - This flag allows system init to mark a michael@0: * different module DB as the 'default' module DB (the one in which michael@0: * 'Add module' changes will go). Without this flag NSS takes the first michael@0: * module as the default Module DB, but in system NSS, that first module michael@0: * is the system module, which is likely read only (at least to the user). michael@0: * This allows system NSS to delegate those changes to the user's module DB, michael@0: * preserving the user's ability to load new PKCS #11 modules (which only michael@0: * affect him), from existing applications like Firefox. michael@0: */ michael@0: #define SECMOD_FLAG_MODULE_DB_IS_MODULE_DB 0x01 /* must be set if any of the michael@0: *other flags are set */ michael@0: #define SECMOD_FLAG_MODULE_DB_SKIP_FIRST 0x02 michael@0: #define SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB 0x04 michael@0: michael@0: michael@0: /* private flags for internal (field in SECMODModule). */ michael@0: /* The meaing of these flags is as follows: michael@0: * michael@0: * SECMOD_FLAG_INTERNAL_IS_INTERNAL - This is a marks the the module is michael@0: * the internal module (that is, softoken). This bit is the same as the michael@0: * already existing meaning of internal = PR_TRUE. None of the other michael@0: * internal flags should be set if this flag isn't on. michael@0: * michael@0: * SECMOD_FLAG_MODULE_INTERNAL_KEY_SLOT - This flag allows system init to mark michael@0: * a different slot returned byt PK11_GetInternalKeySlot(). The 'primary' michael@0: * slot defined by this module will be the new internal key slot. michael@0: */ michael@0: #define SECMOD_FLAG_INTERNAL_IS_INTERNAL 0x01 /* must be set if any of michael@0: *the other flags are set */ michael@0: #define SECMOD_FLAG_INTERNAL_KEY_SLOT 0x02 michael@0: michael@0: /* michael@0: * for 3.4 we continue to use the old SECMODModule structure michael@0: */ michael@0: SECMODModule * michael@0: SECMOD_CreateModule(const char *library, const char *moduleName, michael@0: const char *parameters, const char *nss) michael@0: { michael@0: SECMODModule *mod = secmod_NewModule(); michael@0: char *slotParams,*ciphers; michael@0: /* pk11pars.h still does not have const char * interfaces */ michael@0: char *nssc = (char *)nss; michael@0: if (mod == NULL) return NULL; michael@0: michael@0: mod->commonName = PORT_ArenaStrdup(mod->arena,moduleName ? moduleName : ""); michael@0: if (library) { michael@0: mod->dllName = PORT_ArenaStrdup(mod->arena,library); michael@0: } michael@0: /* new field */ michael@0: if (parameters) { michael@0: mod->libraryParams = PORT_ArenaStrdup(mod->arena,parameters); michael@0: } michael@0: mod->internal = NSSUTIL_ArgHasFlag("flags","internal",nssc); michael@0: mod->isFIPS = NSSUTIL_ArgHasFlag("flags","FIPS",nssc); michael@0: mod->isCritical = NSSUTIL_ArgHasFlag("flags","critical",nssc); michael@0: slotParams = NSSUTIL_ArgGetParamValue("slotParams",nssc); michael@0: mod->slotInfo = NSSUTIL_ArgParseSlotInfo(mod->arena,slotParams, michael@0: &mod->slotInfoCount); michael@0: if (slotParams) PORT_Free(slotParams); michael@0: /* new field */ michael@0: mod->trustOrder = NSSUTIL_ArgReadLong("trustOrder",nssc, michael@0: NSSUTIL_DEFAULT_TRUST_ORDER,NULL); michael@0: /* new field */ michael@0: mod->cipherOrder = NSSUTIL_ArgReadLong("cipherOrder",nssc, michael@0: NSSUTIL_DEFAULT_CIPHER_ORDER,NULL); michael@0: /* new field */ michael@0: mod->isModuleDB = NSSUTIL_ArgHasFlag("flags","moduleDB",nssc); michael@0: mod->moduleDBOnly = NSSUTIL_ArgHasFlag("flags","moduleDBOnly",nssc); michael@0: if (mod->moduleDBOnly) mod->isModuleDB = PR_TRUE; michael@0: michael@0: /* we need more bits, but we also want to preserve binary compatibility michael@0: * so we overload the isModuleDB PRBool with additional flags. michael@0: * These flags are only valid if mod->isModuleDB is already set. michael@0: * NOTE: this depends on the fact that PRBool is at least a char on michael@0: * all platforms. These flags are only valid if moduleDB is set, so michael@0: * code checking if (mod->isModuleDB) will continue to work correctly. */ michael@0: if (mod->isModuleDB) { michael@0: char flags = SECMOD_FLAG_MODULE_DB_IS_MODULE_DB; michael@0: if (NSSUTIL_ArgHasFlag("flags","skipFirst",nssc)) { michael@0: flags |= SECMOD_FLAG_MODULE_DB_SKIP_FIRST; michael@0: } michael@0: if (NSSUTIL_ArgHasFlag("flags","defaultModDB",nssc)) { michael@0: flags |= SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB; michael@0: } michael@0: /* additional moduleDB flags could be added here in the future */ michael@0: mod->isModuleDB = (PRBool) flags; michael@0: } michael@0: michael@0: if (mod->internal) { michael@0: char flags = SECMOD_FLAG_INTERNAL_IS_INTERNAL; michael@0: michael@0: if (NSSUTIL_ArgHasFlag("flags", "internalKeySlot", nssc)) { michael@0: flags |= SECMOD_FLAG_INTERNAL_KEY_SLOT; michael@0: } michael@0: mod->internal = (PRBool) flags; michael@0: } michael@0: michael@0: ciphers = NSSUTIL_ArgGetParamValue("ciphers",nssc); michael@0: NSSUTIL_ArgParseCipherFlags(&mod->ssl[0],ciphers); michael@0: if (ciphers) PORT_Free(ciphers); michael@0: michael@0: secmod_PrivateModuleCount++; michael@0: michael@0: return mod; michael@0: } michael@0: michael@0: PRBool michael@0: SECMOD_GetSkipFirstFlag(SECMODModule *mod) michael@0: { michael@0: char flags = (char) mod->isModuleDB; michael@0: michael@0: return (flags & SECMOD_FLAG_MODULE_DB_SKIP_FIRST) ? PR_TRUE : PR_FALSE; michael@0: } michael@0: michael@0: PRBool michael@0: SECMOD_GetDefaultModDBFlag(SECMODModule *mod) michael@0: { michael@0: char flags = (char) mod->isModuleDB; michael@0: michael@0: return (flags & SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB) ? PR_TRUE : PR_FALSE; michael@0: } michael@0: michael@0: PRBool michael@0: secmod_IsInternalKeySlot(SECMODModule *mod) michael@0: { michael@0: char flags = (char) mod->internal; michael@0: michael@0: return (flags & SECMOD_FLAG_INTERNAL_KEY_SLOT) ? PR_TRUE : PR_FALSE; michael@0: } michael@0: michael@0: void michael@0: secmod_SetInternalKeySlotFlag(SECMODModule *mod, PRBool val) michael@0: { michael@0: char flags = (char) mod->internal; michael@0: michael@0: if (val) { michael@0: flags |= SECMOD_FLAG_INTERNAL_KEY_SLOT; michael@0: } else { michael@0: flags &= ~SECMOD_FLAG_INTERNAL_KEY_SLOT; michael@0: } michael@0: mod->internal = flags; michael@0: } michael@0: michael@0: /* michael@0: * copy desc and value into target. Target is known to be big enough to michael@0: * hold desc +2 +value, which is good because the result of this will be michael@0: * *desc"*value". We may, however, have to add some escapes for special michael@0: * characters imbedded into value (rare). This string potentially comes from michael@0: * a user, so we don't want the user overflowing the target buffer by using michael@0: * excessive escapes. To prevent this we count the escapes we need to add and michael@0: * try to expand the buffer with Realloc. michael@0: */ michael@0: static char * michael@0: secmod_doDescCopy(char *target, int *targetLen, const char *desc, michael@0: int descLen, char *value) michael@0: { michael@0: int diff, esc_len; michael@0: michael@0: esc_len = NSSUTIL_EscapeSize(value, '\"') - 1; michael@0: diff = esc_len - strlen(value); michael@0: if (diff > 0) { michael@0: /* we need to escape... expand newSpecPtr as well to make sure michael@0: * we don't overflow it */ michael@0: char *newPtr = PORT_Realloc(target, *targetLen * diff); michael@0: if (!newPtr) { michael@0: return target; /* not enough space, just drop the whole copy */ michael@0: } michael@0: *targetLen += diff; michael@0: target = newPtr; michael@0: value = NSSUTIL_Escape(value, '\"'); michael@0: if (value == NULL) { michael@0: return target; /* couldn't escape value, just drop the copy */ michael@0: } michael@0: } michael@0: PORT_Memcpy(target, desc, descLen); michael@0: target += descLen; michael@0: *target++='\"'; michael@0: PORT_Memcpy(target, value, esc_len); michael@0: target += esc_len; michael@0: *target++='\"'; michael@0: if (diff > 0) { michael@0: PORT_Free(value); michael@0: } michael@0: return target; michael@0: } michael@0: michael@0: #define SECMOD_SPEC_COPY(new, start, end) \ michael@0: if (end > start) { \ michael@0: int _cnt = end - start; \ michael@0: PORT_Memcpy(new, start, _cnt); \ michael@0: new += _cnt; \ michael@0: } michael@0: #define SECMOD_TOKEN_DESCRIPTION "tokenDescription=" michael@0: #define SECMOD_SLOT_DESCRIPTION "slotDescription=" michael@0: michael@0: michael@0: /* michael@0: * Find any tokens= values in the module spec. michael@0: * Always return a new spec which does not have any tokens= arguments. michael@0: * If tokens= arguments are found, Split the the various tokens defined into michael@0: * an array of child specs to return. michael@0: * michael@0: * Caller is responsible for freeing the child spec and the new token michael@0: * spec. michael@0: */ michael@0: char * michael@0: secmod_ParseModuleSpecForTokens(PRBool convert, PRBool isFIPS, michael@0: char *moduleSpec, char ***children, michael@0: CK_SLOT_ID **ids) michael@0: { michael@0: int newSpecLen = PORT_Strlen(moduleSpec)+2; michael@0: char *newSpec = PORT_Alloc(newSpecLen); michael@0: char *newSpecPtr = newSpec; michael@0: char *modulePrev = moduleSpec; michael@0: char *target = NULL; michael@0: char *tmp = NULL; michael@0: char **childArray = NULL; michael@0: char *tokenIndex; michael@0: CK_SLOT_ID *idArray = NULL; michael@0: int tokenCount = 0; michael@0: int i; michael@0: michael@0: if (newSpec == NULL) { michael@0: return NULL; michael@0: } michael@0: michael@0: *children = NULL; michael@0: if (ids) { michael@0: *ids = NULL; michael@0: } michael@0: moduleSpec = NSSUTIL_ArgStrip(moduleSpec); michael@0: SECMOD_SPEC_COPY(newSpecPtr, modulePrev, moduleSpec); michael@0: michael@0: /* Notes on 'convert' and 'isFIPS' flags: The base parameters for opening michael@0: * a new softoken module takes the following parameters to name the michael@0: * various tokens: michael@0: * michael@0: * cryptoTokenDescription: name of the non-fips crypto token. michael@0: * cryptoSlotDescription: name of the non-fips crypto slot. michael@0: * dbTokenDescription: name of the non-fips db token. michael@0: * dbSlotDescription: name of the non-fips db slot. michael@0: * FIPSTokenDescription: name of the fips db/crypto token. michael@0: * FIPSSlotDescription: name of the fips db/crypto slot. michael@0: * michael@0: * if we are opening a new slot, we need to have the following michael@0: * parameters: michael@0: * tokenDescription: name of the token. michael@0: * slotDescription: name of the slot. michael@0: * michael@0: * michael@0: * The convert flag tells us to drop the unnecessary *TokenDescription michael@0: * and *SlotDescription arguments and convert the appropriate pair michael@0: * (either db or FIPS based on the isFIPS flag) to tokenDescription and michael@0: * slotDescription). michael@0: */ michael@0: /* michael@0: * walk down the list. if we find a tokens= argument, save it, michael@0: * otherise copy the argument. michael@0: */ michael@0: while (*moduleSpec) { michael@0: int next; michael@0: modulePrev = moduleSpec; michael@0: NSSUTIL_HANDLE_STRING_ARG(moduleSpec, target, "tokens=", michael@0: modulePrev = moduleSpec; /* skip copying */ ) michael@0: NSSUTIL_HANDLE_STRING_ARG(moduleSpec, tmp, "cryptoTokenDescription=", michael@0: if (convert) { modulePrev = moduleSpec; } ); michael@0: NSSUTIL_HANDLE_STRING_ARG(moduleSpec, tmp, "cryptoSlotDescription=", michael@0: if (convert) { modulePrev = moduleSpec; } ); michael@0: NSSUTIL_HANDLE_STRING_ARG(moduleSpec, tmp, "dbTokenDescription=", michael@0: if (convert) { michael@0: modulePrev = moduleSpec; michael@0: if (!isFIPS) { michael@0: newSpecPtr = secmod_doDescCopy(newSpecPtr, michael@0: &newSpecLen, SECMOD_TOKEN_DESCRIPTION, michael@0: sizeof(SECMOD_TOKEN_DESCRIPTION)-1, tmp); michael@0: } michael@0: }); michael@0: NSSUTIL_HANDLE_STRING_ARG(moduleSpec, tmp, "dbSlotDescription=", michael@0: if (convert) { michael@0: modulePrev = moduleSpec; /* skip copying */ michael@0: if (!isFIPS) { michael@0: newSpecPtr = secmod_doDescCopy(newSpecPtr, michael@0: &newSpecLen, SECMOD_SLOT_DESCRIPTION, michael@0: sizeof(SECMOD_SLOT_DESCRIPTION)-1, tmp); michael@0: } michael@0: } ); michael@0: NSSUTIL_HANDLE_STRING_ARG(moduleSpec, tmp, "FIPSTokenDescription=", michael@0: if (convert) { michael@0: modulePrev = moduleSpec; /* skip copying */ michael@0: if (isFIPS) { michael@0: newSpecPtr = secmod_doDescCopy(newSpecPtr, michael@0: &newSpecLen, SECMOD_TOKEN_DESCRIPTION, michael@0: sizeof(SECMOD_TOKEN_DESCRIPTION)-1, tmp); michael@0: } michael@0: } ); michael@0: NSSUTIL_HANDLE_STRING_ARG(moduleSpec, tmp, "FIPSSlotDescription=", michael@0: if (convert) { michael@0: modulePrev = moduleSpec; /* skip copying */ michael@0: if (isFIPS) { michael@0: newSpecPtr = secmod_doDescCopy(newSpecPtr, michael@0: &newSpecLen, SECMOD_SLOT_DESCRIPTION, michael@0: sizeof(SECMOD_SLOT_DESCRIPTION)-1, tmp); michael@0: } michael@0: } ); michael@0: NSSUTIL_HANDLE_FINAL_ARG(moduleSpec) michael@0: SECMOD_SPEC_COPY(newSpecPtr, modulePrev, moduleSpec); michael@0: } michael@0: if (tmp) { michael@0: PORT_Free(tmp); michael@0: tmp = NULL; michael@0: } michael@0: *newSpecPtr = 0; michael@0: michael@0: /* no target found, return the newSpec */ michael@0: if (target == NULL) { michael@0: return newSpec; michael@0: } michael@0: michael@0: /* now build the child array from target */ michael@0: /*first count them */ michael@0: for (tokenIndex = NSSUTIL_ArgStrip(target); *tokenIndex; michael@0: tokenIndex = NSSUTIL_ArgStrip(NSSUTIL_ArgSkipParameter(tokenIndex))) { michael@0: tokenCount++; michael@0: } michael@0: michael@0: childArray = PORT_NewArray(char *, tokenCount+1); michael@0: if (childArray == NULL) { michael@0: /* just return the spec as is then */ michael@0: PORT_Free(target); michael@0: return newSpec; michael@0: } michael@0: if (ids) { michael@0: idArray = PORT_NewArray(CK_SLOT_ID, tokenCount+1); michael@0: if (idArray == NULL) { michael@0: PORT_Free(childArray); michael@0: PORT_Free(target); michael@0: return newSpec; michael@0: } michael@0: } michael@0: michael@0: /* now fill them in */ michael@0: for (tokenIndex = NSSUTIL_ArgStrip(target), i=0 ; michael@0: *tokenIndex && (i < tokenCount); michael@0: tokenIndex=NSSUTIL_ArgStrip(tokenIndex)) { michael@0: int next; michael@0: char *name = NSSUTIL_ArgGetLabel(tokenIndex, &next); michael@0: tokenIndex += next; michael@0: michael@0: if (idArray) { michael@0: idArray[i] = NSSUTIL_ArgDecodeNumber(name); michael@0: } michael@0: michael@0: PORT_Free(name); /* drop the explicit number */ michael@0: michael@0: /* if anything is left, copy the args to the child array */ michael@0: if (!NSSUTIL_ArgIsBlank(*tokenIndex)) { michael@0: childArray[i++] = NSSUTIL_ArgFetchValue(tokenIndex, &next); michael@0: tokenIndex += next; michael@0: } michael@0: } michael@0: michael@0: PORT_Free(target); michael@0: childArray[i] = 0; michael@0: if (idArray) { michael@0: idArray[i] = 0; michael@0: } michael@0: michael@0: /* return it */ michael@0: *children = childArray; michael@0: if (ids) { michael@0: *ids = idArray; michael@0: } michael@0: return newSpec; michael@0: } michael@0: michael@0: /* get the database and flags from the spec */ michael@0: static char * michael@0: secmod_getConfigDir(char *spec, char **certPrefix, char **keyPrefix, michael@0: PRBool *readOnly) michael@0: { michael@0: char * config = NULL; michael@0: michael@0: *certPrefix = NULL; michael@0: *keyPrefix = NULL; michael@0: *readOnly = NSSUTIL_ArgHasFlag("flags","readOnly",spec); michael@0: michael@0: spec = NSSUTIL_ArgStrip(spec); michael@0: while (*spec) { michael@0: int next; michael@0: NSSUTIL_HANDLE_STRING_ARG(spec, config, "configdir=", ;) michael@0: NSSUTIL_HANDLE_STRING_ARG(spec, *certPrefix, "certPrefix=", ;) michael@0: NSSUTIL_HANDLE_STRING_ARG(spec, *keyPrefix, "keyPrefix=", ;) michael@0: NSSUTIL_HANDLE_FINAL_ARG(spec) michael@0: } michael@0: return config; michael@0: } michael@0: michael@0: struct SECMODConfigListStr { michael@0: char *config; michael@0: char *certPrefix; michael@0: char *keyPrefix; michael@0: PRBool isReadOnly; michael@0: }; michael@0: michael@0: /* michael@0: * return an array of already openned databases from a spec list. michael@0: */ michael@0: SECMODConfigList * michael@0: secmod_GetConfigList(PRBool isFIPS, char *spec, int *count) michael@0: { michael@0: char **children; michael@0: CK_SLOT_ID *ids; michael@0: char *strippedSpec; michael@0: int childCount; michael@0: SECMODConfigList *conflist = NULL; michael@0: int i; michael@0: michael@0: strippedSpec = secmod_ParseModuleSpecForTokens(PR_TRUE, isFIPS, michael@0: spec,&children,&ids); michael@0: if (strippedSpec == NULL) { michael@0: return NULL; michael@0: } michael@0: michael@0: for (childCount=0; children && children[childCount]; childCount++) ; michael@0: *count = childCount+1; /* include strippedSpec */ michael@0: conflist = PORT_NewArray(SECMODConfigList,*count); michael@0: if (conflist == NULL) { michael@0: *count = 0; michael@0: goto loser; michael@0: } michael@0: michael@0: conflist[0].config = secmod_getConfigDir(strippedSpec, michael@0: &conflist[0].certPrefix, michael@0: &conflist[0].keyPrefix, michael@0: &conflist[0].isReadOnly); michael@0: for (i=0; i < childCount; i++) { michael@0: conflist[i+1].config = secmod_getConfigDir(children[i], michael@0: &conflist[i+1].certPrefix, michael@0: &conflist[i+1].keyPrefix, michael@0: &conflist[i+1].isReadOnly); michael@0: } michael@0: michael@0: loser: michael@0: secmod_FreeChildren(children, ids); michael@0: PORT_Free(strippedSpec); michael@0: return conflist; michael@0: } michael@0: michael@0: /* michael@0: * determine if we are trying to open an old dbm database. For this test michael@0: * RDB databases should return PR_FALSE. michael@0: */ michael@0: static PRBool michael@0: secmod_configIsDBM(char *configDir) michael@0: { michael@0: char *env; michael@0: michael@0: /* explicit dbm open */ michael@0: if (strncmp(configDir, "dbm:", 4) == 0) { michael@0: return PR_TRUE; michael@0: } michael@0: /* explicit open of a non-dbm database */ michael@0: if ((strncmp(configDir, "sql:",4) == 0) michael@0: || (strncmp(configDir, "rdb:", 4) == 0) michael@0: || (strncmp(configDir, "extern:", 7) == 0)) { michael@0: return PR_FALSE; michael@0: } michael@0: env = PR_GetEnv("NSS_DEFAULT_DB_TYPE"); michael@0: /* implicit dbm open */ michael@0: if ((env == NULL) || (strcmp(env,"dbm") == 0)) { michael@0: return PR_TRUE; michael@0: } michael@0: /* implicit non-dbm open */ michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: /* michael@0: * match two prefixes. prefix may be NULL. NULL patches '\0' michael@0: */ michael@0: static PRBool michael@0: secmod_matchPrefix(char *prefix1, char *prefix2) michael@0: { michael@0: if ((prefix1 == NULL) || (*prefix1 == 0)) { michael@0: if ((prefix2 == NULL) || (*prefix2 == 0)) { michael@0: return PR_TRUE; michael@0: } michael@0: return PR_FALSE; michael@0: } michael@0: if (strcmp(prefix1, prefix2) == 0) { michael@0: return PR_TRUE; michael@0: } michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: /* michael@0: * return true if we are requesting a database that is already openned. michael@0: */ michael@0: PRBool michael@0: secmod_MatchConfigList(char *spec, SECMODConfigList *conflist, int count) michael@0: { michael@0: char *config; michael@0: char *certPrefix; michael@0: char *keyPrefix; michael@0: PRBool isReadOnly; michael@0: PRBool ret=PR_FALSE; michael@0: int i; michael@0: michael@0: config = secmod_getConfigDir(spec, &certPrefix, &keyPrefix, &isReadOnly); michael@0: if (!config) { michael@0: ret=PR_TRUE; michael@0: goto done; michael@0: } michael@0: michael@0: /* NOTE: we dbm isn't multiple open safe. If we open the same database michael@0: * twice from two different locations, then we can corrupt our database michael@0: * (the cache will be inconsistent). Protect against this by claiming michael@0: * for comparison only that we are always openning dbm databases read only. michael@0: */ michael@0: if (secmod_configIsDBM(config)) { michael@0: isReadOnly = 1; michael@0: } michael@0: for (i=0; i < count; i++) { michael@0: if ((strcmp(config,conflist[i].config) == 0) && michael@0: secmod_matchPrefix(certPrefix, conflist[i].certPrefix) && michael@0: secmod_matchPrefix(keyPrefix, conflist[i].keyPrefix) && michael@0: /* this last test -- if we just need the DB open read only, michael@0: * than any open will suffice, but if we requested it read/write michael@0: * and it's only open read only, we need to open it again */ michael@0: (isReadOnly || !conflist[i].isReadOnly)) { michael@0: ret = PR_TRUE; michael@0: goto done; michael@0: } michael@0: } michael@0: michael@0: ret = PR_FALSE; michael@0: done: michael@0: PORT_Free(config); michael@0: PORT_Free(certPrefix); michael@0: PORT_Free(keyPrefix); michael@0: return ret; michael@0: } michael@0: michael@0: void michael@0: secmod_FreeConfigList(SECMODConfigList *conflist, int count) michael@0: { michael@0: int i; michael@0: for (i=0; i < count; i++) { michael@0: PORT_Free(conflist[i].config); michael@0: PORT_Free(conflist[i].certPrefix); michael@0: PORT_Free(conflist[i].keyPrefix); michael@0: } michael@0: PORT_Free(conflist); michael@0: } michael@0: michael@0: void michael@0: secmod_FreeChildren(char **children, CK_SLOT_ID *ids) michael@0: { michael@0: char **thisChild; michael@0: michael@0: if (!children) { michael@0: return; michael@0: } michael@0: michael@0: for (thisChild = children; thisChild && *thisChild; thisChild++ ) { michael@0: PORT_Free(*thisChild); michael@0: } michael@0: PORT_Free(children); michael@0: if (ids) { michael@0: PORT_Free(ids); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: /* michael@0: * caclulate the length of each child record: michael@0: * " 0x{id}=<{escaped_child}>" michael@0: */ michael@0: static int michael@0: secmod_getChildLength(char *child, CK_SLOT_ID id) michael@0: { michael@0: int length = NSSUTIL_DoubleEscapeSize(child, '>', ']'); michael@0: if (id == 0) { michael@0: length++; michael@0: } michael@0: while (id) { michael@0: length++; michael@0: id = id >> 4; michael@0: } michael@0: length += 6; /* {sp}0x[id]=<{child}> */ michael@0: return length; michael@0: } michael@0: michael@0: /* michael@0: * Build a child record: michael@0: * " 0x{id}=<{escaped_child}>" michael@0: */ michael@0: static SECStatus michael@0: secmod_mkTokenChild(char **next, int *length, char *child, CK_SLOT_ID id) michael@0: { michael@0: int len; michael@0: char *escSpec; michael@0: michael@0: len = PR_snprintf(*next, *length, " 0x%x=<",id); michael@0: if (len < 0) { michael@0: return SECFailure; michael@0: } michael@0: *next += len; michael@0: *length -= len; michael@0: escSpec = NSSUTIL_DoubleEscape(child, '>', ']'); michael@0: if (escSpec == NULL) { michael@0: return SECFailure; michael@0: } michael@0: if (*child && (*escSpec == 0)) { michael@0: PORT_Free(escSpec); michael@0: return SECFailure; michael@0: } michael@0: len = strlen(escSpec); michael@0: if (len+1 > *length) { michael@0: PORT_Free(escSpec); michael@0: return SECFailure; michael@0: } michael@0: PORT_Memcpy(*next,escSpec, len); michael@0: *next += len; michael@0: *length -= len; michael@0: PORT_Free(escSpec); michael@0: **next = '>'; michael@0: (*next)++; michael@0: (*length)--; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: #define TOKEN_STRING " tokens=[" michael@0: michael@0: char * michael@0: secmod_MkAppendTokensList(PLArenaPool *arena, char *oldParam, char *newToken, michael@0: CK_SLOT_ID newID, char **children, CK_SLOT_ID *ids) michael@0: { michael@0: char *rawParam = NULL; /* oldParam with tokens stripped off */ michael@0: char *newParam = NULL; /* space for the return parameter */ michael@0: char *nextParam = NULL; /* current end of the new parameter */ michael@0: char **oldChildren = NULL; michael@0: CK_SLOT_ID *oldIds = NULL; michael@0: void *mark = NULL; /* mark the arena pool in case we need michael@0: * to release it */ michael@0: int length, i, tmpLen; michael@0: SECStatus rv; michael@0: michael@0: /* first strip out and save the old tokenlist */ michael@0: rawParam = secmod_ParseModuleSpecForTokens(PR_FALSE,PR_FALSE, michael@0: oldParam,&oldChildren,&oldIds); michael@0: if (!rawParam) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* now calculate the total length of the new buffer */ michael@0: /* First the 'fixed stuff', length of rawparam (does not include a NULL), michael@0: * length of the token string (does include the NULL), closing bracket */ michael@0: length = strlen(rawParam) + sizeof(TOKEN_STRING) + 1; michael@0: /* now add then length of all the old children */ michael@0: for (i=0; oldChildren && oldChildren[i]; i++) { michael@0: length += secmod_getChildLength(oldChildren[i], oldIds[i]); michael@0: } michael@0: michael@0: /* add the new token */ michael@0: length += secmod_getChildLength(newToken, newID); michael@0: michael@0: /* and it's new children */ michael@0: for (i=0; children && children[i]; i++) { michael@0: if (ids[i] == -1) { michael@0: continue; michael@0: } michael@0: length += secmod_getChildLength(children[i], ids[i]); michael@0: } michael@0: michael@0: /* now allocate and build the string */ michael@0: mark = PORT_ArenaMark(arena); michael@0: if (!mark) { michael@0: goto loser; michael@0: } michael@0: newParam = PORT_ArenaAlloc(arena,length); michael@0: if (!newParam) { michael@0: goto loser; michael@0: } michael@0: michael@0: PORT_Strcpy(newParam, oldParam); michael@0: tmpLen = strlen(oldParam); michael@0: nextParam = newParam + tmpLen; michael@0: length -= tmpLen; michael@0: PORT_Memcpy(nextParam, TOKEN_STRING, sizeof(TOKEN_STRING)-1); michael@0: nextParam += sizeof(TOKEN_STRING)-1; michael@0: length -= sizeof(TOKEN_STRING)-1; michael@0: michael@0: for (i=0; oldChildren && oldChildren[i]; i++) { michael@0: rv = secmod_mkTokenChild(&nextParam,&length,oldChildren[i],oldIds[i]); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: rv = secmod_mkTokenChild(&nextParam, &length, newToken, newID); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: for (i=0; children && children[i]; i++) { michael@0: if (ids[i] == -1) { michael@0: continue; michael@0: } michael@0: rv = secmod_mkTokenChild(&nextParam, &length, children[i], ids[i]); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: if (length < 2) { michael@0: goto loser; michael@0: } michael@0: michael@0: *nextParam++ = ']'; michael@0: *nextParam++ = 0; michael@0: michael@0: /* we are going to return newParam now, don't release the mark */ michael@0: PORT_ArenaUnmark(arena, mark); michael@0: mark = NULL; michael@0: michael@0: loser: michael@0: if (mark) { michael@0: PORT_ArenaRelease(arena, mark); michael@0: newParam = NULL; /* if the mark is still active, michael@0: * don't return the param */ michael@0: } michael@0: if (rawParam) { michael@0: PORT_Free(rawParam); michael@0: } michael@0: if (oldChildren) { michael@0: secmod_FreeChildren(oldChildren, oldIds); michael@0: } michael@0: return newParam; michael@0: } michael@0: michael@0: static char * michael@0: secmod_mkModuleSpec(SECMODModule * module) michael@0: { michael@0: char *nss = NULL, *modSpec = NULL, **slotStrings = NULL; michael@0: int slotCount, i, si; michael@0: SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); michael@0: michael@0: /* allocate target slot info strings */ michael@0: slotCount = 0; michael@0: michael@0: SECMOD_GetReadLock(moduleLock); michael@0: if (module->slotCount) { michael@0: for (i=0; i < module->slotCount; i++) { michael@0: if (module->slots[i]->defaultFlags !=0) { michael@0: slotCount++; michael@0: } michael@0: } michael@0: } else { michael@0: slotCount = module->slotInfoCount; michael@0: } michael@0: michael@0: slotStrings = (char **)PORT_ZAlloc(slotCount*sizeof(char *)); michael@0: if (slotStrings == NULL) { michael@0: SECMOD_ReleaseReadLock(moduleLock); michael@0: goto loser; michael@0: } michael@0: michael@0: michael@0: /* build the slot info strings */ michael@0: if (module->slotCount) { michael@0: for (i=0, si= 0; i < module->slotCount; i++) { michael@0: if (module->slots[i]->defaultFlags) { michael@0: PORT_Assert(si < slotCount); michael@0: if (si >= slotCount) break; michael@0: slotStrings[si] = NSSUTIL_MkSlotString(module->slots[i]->slotID, michael@0: module->slots[i]->defaultFlags, michael@0: module->slots[i]->timeout, michael@0: module->slots[i]->askpw, michael@0: module->slots[i]->hasRootCerts, michael@0: module->slots[i]->hasRootTrust); michael@0: si++; michael@0: } michael@0: } michael@0: } else { michael@0: for (i=0; i < slotCount; i++) { michael@0: slotStrings[i] = NSSUTIL_MkSlotString( michael@0: module->slotInfo[i].slotID, michael@0: module->slotInfo[i].defaultFlags, michael@0: module->slotInfo[i].timeout, michael@0: module->slotInfo[i].askpw, michael@0: module->slotInfo[i].hasRootCerts, michael@0: module->slotInfo[i].hasRootTrust); michael@0: } michael@0: } michael@0: michael@0: SECMOD_ReleaseReadLock(moduleLock); michael@0: nss = NSSUTIL_MkNSSString(slotStrings,slotCount,module->internal, michael@0: module->isFIPS, module->isModuleDB, michael@0: module->moduleDBOnly, module->isCritical, michael@0: module->trustOrder, module->cipherOrder, michael@0: module->ssl[0],module->ssl[1]); michael@0: modSpec= NSSUTIL_MkModuleSpec(module->dllName,module->commonName, michael@0: module->libraryParams,nss); michael@0: PORT_Free(slotStrings); michael@0: PR_smprintf_free(nss); michael@0: loser: michael@0: return (modSpec); michael@0: } michael@0: michael@0: michael@0: char ** michael@0: SECMOD_GetModuleSpecList(SECMODModule *module) michael@0: { michael@0: SECMODModuleDBFunc func = (SECMODModuleDBFunc) module->moduleDBFunc; michael@0: if (func) { michael@0: return (*func)(SECMOD_MODULE_DB_FUNCTION_FIND, michael@0: module->libraryParams,NULL); michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: SECStatus michael@0: SECMOD_AddPermDB(SECMODModule *module) michael@0: { michael@0: SECMODModuleDBFunc func; michael@0: char *moduleSpec; michael@0: char **retString; michael@0: michael@0: if (module->parent == NULL) return SECFailure; michael@0: michael@0: func = (SECMODModuleDBFunc) module->parent->moduleDBFunc; michael@0: if (func) { michael@0: moduleSpec = secmod_mkModuleSpec(module); michael@0: retString = (*func)(SECMOD_MODULE_DB_FUNCTION_ADD, michael@0: module->parent->libraryParams,moduleSpec); michael@0: PORT_Free(moduleSpec); michael@0: if (retString != NULL) return SECSuccess; michael@0: } michael@0: return SECFailure; michael@0: } michael@0: michael@0: SECStatus michael@0: SECMOD_DeletePermDB(SECMODModule *module) michael@0: { michael@0: SECMODModuleDBFunc func; michael@0: char *moduleSpec; michael@0: char **retString; michael@0: michael@0: if (module->parent == NULL) return SECFailure; michael@0: michael@0: func = (SECMODModuleDBFunc) module->parent->moduleDBFunc; michael@0: if (func) { michael@0: moduleSpec = secmod_mkModuleSpec(module); michael@0: retString = (*func)(SECMOD_MODULE_DB_FUNCTION_DEL, michael@0: module->parent->libraryParams,moduleSpec); michael@0: PORT_Free(moduleSpec); michael@0: if (retString != NULL) return SECSuccess; michael@0: } michael@0: return SECFailure; michael@0: } michael@0: michael@0: SECStatus michael@0: SECMOD_FreeModuleSpecList(SECMODModule *module, char **moduleSpecList) michael@0: { michael@0: SECMODModuleDBFunc func = (SECMODModuleDBFunc) module->moduleDBFunc; michael@0: char **retString; michael@0: if (func) { michael@0: retString = (*func)(SECMOD_MODULE_DB_FUNCTION_RELEASE, michael@0: module->libraryParams,moduleSpecList); michael@0: if (retString != NULL) return SECSuccess; michael@0: } michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* michael@0: * load a PKCS#11 module but do not add it to the default NSS trust domain michael@0: */ michael@0: SECMODModule * michael@0: SECMOD_LoadModule(char *modulespec,SECMODModule *parent, PRBool recurse) michael@0: { michael@0: char *library = NULL, *moduleName = NULL, *parameters = NULL, *nss= NULL; michael@0: SECStatus status; michael@0: SECMODModule *module = NULL; michael@0: SECMODModule *oldModule = NULL; michael@0: SECStatus rv; michael@0: michael@0: /* initialize the underlying module structures */ michael@0: SECMOD_Init(); michael@0: michael@0: status = NSSUTIL_ArgParseModuleSpec(modulespec, &library, &moduleName, michael@0: ¶meters, &nss); michael@0: if (status != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: module = SECMOD_CreateModule(library, moduleName, parameters, nss); michael@0: if (library) PORT_Free(library); michael@0: if (moduleName) PORT_Free(moduleName); michael@0: if (parameters) PORT_Free(parameters); michael@0: if (nss) PORT_Free(nss); michael@0: if (!module) { michael@0: goto loser; michael@0: } michael@0: if (parent) { michael@0: module->parent = SECMOD_ReferenceModule(parent); michael@0: if (module->internal && secmod_IsInternalKeySlot(parent)) { michael@0: module->internal = parent->internal; michael@0: } michael@0: } michael@0: michael@0: /* load it */ michael@0: rv = secmod_LoadPKCS11Module(module, &oldModule); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* if we just reload an old module, no need to add it to any lists. michael@0: * we simple release all our references */ michael@0: if (oldModule) { michael@0: /* This module already exists, don't link it anywhere. This michael@0: * will probably destroy this module */ michael@0: SECMOD_DestroyModule(module); michael@0: return oldModule; michael@0: } michael@0: michael@0: if (recurse && module->isModuleDB) { michael@0: char ** moduleSpecList; michael@0: PORT_SetError(0); michael@0: michael@0: moduleSpecList = SECMOD_GetModuleSpecList(module); michael@0: if (moduleSpecList) { michael@0: char **index; michael@0: michael@0: index = moduleSpecList; michael@0: if (*index && SECMOD_GetSkipFirstFlag(module)) { michael@0: index++; michael@0: } michael@0: michael@0: for (; *index; index++) { michael@0: SECMODModule *child; michael@0: if (0 == PORT_Strcmp(*index, modulespec)) { michael@0: /* avoid trivial infinite recursion */ michael@0: PORT_SetError(SEC_ERROR_NO_MODULE); michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: child = SECMOD_LoadModule(*index,module,PR_TRUE); michael@0: if (!child) break; michael@0: if (child->isCritical && !child->loaded) { michael@0: int err = PORT_GetError(); michael@0: if (!err) michael@0: err = SEC_ERROR_NO_MODULE; michael@0: SECMOD_DestroyModule(child); michael@0: PORT_SetError(err); michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: SECMOD_DestroyModule(child); michael@0: } michael@0: SECMOD_FreeModuleSpecList(module,moduleSpecList); michael@0: } else { michael@0: if (!PORT_GetError()) michael@0: PORT_SetError(SEC_ERROR_NO_MODULE); michael@0: rv = SECFailure; michael@0: } michael@0: } michael@0: michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: michael@0: /* inherit the reference */ michael@0: if (!module->moduleDBOnly) { michael@0: SECMOD_AddModuleToList(module); michael@0: } else { michael@0: SECMOD_AddModuleToDBOnlyList(module); michael@0: } michael@0: michael@0: /* handle any additional work here */ michael@0: return module; michael@0: michael@0: loser: michael@0: if (module) { michael@0: if (module->loaded) { michael@0: SECMOD_UnloadModule(module); michael@0: } michael@0: SECMOD_AddModuleToUnloadList(module); michael@0: } michael@0: return module; michael@0: } michael@0: michael@0: /* michael@0: * load a PKCS#11 module and add it to the default NSS trust domain michael@0: */ michael@0: SECMODModule * michael@0: SECMOD_LoadUserModule(char *modulespec,SECMODModule *parent, PRBool recurse) michael@0: { michael@0: SECStatus rv = SECSuccess; michael@0: SECMODModule * newmod = SECMOD_LoadModule(modulespec, parent, recurse); michael@0: SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); michael@0: michael@0: if (newmod) { michael@0: SECMOD_GetReadLock(moduleLock); michael@0: rv = STAN_AddModuleToDefaultTrustDomain(newmod); michael@0: SECMOD_ReleaseReadLock(moduleLock); michael@0: if (SECSuccess != rv) { michael@0: SECMOD_DestroyModule(newmod); michael@0: return NULL; michael@0: } michael@0: } michael@0: return newmod; michael@0: } michael@0: michael@0: /* michael@0: * remove the PKCS#11 module from the default NSS trust domain, call michael@0: * C_Finalize, and destroy the module structure michael@0: */ michael@0: SECStatus SECMOD_UnloadUserModule(SECMODModule *mod) michael@0: { michael@0: SECStatus rv = SECSuccess; michael@0: int atype = 0; michael@0: SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); michael@0: if (!mod) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: SECMOD_GetReadLock(moduleLock); michael@0: rv = STAN_RemoveModuleFromDefaultTrustDomain(mod); michael@0: SECMOD_ReleaseReadLock(moduleLock); michael@0: if (SECSuccess != rv) { michael@0: return SECFailure; michael@0: } michael@0: return SECMOD_DeleteModuleEx(NULL, mod, &atype, PR_FALSE); michael@0: } michael@0: