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