Make SECOID_AddEntry be thread safe. Export it. Bug 124923. r=relyea.

This commit is contained in:
nelsonb%netscape.com 2004-01-29 21:23:36 +00:00
parent ee55e2e256
commit ea227986af
3 changed files with 221 additions and 111 deletions

View File

@ -35,7 +35,7 @@
* Types for encoding/decoding of ASN.1 using BER/DER (Basic/Distinguished
* Encoding Rules).
*
* $Id: secasn1t.h,v 1.7 2003/11/07 01:41:22 nelsonb%netscape.com Exp $
* $Id: secasn1t.h,v 1.8 2004/01/29 21:23:35 nelsonb%netscape.com Exp $
*/
#ifndef _SECASN1T_H_
@ -116,12 +116,14 @@ typedef struct sec_ASN1Template_struct {
#define SEC_ASN1_ENUMERATED 0x0a
#define SEC_ASN1_EMBEDDED_PDV 0x0b
#define SEC_ASN1_UTF8_STRING 0x0c
/* 0x0d */
/* 0x0e */
/* 0x0f */
#define SEC_ASN1_SEQUENCE 0x10
#define SEC_ASN1_SET 0x11
#define SEC_ASN1_NUMERIC_STRING 0x12
#define SEC_ASN1_PRINTABLE_STRING 0x13
#define SEC_ASN1_T61_STRING 0x14
#define SEC_ASN1_TELETEX_STRING SEC_ASN1_T61_STRING
#define SEC_ASN1_VIDEOTEX_STRING 0x15
#define SEC_ASN1_IA5_STRING 0x16
#define SEC_ASN1_UTC_TIME 0x17
@ -133,6 +135,7 @@ typedef struct sec_ASN1Template_struct {
/* 0x1d */
#define SEC_ASN1_BMP_STRING 0x1e
#define SEC_ASN1_HIGH_TAG_NUMBER 0x1f
#define SEC_ASN1_TELETEX_STRING SEC_ASN1_T61_STRING
/*
** Modifiers to type tags. These are also specified by a/the

View File

@ -41,6 +41,7 @@
#include "secitem.h"
#include "secerr.h"
#include "plhash.h"
#include "nssrwlk.h"
/* MISSI Mosaic Object ID space */
#define USGOV 0x60, 0x86, 0x48, 0x01, 0x65
@ -1427,46 +1428,71 @@ const static SECOidData oids[] = {
/*
* now the dynamic table. The dynamic table gets build at init time.
* and gets modified if the user loads new crypto modules.
* and conceivably gets modified if the user loads new crypto modules.
* All this static data, and the allocated data to which it points,
* is protected by a global reader/writer lock.
* The c language guarantees that global and static data that is not
* explicitly initialized will be imiiialized with zeros. If we
* initialize it with zeros, the data goes into the initialized data
* secment, and increases the size of the library. By leaving it
* uninitialized, it is allocated in BSS, and does NOT increase the
* library size.
*/
static NSSRWLock * dynOidLock;
static PLArenaPool * dynOidPool;
static PLHashTable * dynOidHash;
static SECOidData ** dynOidTable; /* not in the pool */
static int dynOidEntriesAllocated;
static int dynOidEntriesUsed;
static PLHashTable *oid_d_hash = 0;
static SECOidData **secoidDynamicTable = NULL;
static int secoidDynamicTableSize = 0;
static int secoidLastDynamicEntry = 0;
static int secoidLastHashEntry = 0;
/* Creates NSSRWLock and dynOidPool, if they don't exist.
** This function MIGHT create the lock, but not the pool, so
** code should test for dynOidPool, not dynOidLock, when deciding
** whether or not to call this function.
*/
static SECStatus
secoid_DynamicRehash(void)
secoid_InitDynOidData(void)
{
SECOidData *oid;
PLHashEntry *entry;
int i;
int last = secoidLastDynamicEntry;
SECStatus rv = SECSuccess;
NSSRWLock * lock;
if (!oid_d_hash) {
oid_d_hash = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
PL_CompareValues, NULL, NULL);
/* This function will create the lock if it doesn't exist,
** and will return the address of the lcok, whether it was
** previously created, or was created by the function.
*/
lock = nssRWLock_AtomicCreate(&dynOidLock, 1, "dynamic OID data");
if (!lock) {
return SECFailure; /* Error code should already be set. */
}
if ( !oid_d_hash ) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return(SECFailure);
}
for ( i = secoidLastHashEntry; i < last; i++ ) {
oid = secoidDynamicTable[i];
entry = PL_HashTableAdd( oid_d_hash, &oid->oid, oid );
if ( entry == NULL ) {
return(SECFailure);
PORT_Assert(lock == dynOidLock);
NSSRWLock_LockWrite(lock);
if (!dynOidPool) {
dynOidPool = PORT_NewArena(2048);
if (!dynOidPool) {
rv = SECFailure /* Error code should already be set. */;
}
}
secoidLastHashEntry = last;
return(SECSuccess);
NSSRWLock_UnlockWrite(lock);
return rv;
}
/* Add oidData to hash table. Caller holds write lock dynOidLock. */
static SECStatus
secoid_HashDynamicOiddata(const SECOidData * oid)
{
PLHashEntry *entry;
if (!dynOidHash) {
dynOidHash = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
PL_CompareValues, NULL, NULL);
if ( !dynOidHash ) {
return SECFailure;
}
}
entry = PL_HashTableAdd( dynOidHash, &oid->oid, (void *)oid );
return entry ? SECSuccess : SECFailure;
}
/*
@ -1476,94 +1502,143 @@ secoid_DynamicRehash(void)
* no locks.... (sigh).
*/
static SECOidData *
secoid_FindDynamic(SECItem *key) {
secoid_FindDynamic(const SECItem *key)
{
SECOidData *ret = NULL;
if (secoidDynamicTable == NULL) {
/* PORT_SetError! */
return NULL;
}
if (secoidLastHashEntry != secoidLastDynamicEntry) {
SECStatus rv = secoid_DynamicRehash();
if ( rv != SECSuccess ) {
return NULL;
if (dynOidTable) {
NSSRWLock_LockRead(dynOidLock);
if (dynOidTable) { /* must check it again with lock held. */
ret = (SECOidData *)PL_HashTableLookup(dynOidHash, key);
}
NSSRWLock_UnlockRead(dynOidLock);
}
if (ret == NULL) {
PORT_SetError(SEC_ERROR_UNRECOGNIZED_OID);
}
ret = (SECOidData *)PL_HashTableLookup (oid_d_hash, key);
return ret;
}
static SECOidData *
secoid_FindDynamicByTag(SECOidTag tagnum)
{
SECOidData *data = NULL;
int tagNumDiff;
if (secoidDynamicTable == NULL) {
return NULL;
}
if (tagnum < SEC_OID_TOTAL) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return NULL;
}
tagNumDiff = tagnum - SEC_OID_TOTAL;
if (tagNumDiff >= secoidLastDynamicEntry) {
return NULL;
}
return(secoidDynamicTable[tagNumDiff]);
if (dynOidTable) {
NSSRWLock_LockRead(dynOidLock);
if (dynOidTable != NULL && /* must check it again with lock held. */
tagNumDiff < dynOidEntriesUsed) {
data = dynOidTable[tagNumDiff];
}
NSSRWLock_UnlockRead(dynOidLock);
}
if (data == NULL) {
PORT_SetError(SEC_ERROR_UNRECOGNIZED_OID);
}
return data;
}
/*
* this routine is definately not thread safe. It is only called out
* of the UI, or at init time. If we want to call it any other time,
* we need to make it thread safe.
* This routine is thread safe now.
*/
SECStatus
SECOID_AddEntry(SECItem *oid, char *description, unsigned long mech) {
SECOidData *oiddp = (SECOidData *)PORT_Alloc(sizeof(SECOidData));
int last = secoidLastDynamicEntry;
int tableSize = secoidDynamicTableSize;
int next = last++;
SECOidData **newTable = secoidDynamicTable;
SECOidData **oldTable = NULL;
SECOidTag
SECOID_AddEntry(const SECOidData * src)
{
SECOidData * dst;
SECOidData **table;
SECOidTag ret = SEC_OID_UNKNOWN;
SECStatus rv;
int tableEntries;
int used;
if (oid == NULL) {
return SECFailure;
if (!src || !src->oid.data || !src->oid.len || \
!src->desc || !strlen(src->desc)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return ret;
}
if (src->supportedExtension != INVALID_CERT_EXTENSION &&
src->supportedExtension != UNSUPPORTED_CERT_EXTENSION &&
src->supportedExtension != SUPPORTED_CERT_EXTENSION ) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return ret;
}
/* fill in oid structure */
if (SECITEM_CopyItem(NULL,&oiddp->oid,oid) != SECSuccess) {
PORT_Free(oiddp);
return SECFailure;
if (!dynOidPool && secoid_InitDynOidData() != SECSuccess) {
/* Caller has set error code. */
return ret;
}
oiddp->offset = (SECOidTag)(next + SEC_OID_TOTAL);
/* may we should just reference the copy passed to us? */
oiddp->desc = PORT_Strdup(description);
oiddp->mechanism = mech;
NSSRWLock_LockWrite(dynOidLock);
if (last > tableSize) {
int oldTableSize = tableSize;
tableSize += 10;
oldTable = newTable;
newTable = (SECOidData **)PORT_ZAlloc(sizeof(SECOidData *)*tableSize);
/* We've just acquired the write lock, and now we call FindOIDTag
** which will acquire and release the read lock. NSSRWLock has been
** designed to allow this very case without deadlock. This approach
** makes the test for the presence of the OID, and the subsequent
** addition of the OID to the table a single atomic write operation.
*/
ret = SECOID_FindOIDTag(&src->oid);
if (ret != SEC_OID_UNKNOWN) {
/* we could return an error here, but I chose not to do that.
** This way, if we add an OID to the shared library's built in
** list of OIDs in some future release, and that OID is the same
** as some OID that a program has been adding, the program will
** not suddenly stop working.
*/
goto done;
}
table = dynOidTable;
tableEntries = dynOidEntriesAllocated;
used = dynOidEntriesUsed;
if (used + 1 > tableEntries) {
SECOidData **newTable;
int newTableEntries = tableEntries + 16;
newTable = (SECOidData **)PORT_Realloc(table,
newTableEntries * sizeof(SECOidData *));
if (newTable == NULL) {
PORT_Free(oiddp->oid.data);
PORT_Free(oiddp);
return SECFailure;
goto done;
}
PORT_Memcpy(newTable,oldTable,sizeof(SECOidData *)*oldTableSize);
PORT_Free(oldTable);
dynOidTable = table = newTable;
dynOidEntriesAllocated = tableEntries = newTableEntries;
}
newTable[next] = oiddp;
secoidDynamicTable = newTable;
secoidDynamicTableSize = tableSize;
secoidLastDynamicEntry= last;
return SECSuccess;
/* copy oid structure */
dst = PORT_ArenaNew(dynOidPool, SECOidData);
if (!dst) {
goto done;
}
rv = SECITEM_CopyItem(dynOidPool, &dst->oid, &src->oid);
if (rv != SECSuccess) {
goto done;
}
dst->desc = PORT_ArenaStrdup(dynOidPool, src->desc);
if (!dst->desc) {
goto done;
}
dst->offset = (SECOidTag)(used + SEC_OID_TOTAL);
dst->mechanism = src->mechanism;
dst->supportedExtension = src->supportedExtension;
rv = secoid_HashDynamicOiddata(dst);
if ( rv == SECSuccess ) {
table[used++] = dst;
dynOidEntriesUsed = used;
ret = dst->offset;
}
done:
NSSRWLock_UnlockWrite(dynOidLock);
return ret;
}
/* normal static table processing */
static PLHashTable *oidhash = NULL;
@ -1583,6 +1658,10 @@ secoid_Init(void)
const SECOidData *oid;
int i;
if (!dynOidPool && secoid_InitDynOidData() != SECSuccess) {
return SECFailure;
}
if (oidhash) {
return SECSuccess;
}
@ -1642,7 +1721,7 @@ SECOID_FindOIDByMechanism(unsigned long mechanism)
}
SECOidData *
SECOID_FindOID(SECItem *oid)
SECOID_FindOID(const SECItem *oid)
{
SECOidData *ret;
@ -1660,7 +1739,7 @@ SECOID_FindOID(SECItem *oid)
}
SECOidTag
SECOID_FindOIDTag(SECItem *oid)
SECOID_FindOIDTag(const SECItem *oid)
{
SECOidData *oiddata;
@ -1709,8 +1788,6 @@ SECOID_FindOIDTagDescription(SECOidTag tagnum)
SECStatus
SECOID_Shutdown(void)
{
int i;
if (oidhash) {
PL_HashTableDestroy(oidhash);
oidhash = NULL;
@ -1719,19 +1796,43 @@ SECOID_Shutdown(void)
PL_HashTableDestroy(oidmechhash);
oidmechhash = NULL;
}
if (oid_d_hash) {
PL_HashTableDestroy(oid_d_hash);
oid_d_hash = NULL;
}
if (secoidDynamicTable) {
for (i=0; i < secoidLastDynamicEntry; i++) {
PORT_Free(secoidDynamicTable[i]);
/* Have to handle the case where the lock was created, but
** the pool wasn't.
** I'm not going to attempt to create the lock, just to protect
** the destruction of data the probably isn't inisialized anyway.
*/
if (dynOidLock) {
NSSRWLock_LockWrite(dynOidLock);
if (dynOidHash) {
PL_HashTableDestroy(dynOidHash);
dynOidHash = NULL;
}
PORT_Free(secoidDynamicTable);
secoidDynamicTable = NULL;
secoidDynamicTableSize = 0;
secoidLastDynamicEntry = 0;
secoidLastHashEntry = 0;
if (dynOidPool) {
PORT_FreeArena(dynOidPool, PR_FALSE);
dynOidPool = NULL;
}
if (dynOidTable) {
PORT_Free(dynOidTable);
dynOidTable = NULL;
}
dynOidEntriesAllocated = 0;
dynOidEntriesUsed = 0;
NSSRWLock_UnlockWrite(dynOidLock);
NSSRWLock_Destroy(dynOidLock);
dynOidLock = NULL;
} else {
/* Since dynOidLock doesn't exist, then all the data it protects
** should be uninitialized. We'll check that (in DEBUG builds),
** and then make sure it is so, in case NSS is reinitialized.
*/
PORT_Assert(!dynOidHash && !dynOidPool && !dynOidTable && \
!dynOidEntriesAllocated && !dynOidEntriesUsed);
dynOidHash = NULL;
dynOidPool = NULL;
dynOidTable = NULL;
dynOidEntriesAllocated = 0;
dynOidEntriesUsed = 0;
}
return SECSuccess;
}

View File

@ -36,7 +36,7 @@
/*
* secoid.h - public data structures and prototypes for ASN.1 OID functions
*
* $Id: secoid.h,v 1.4 2001/08/24 18:34:34 relyea%netscape.com Exp $
* $Id: secoid.h,v 1.5 2004/01/29 21:23:36 nelsonb%netscape.com Exp $
*/
#include "plarena.h"
@ -55,8 +55,8 @@ SEC_ASN1_CHOOSER_DECLARE(SECOID_AlgorithmIDTemplate)
/*
* OID handling routines
*/
extern SECOidData *SECOID_FindOID(SECItem *oid);
extern SECOidTag SECOID_FindOIDTag(SECItem *oid);
extern SECOidData *SECOID_FindOID( const SECItem *oid);
extern SECOidTag SECOID_FindOIDTag(const SECItem *oid);
extern SECOidData *SECOID_FindOIDByTag(SECOidTag tagnum);
extern SECOidData *SECOID_FindOIDByMechanism(unsigned long mechanism);
@ -69,7 +69,7 @@ extern SECOidData *SECOID_FindOIDByMechanism(unsigned long mechanism);
** Fill in an algorithm-ID object given a tag and some parameters.
** "aid" where the DER encoded algorithm info is stored (memory
** is allocated)
** "tag" the tag defining the algorithm (SEC_OID_*)
** "tag" the tag number defining the algorithm
** "params" if not NULL, the parameters to go with the algorithm
*/
extern SECStatus SECOID_SetAlgorithmID(PRArenaPool *arena, SECAlgorithmID *aid,
@ -85,7 +85,7 @@ extern SECStatus SECOID_CopyAlgorithmID(PRArenaPool *arena, SECAlgorithmID *dest
SECAlgorithmID *src);
/*
** Get the SEC_OID_* tag for the given algorithm-id object.
** Get the tag number for the given algorithm-id object.
*/
extern SECOidTag SECOID_GetAlgorithmTag(SECAlgorithmID *aid);
@ -105,10 +105,16 @@ extern SECComparison SECOID_CompareAlgorithmID(SECAlgorithmID *a,
extern PRBool SECOID_KnownCertExtenOID (SECItem *extenOid);
/* Given a SEC_OID_* tag, return a string describing it.
/* Given a tag number, return a string describing it.
*/
extern const char *SECOID_FindOIDTagDescription(SECOidTag tagnum);
/* Add a dynamic SECOidData to the dynamic OID table.
** Routine copies the src entry, and returns the new SECOidTag.
** Returns SEC_OID_INVALID if failed to add for some reason.
*/
extern SECOidTag SECOID_AddEntry(const SECOidData * src);
/*
* free up the oid data structures.
*/