blob: 5855084753024293b1fa923bbc7a10f9685c20c9 [file] [log] [blame]
/* ***** 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,
&parameters, &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);
}