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
Родитель aa1cc56ff2
Коммит ae49b07260
3 изменённых файлов: 221 добавлений и 111 удалений

Просмотреть файл

@ -35,7 +35,7 @@
* Types for encoding/decoding of ASN.1 using BER/DER (Basic/Distinguished * Types for encoding/decoding of ASN.1 using BER/DER (Basic/Distinguished
* Encoding Rules). * 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_ #ifndef _SECASN1T_H_
@ -116,12 +116,14 @@ typedef struct sec_ASN1Template_struct {
#define SEC_ASN1_ENUMERATED 0x0a #define SEC_ASN1_ENUMERATED 0x0a
#define SEC_ASN1_EMBEDDED_PDV 0x0b #define SEC_ASN1_EMBEDDED_PDV 0x0b
#define SEC_ASN1_UTF8_STRING 0x0c #define SEC_ASN1_UTF8_STRING 0x0c
/* 0x0d */
/* 0x0e */
/* 0x0f */
#define SEC_ASN1_SEQUENCE 0x10 #define SEC_ASN1_SEQUENCE 0x10
#define SEC_ASN1_SET 0x11 #define SEC_ASN1_SET 0x11
#define SEC_ASN1_NUMERIC_STRING 0x12 #define SEC_ASN1_NUMERIC_STRING 0x12
#define SEC_ASN1_PRINTABLE_STRING 0x13 #define SEC_ASN1_PRINTABLE_STRING 0x13
#define SEC_ASN1_T61_STRING 0x14 #define SEC_ASN1_T61_STRING 0x14
#define SEC_ASN1_TELETEX_STRING SEC_ASN1_T61_STRING
#define SEC_ASN1_VIDEOTEX_STRING 0x15 #define SEC_ASN1_VIDEOTEX_STRING 0x15
#define SEC_ASN1_IA5_STRING 0x16 #define SEC_ASN1_IA5_STRING 0x16
#define SEC_ASN1_UTC_TIME 0x17 #define SEC_ASN1_UTC_TIME 0x17
@ -133,6 +135,7 @@ typedef struct sec_ASN1Template_struct {
/* 0x1d */ /* 0x1d */
#define SEC_ASN1_BMP_STRING 0x1e #define SEC_ASN1_BMP_STRING 0x1e
#define SEC_ASN1_HIGH_TAG_NUMBER 0x1f #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 ** Modifiers to type tags. These are also specified by a/the

Просмотреть файл

@ -41,6 +41,7 @@
#include "secitem.h" #include "secitem.h"
#include "secerr.h" #include "secerr.h"
#include "plhash.h" #include "plhash.h"
#include "nssrwlk.h"
/* MISSI Mosaic Object ID space */ /* MISSI Mosaic Object ID space */
#define USGOV 0x60, 0x86, 0x48, 0x01, 0x65 #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. * 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; /* Creates NSSRWLock and dynOidPool, if they don't exist.
static SECOidData **secoidDynamicTable = NULL; ** This function MIGHT create the lock, but not the pool, so
static int secoidDynamicTableSize = 0; ** code should test for dynOidPool, not dynOidLock, when deciding
static int secoidLastDynamicEntry = 0; ** whether or not to call this function.
static int secoidLastHashEntry = 0; */
static SECStatus static SECStatus
secoid_DynamicRehash(void) secoid_InitDynOidData(void)
{ {
SECOidData *oid; SECStatus rv = SECSuccess;
PLHashEntry *entry; NSSRWLock * lock;
int i;
int last = secoidLastDynamicEntry;
if (!oid_d_hash) { /* This function will create the lock if it doesn't exist,
oid_d_hash = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare, ** and will return the address of the lcok, whether it was
PL_CompareValues, NULL, NULL); ** 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. */
} }
PORT_Assert(lock == dynOidLock);
NSSRWLock_LockWrite(lock);
if ( !oid_d_hash ) { if (!dynOidPool) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); dynOidPool = PORT_NewArena(2048);
return(SECFailure); if (!dynOidPool) {
} rv = SECFailure /* Error code should already be set. */;
for ( i = secoidLastHashEntry; i < last; i++ ) {
oid = secoidDynamicTable[i];
entry = PL_HashTableAdd( oid_d_hash, &oid->oid, oid );
if ( entry == NULL ) {
return(SECFailure);
} }
} }
secoidLastHashEntry = last; NSSRWLock_UnlockWrite(lock);
return(SECSuccess); 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,92 +1502,141 @@ secoid_DynamicRehash(void)
* no locks.... (sigh). * no locks.... (sigh).
*/ */
static SECOidData * static SECOidData *
secoid_FindDynamic(SECItem *key) { secoid_FindDynamic(const SECItem *key)
{
SECOidData *ret = NULL; SECOidData *ret = NULL;
if (secoidDynamicTable == NULL) {
/* PORT_SetError! */
return NULL;
}
if (secoidLastHashEntry != secoidLastDynamicEntry) {
SECStatus rv = secoid_DynamicRehash();
if ( rv != SECSuccess ) {
return NULL;
}
}
ret = (SECOidData *)PL_HashTableLookup (oid_d_hash, key);
return ret;
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);
}
return ret;
} }
static SECOidData * static SECOidData *
secoid_FindDynamicByTag(SECOidTag tagnum) secoid_FindDynamicByTag(SECOidTag tagnum)
{ {
SECOidData *data = NULL;
int tagNumDiff; int tagNumDiff;
if (secoidDynamicTable == NULL) {
return NULL;
}
if (tagnum < SEC_OID_TOTAL) { if (tagnum < SEC_OID_TOTAL) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return NULL; return NULL;
} }
tagNumDiff = tagnum - SEC_OID_TOTAL; 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 * This routine is thread safe now.
* of the UI, or at init time. If we want to call it any other time,
* we need to make it thread safe.
*/ */
SECStatus SECOidTag
SECOID_AddEntry(SECItem *oid, char *description, unsigned long mech) { SECOID_AddEntry(const SECOidData * src)
SECOidData *oiddp = (SECOidData *)PORT_Alloc(sizeof(SECOidData)); {
int last = secoidLastDynamicEntry; SECOidData * dst;
int tableSize = secoidDynamicTableSize; SECOidData **table;
int next = last++; SECOidTag ret = SEC_OID_UNKNOWN;
SECOidData **newTable = secoidDynamicTable; SECStatus rv;
SECOidData **oldTable = NULL; int tableEntries;
int used;
if (oid == NULL) { if (!src || !src->oid.data || !src->oid.len || \
return SECFailure; !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 (!dynOidPool && secoid_InitDynOidData() != SECSuccess) {
if (SECITEM_CopyItem(NULL,&oiddp->oid,oid) != SECSuccess) { /* Caller has set error code. */
PORT_Free(oiddp); return ret;
return SECFailure;
} }
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) { /* We've just acquired the write lock, and now we call FindOIDTag
int oldTableSize = tableSize; ** which will acquire and release the read lock. NSSRWLock has been
tableSize += 10; ** designed to allow this very case without deadlock. This approach
oldTable = newTable; ** makes the test for the presence of the OID, and the subsequent
newTable = (SECOidData **)PORT_ZAlloc(sizeof(SECOidData *)*tableSize); ** 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) { if (newTable == NULL) {
PORT_Free(oiddp->oid.data); goto done;
PORT_Free(oiddp);
return SECFailure;
} }
PORT_Memcpy(newTable,oldTable,sizeof(SECOidData *)*oldTableSize); dynOidTable = table = newTable;
PORT_Free(oldTable); dynOidEntriesAllocated = tableEntries = newTableEntries;
} }
newTable[next] = oiddp; /* copy oid structure */
secoidDynamicTable = newTable; dst = PORT_ArenaNew(dynOidPool, SECOidData);
secoidDynamicTableSize = tableSize; if (!dst) {
secoidLastDynamicEntry= last; goto done;
return SECSuccess; }
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;
} }
@ -1583,6 +1658,10 @@ secoid_Init(void)
const SECOidData *oid; const SECOidData *oid;
int i; int i;
if (!dynOidPool && secoid_InitDynOidData() != SECSuccess) {
return SECFailure;
}
if (oidhash) { if (oidhash) {
return SECSuccess; return SECSuccess;
} }
@ -1642,7 +1721,7 @@ SECOID_FindOIDByMechanism(unsigned long mechanism)
} }
SECOidData * SECOidData *
SECOID_FindOID(SECItem *oid) SECOID_FindOID(const SECItem *oid)
{ {
SECOidData *ret; SECOidData *ret;
@ -1660,7 +1739,7 @@ SECOID_FindOID(SECItem *oid)
} }
SECOidTag SECOidTag
SECOID_FindOIDTag(SECItem *oid) SECOID_FindOIDTag(const SECItem *oid)
{ {
SECOidData *oiddata; SECOidData *oiddata;
@ -1709,8 +1788,6 @@ SECOID_FindOIDTagDescription(SECOidTag tagnum)
SECStatus SECStatus
SECOID_Shutdown(void) SECOID_Shutdown(void)
{ {
int i;
if (oidhash) { if (oidhash) {
PL_HashTableDestroy(oidhash); PL_HashTableDestroy(oidhash);
oidhash = NULL; oidhash = NULL;
@ -1719,19 +1796,43 @@ SECOID_Shutdown(void)
PL_HashTableDestroy(oidmechhash); PL_HashTableDestroy(oidmechhash);
oidmechhash = NULL; oidmechhash = NULL;
} }
if (oid_d_hash) { /* Have to handle the case where the lock was created, but
PL_HashTableDestroy(oid_d_hash); ** the pool wasn't.
oid_d_hash = NULL; ** I'm not going to attempt to create the lock, just to protect
} ** the destruction of data the probably isn't inisialized anyway.
if (secoidDynamicTable) { */
for (i=0; i < secoidLastDynamicEntry; i++) { if (dynOidLock) {
PORT_Free(secoidDynamicTable[i]); NSSRWLock_LockWrite(dynOidLock);
if (dynOidHash) {
PL_HashTableDestroy(dynOidHash);
dynOidHash = NULL;
} }
PORT_Free(secoidDynamicTable); if (dynOidPool) {
secoidDynamicTable = NULL; PORT_FreeArena(dynOidPool, PR_FALSE);
secoidDynamicTableSize = 0; dynOidPool = NULL;
secoidLastDynamicEntry = 0; }
secoidLastHashEntry = 0; 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; return SECSuccess;
} }

Просмотреть файл

@ -36,7 +36,7 @@
/* /*
* secoid.h - public data structures and prototypes for ASN.1 OID functions * 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" #include "plarena.h"
@ -55,8 +55,8 @@ SEC_ASN1_CHOOSER_DECLARE(SECOID_AlgorithmIDTemplate)
/* /*
* OID handling routines * OID handling routines
*/ */
extern SECOidData *SECOID_FindOID(SECItem *oid); extern SECOidData *SECOID_FindOID( const SECItem *oid);
extern SECOidTag SECOID_FindOIDTag(SECItem *oid); extern SECOidTag SECOID_FindOIDTag(const SECItem *oid);
extern SECOidData *SECOID_FindOIDByTag(SECOidTag tagnum); extern SECOidData *SECOID_FindOIDByTag(SECOidTag tagnum);
extern SECOidData *SECOID_FindOIDByMechanism(unsigned long mechanism); 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. ** Fill in an algorithm-ID object given a tag and some parameters.
** "aid" where the DER encoded algorithm info is stored (memory ** "aid" where the DER encoded algorithm info is stored (memory
** is allocated) ** 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 ** "params" if not NULL, the parameters to go with the algorithm
*/ */
extern SECStatus SECOID_SetAlgorithmID(PRArenaPool *arena, SECAlgorithmID *aid, extern SECStatus SECOID_SetAlgorithmID(PRArenaPool *arena, SECAlgorithmID *aid,
@ -85,7 +85,7 @@ extern SECStatus SECOID_CopyAlgorithmID(PRArenaPool *arena, SECAlgorithmID *dest
SECAlgorithmID *src); 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); extern SECOidTag SECOID_GetAlgorithmTag(SECAlgorithmID *aid);
@ -105,10 +105,16 @@ extern SECComparison SECOID_CompareAlgorithmID(SECAlgorithmID *a,
extern PRBool SECOID_KnownCertExtenOID (SECItem *extenOid); 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); 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. * free up the oid data structures.
*/ */