gecko-dev/security/nss/lib/pk11wrap/pk11db.c
2000-05-16 17:37:10 +00:00

646 lines
18 KiB
C

/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* The following code handles the storage of PKCS 11 modules used by the
* NSS. This file is written to abstract away how the modules are
* stored so we can deside that later.
*/
#include "seccomon.h"
#include "secmod.h"
#include "prlock.h"
#include "pkcs11.h"
#include "secmodi.h"
#include "pk11func.h"
#include "mcom_db.h"
/* create a new module */
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;
#ifdef PKCS11_USE_THREADS
newMod->refLock = (void *)PR_NewLock();
if (newMod->refLock == NULL) {
PORT_FreeArena(arena,PR_FALSE);
return NULL;
}
#else
newMod->refLock = NULL;
#endif
return newMod;
}
/* create a new ModuleListElement */
SECMODModuleList *SECMOD_NewModuleListElement(void) {
SECMODModuleList *newModList;
newModList= (SECMODModuleList *) PORT_Alloc(sizeof(SECMODModuleList));
if (newModList) {
newModList->next = NULL;
newModList->module = NULL;
}
return newModList;
}
static unsigned long internalFlags = SECMOD_RSA_FLAG|SECMOD_DSA_FLAG|
SECMOD_RC2_FLAG| SECMOD_RC4_FLAG|SECMOD_DES_FLAG|SECMOD_RANDOM_FLAG|
SECMOD_SHA1_FLAG|SECMOD_MD5_FLAG|SECMOD_MD2_FLAG|SECMOD_SSL_FLAG|
SECMOD_TLS_FLAG;
/* create a Internal module */
SECMODModule *SECMOD_NewInternal(void) {
SECMODModule *intern;
static PK11PreSlotInfo internSlotInfo =
{ 1, SECMOD_RSA_FLAG|SECMOD_DSA_FLAG|SECMOD_RC2_FLAG|
SECMOD_RC4_FLAG|SECMOD_DES_FLAG|SECMOD_RANDOM_FLAG|
SECMOD_SHA1_FLAG|SECMOD_MD5_FLAG|SECMOD_MD2_FLAG|
SECMOD_SSL_FLAG|SECMOD_TLS_FLAG, -1, 30 };
intern = SECMOD_NewModule();
if (intern == NULL) {
return NULL;
}
/*
* make this module an internal module
*/
intern->commonName = "Netscape Internal PKCS #11 Module";
intern->internal = PR_TRUE;
intern->slotInfoCount = 1;
intern->slotInfo = &internSlotInfo;
return (intern);
}
/* create a FIPS Internal module */
SECMODModule *SECMOD_GetFIPSInternal(void) {
SECMODModule *intern;
intern = SECMOD_NewInternal();
if (intern == NULL) {
return NULL;
}
/*
* make this module a FIPS internal module
*/
intern->slotInfo[0].slotID = 3; /* FIPS slot */
intern->commonName = "Netscape Internal FIPS PKCS #11 Module";
intern->isFIPS = PR_TRUE;
return (intern);
}
SECMODModule *SECMOD_DupModule(SECMODModule *old) {
SECMODModule *newMod;
newMod = SECMOD_NewModule();
if (newMod == NULL) {
return NULL;
}
/*
* initialize of the fields of the module
*/
newMod->dllName = PORT_ArenaStrdup(newMod->arena,old->dllName);
newMod->commonName = PORT_ArenaStrdup(newMod->arena,old->commonName);;
return newMod;
}
/*
* make a new reference to a module so It doesn't go away on us
*/
SECMODModule *
SECMOD_ReferenceModule(SECMODModule *module) {
PK11_USE_THREADS(PR_Lock((PRLock *)module->refLock);)
PORT_Assert(module->refCount > 0);
module->refCount++;
PK11_USE_THREADS(PR_Unlock((PRLock*)module->refLock);)
return module;
}
/* destroy an existing module */
void
SECMOD_DestroyModule(SECMODModule *module) {
PRBool willfree = PR_FALSE;
int slotCount;
int i;
PK11_USE_THREADS(PR_Lock((PRLock *)module->refLock);)
if (module->refCount-- == 1) {
willfree = PR_TRUE;
}
PORT_Assert(willfree || (module->refCount > 0));
PK11_USE_THREADS(PR_Unlock((PRLock *)module->refLock);)
if (!willfree) {
return;
}
/* slots can't really disappear until our module starts freeing them,
* so this check is safe */
slotCount = module->slotCount;
if (slotCount == 0) {
SECMOD_SlotDestroyModule(module,PR_FALSE);
return;
}
/* now free all out slots, when they are done, they will cause the
* module to disappear altogether */
for (i=0 ; i < slotCount; i++) {
if (!module->slots[i]->disabled) {
PK11_ClearSlotList(module->slots[i]);
}
PK11_FreeSlot(module->slots[i]);
}
/* WARNING: once the last slot has been freed is it possible (even likely)
* that module is no more... touching it now is a good way to go south */
}
/* we can only get here if we've destroyed the module, or some one has
* erroneously freed a slot that wasn't referenced. */
void
SECMOD_SlotDestroyModule(SECMODModule *module, PRBool fromSlot) {
PRBool willfree = PR_FALSE;
if (fromSlot) {
PORT_Assert(module->refCount == 0);
PK11_USE_THREADS(PR_Lock((PRLock *)module->refLock);)
if (module->slotCount-- == 1) {
willfree = PR_TRUE;
}
PORT_Assert(willfree || (module->slotCount > 0));
PK11_USE_THREADS(PR_Unlock((PRLock *)module->refLock);)
if (!willfree) return;
}
if (module->loaded) {
SECMOD_UnloadModule(module);
}
PK11_USE_THREADS(PR_DestroyLock((PRLock *)module->refLock);)
PORT_FreeArena(module->arena,PR_FALSE);
}
/* destroy a list element
* this destroys a single element, and returns the next element
* on the chain. It makes it easy to implement for loops to delete
* the chain. It also make deleting a single element easy */
SECMODModuleList *
SECMOD_DestroyModuleListElement(SECMODModuleList *element) {
SECMODModuleList *next = element->next;
if (element->module) {
SECMOD_DestroyModule(element->module);
element->module = NULL;
}
PORT_Free(element);
return next;
}
/*
* Destroy an entire module list
*/
void
SECMOD_DestroyModuleList(SECMODModuleList *list) {
SECMODModuleList *lp;
for ( lp = list; lp != NULL; lp = SECMOD_DestroyModuleListElement(lp)) ;
}
/* Construct a database key for a given module */
static SECStatus secmod_MakeKey(DBT *key, SECMODModule * module) {
int len = 0;
len = PORT_Strlen(module->commonName);
key->data = module->commonName;
key->size = len;
return SECSuccess;
}
/* free out constructed database key */
static void secmod_FreeKey(DBT *key) {
key->data = NULL;
key->size = 0;
}
typedef struct secmodDataStr secmodData;
typedef struct secmodSlotDataStr secmodSlotData;
struct secmodDataStr {
unsigned char major;
unsigned char minor;
unsigned char nameStart[2];
unsigned char slotOffset[2];
unsigned char internal;
unsigned char fips;
unsigned char ssl[8];
unsigned char names[4]; /* enough space for the length fields */
};
struct secmodSlotDataStr {
unsigned char slotID[4];
unsigned char defaultFlags[4];
unsigned char timeout[4];
unsigned char askpw;
unsigned char reserved[19]; /* this makes it a round 32 bytes */
};
#define SECMOD_DB_VERSION_MAJOR 0
#define SECMOD_DB_VERSION_MINOR 4
#define SECMOD_DB_NOUI_VERSION_MAJOR 0
#define SECMOD_DB_NOUI_VERSION_MINOR 3
#define SECMOD_PUTSHORT(dest,src) \
(dest)[1] = (unsigned char) ((src)&0xff); \
(dest)[0] = (unsigned char) (((src) >> 8) & 0xff);
#define SECMOD_PUTLONG(dest,src) \
(dest)[3] = (unsigned char) ((src)&0xff); \
(dest)[2] = (unsigned char) (((src) >> 8) & 0xff); \
(dest)[1] = (unsigned char) (((src) >> 16) & 0xff); \
(dest)[0] = (unsigned char) (((src) >> 24) & 0xff);
#define SECMOD_GETSHORT(src) \
((unsigned short) (((src)[0] << 8) | (src)[1]))
#define SECMOD_GETLONG(src) \
((unsigned long) (( (unsigned long) (src)[0] << 24) | \
( (unsigned long) (src)[1] << 16) | \
( (unsigned long) (src)[2] << 8) | \
(unsigned long) (src)[3]))
/*
* build a data base entry from a module
*/
static SECStatus secmod_EncodeData(DBT *data, SECMODModule * module) {
secmodData *encoded;
secmodSlotData *slot;
unsigned char *dataPtr;
unsigned short len, len2 = 0,count = 0;
unsigned short offset;
int dataLen, i, si;
len = PORT_Strlen(module->commonName);
if (module->dllName) {
len2 = PORT_Strlen(module->dllName);
}
if (module->slotCount != 0) {
for (i=0; i < module->slotCount; i++) {
if (module->slots[i]->defaultFlags != 0) {
count++;
}
}
} else {
count = module->slotInfoCount;
}
dataLen = sizeof(secmodData) + len + len2 + 2 +
count*sizeof(secmodSlotData);
data->data = (unsigned char *)
PORT_Alloc(dataLen);
encoded = (secmodData *)data->data;
dataPtr = (unsigned char *) data->data;
data->size = dataLen;
if (encoded == NULL) return SECFailure;
encoded->major = SECMOD_DB_VERSION_MAJOR;
encoded->minor = SECMOD_DB_VERSION_MINOR;
encoded->internal = (unsigned char) (module->internal ? 1 : 0);
encoded->fips = (unsigned char) (module->isFIPS ? 1 : 0);
SECMOD_PUTLONG(encoded->ssl,module->ssl[0]);
SECMOD_PUTLONG(&encoded->ssl[4],module->ssl[1]);
offset = (unsigned long) &(((secmodData *)0)->names[0]);
SECMOD_PUTSHORT(encoded->nameStart,offset);
offset = offset +len + len2 + 4;
SECMOD_PUTSHORT(encoded->slotOffset,offset);
SECMOD_PUTSHORT(&dataPtr[offset],count);
slot = (secmodSlotData *)(dataPtr+offset+2);
SECMOD_PUTSHORT(encoded->names,len);
PORT_Memcpy(&encoded->names[2],module->commonName,len);
SECMOD_PUTSHORT(&encoded->names[len+2],len2);
if (len2) PORT_Memcpy(&encoded->names[len+4],module->dllName,len2);
if (module->slotCount) {
for (i=0,si=0; i < module->slotCount; i++) {
if (module->slots[i]->defaultFlags) {
SECMOD_PUTLONG(slot[si].slotID, module->slots[i]->slotID);
SECMOD_PUTLONG(slot[si].defaultFlags,
module->slots[i]->defaultFlags);
SECMOD_PUTLONG(slot[si].timeout,module->slots[i]->timeout);
slot[si].askpw = module->slots[i]->askpw;
PORT_Memset(slot[si].reserved, 0, sizeof(slot[si].reserved));
si++;
}
}
} else {
for (i=0; i < module->slotInfoCount; i++) {
SECMOD_PUTLONG(slot[i].slotID, module->slotInfo[i].slotID);
SECMOD_PUTLONG(slot[i].defaultFlags,
module->slotInfo[i].defaultFlags);
SECMOD_PUTLONG(slot[i].timeout,module->slotInfo[i].timeout);
slot[i].askpw = module->slotInfo[i].askpw;
PORT_Memset(slot[i].reserved, 0, sizeof(slot[i].reserved));
}
}
return SECSuccess;
}
static void secmod_FreeData(DBT *data) {
if (data->data) {
PORT_Free(data->data);
}
}
/*
* build a module from the data base entry.
*/
static SECMODModule *secmod_DecodeData(DBT *data) {
SECMODModule * module;
secmodData *encoded;
secmodSlotData *slots;
unsigned char *names;
unsigned short len,len1;
unsigned long slotCount;
unsigned short offset;
PRBool isOldVersion = PR_FALSE;
int i;
encoded = (secmodData *)data->data;
names = (unsigned char *)data->data;
offset = SECMOD_GETSHORT(encoded->slotOffset);
slots = (secmodSlotData *) (names + offset + 2);
slotCount = SECMOD_GETSHORT(names + offset);
names += SECMOD_GETSHORT(encoded->nameStart);
module = SECMOD_NewModule();
if (module == NULL) return NULL;
module->internal = (encoded->internal != 0) ? PR_TRUE: PR_FALSE;
module->isFIPS = (encoded->fips != 0) ? PR_TRUE: PR_FALSE;
len = SECMOD_GETSHORT(names);
if (module->internal && (encoded->major == SECMOD_DB_NOUI_VERSION_MAJOR) &&
(encoded->minor <= SECMOD_DB_NOUI_VERSION_MINOR)) {
isOldVersion = PR_TRUE;
}
/* decode the common name */
module->commonName = (char*)PORT_ArenaAlloc(module->arena,len+1);
if (module->commonName == NULL) {
SECMOD_DestroyModule(module);
return NULL;
}
PORT_Memcpy(module->commonName,&names[2],len);
module->commonName[len] = 0;
/* decode the DLL name */
len1 = (names[len+2] << 8) | names[len+3];
if (len1) {
module->dllName = (char*)PORT_ArenaAlloc(module->arena,len1 + 1);
if (module->dllName == NULL) {
SECMOD_DestroyModule(module);
return NULL;
}
PORT_Memcpy(module->dllName,&names[len+4],len1);
module->dllName[len1] = 0;
}
module->slotInfoCount = slotCount;
module->slotInfo = (PK11PreSlotInfo *) PORT_ArenaAlloc(module->arena,
slotCount * sizeof(PK11PreSlotInfo));
for (i=0; i < (int) slotCount; i++) {
module->slotInfo[i].slotID = SECMOD_GETLONG(slots[i].slotID);
module->slotInfo[i].defaultFlags =
SECMOD_GETLONG(slots[i].defaultFlags);
if (isOldVersion && module->internal &&
(module->slotInfo[i].slotID != 2)) {
module->slotInfo[i].defaultFlags |= internalFlags;
}
module->slotInfo[i].timeout = SECMOD_GETLONG(slots[i].timeout);
module->slotInfo[i].askpw = slots[i].askpw;
if (module->slotInfo[i].askpw == 0xff) {
module->slotInfo[i].askpw = -1;
}
}
/* decode SSL cipher enable flags */
module->ssl[0] = SECMOD_GETLONG(encoded->ssl);
module->ssl[1] = SECMOD_GETLONG(&encoded->ssl[4]);
return (module);
}
/*
* open the PKCS #11 data base.
*/
static char *pkcs11dbName = NULL;
void SECMOD_InitDB(char *dbname) {
pkcs11dbName = PORT_Strdup(dbname);
}
static DB *secmod_OpenDB(PRBool readOnly) {
DB *pkcs11db = NULL;
char *dbname;
if (pkcs11dbName == NULL) return NULL;
dbname = pkcs11dbName;
/* I'm sure we should do more checks here sometime... */
pkcs11db = dbopen(dbname, readOnly ? O_RDONLY : O_RDWR, 0600, DB_HASH, 0);
/* didn't exist? create it */
if (pkcs11db == NULL) {
if (readOnly) return NULL;
pkcs11db = dbopen( dbname,
O_RDWR | O_CREAT | O_TRUNC, 0600, DB_HASH, 0 );
if (pkcs11db) (* pkcs11db->sync)(pkcs11db, 0);
}
return pkcs11db;
}
static void secmod_CloseDB(DB *pkcs11db) {
(*pkcs11db->close)(pkcs11db);
}
/*
* Read all the existing modules in
*/
SECMODModuleList *
SECMOD_ReadPermDB(void) {
DBT key,data;
int ret;
DB *pkcs11db = NULL;
SECMODModuleList *newmod = NULL,*mod = NULL;
pkcs11db = secmod_OpenDB(PR_TRUE);
if (pkcs11db == NULL) {
return NULL;
}
/* read and parse the file or data base */
ret = (*pkcs11db->seq)(pkcs11db, &key, &data, R_FIRST);
if (ret) goto done;
do {
/* allocate space for modules */
newmod = SECMOD_NewModuleListElement();
if (newmod == NULL) break;
newmod->module = secmod_DecodeData(&data);
if (newmod->module == NULL) {
SECMOD_DestroyModuleListElement(newmod);
break;
}
newmod->next = mod;
mod = newmod;
} while ( (*pkcs11db->seq)(pkcs11db, &key, &data, R_NEXT) == 0);
done:
secmod_CloseDB(pkcs11db);
return mod;
}
/*
* Delete a module from the Data Base
*/
SECStatus
SECMOD_DeletePermDB(SECMODModule * module) {
DBT key;
SECStatus rv = SECFailure;
DB *pkcs11db = NULL;
int ret;
/* make sure we have a db handle */
pkcs11db = secmod_OpenDB(PR_FALSE);
if (pkcs11db == NULL) {
return SECFailure;
}
rv = secmod_MakeKey(&key,module);
if (rv != SECSuccess) goto done;
rv = SECFailure;
ret = (*pkcs11db->del)(pkcs11db, &key, 0);
secmod_FreeKey(&key);
if (ret != 0) goto done;
ret = (*pkcs11db->sync)(pkcs11db, 0);
if (ret == 0) rv = SECSuccess;
done:
secmod_CloseDB(pkcs11db);
return rv;
}
/*
* Add a module to the Data base
*/
SECStatus
SECMOD_AddPermDB(SECMODModule *module) {
DBT key,data;
SECStatus rv = SECFailure;
DB *pkcs11db = NULL;
int ret;
/* make sure we have a db handle */
pkcs11db = secmod_OpenDB(PR_FALSE);
if (pkcs11db == NULL) {
return SECFailure;
}
rv = secmod_MakeKey(&key,module);
if (rv != SECSuccess) goto done;
rv = secmod_EncodeData(&data,module);
if (rv != SECSuccess) {
secmod_FreeKey(&key);
goto done;
}
rv = SECFailure;
ret = (*pkcs11db->put)(pkcs11db, &key, &data, 0);
secmod_FreeKey(&key);
secmod_FreeData(&data);
if (ret != 0) goto done;
ret = (*pkcs11db->sync)(pkcs11db, 0);
if (ret == 0) rv = SECSuccess;
done:
secmod_CloseDB(pkcs11db);
return rv;
}