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