relyea%netscape.com dd1d27c432 Clean up compilier warnings on Solaris and Linux, most particularly:
1) Implicit declaration of function.
2) Possibly unitialized variables.

These warnings have indicated some real problems in the code, so many changes
are not just to silence the warnings, but to fix the problems. Others were
inocuous, but the warnings were silenced to reduce the noise.


git-svn-id: svn://10.0.0.236/trunk@109938 18797224-902f-48f8-a5cc-f745e15eee43
2001-12-07 01:36:25 +00:00

1616 lines
35 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.
*/
#ifdef DEBUG
static const char CVS_ID[] = "@(#) $RCSfile: oid.c,v $ $Revision: 1.4 $ $Date: 2001-12-07 01:36:10 $ $Name: not supported by cvs2svn $";
#endif /* DEBUG */
/*
* oid.c
*
* This file contains the implementation of the basic OID routines.
*/
#ifndef BASE_H
#include "base.h"
#endif /* BASE_H */
#ifndef PKI1_H
#include "pki1.h"
#endif /* PKI1_H */
#include "plhash.h"
#include "plstr.h"
/*
* NSSOID
*
* The public "methods" regarding this "object" are:
*
* NSSOID_CreateFromBER -- constructor
* NSSOID_CreateFromUTF8 -- constructor
* (there is no explicit destructor)
*
* NSSOID_GetDEREncoding
* NSSOID_GetUTF8Encoding
* The non-public "methods" regarding this "object" are:
*
* nssOID_CreateFromBER -- constructor
* nssOID_CreateFromUTF8 -- constructor
* (there is no explicit destructor)
*
* nssOID_GetDEREncoding
* nssOID_GetUTF8Encoding
*
* In debug builds, the following non-public calls are also available:
*
* nssOID_verifyPointer
* nssOID_getExplanation
* nssOID_getTaggedUTF8
*/
const NSSOID *NSS_OID_UNKNOWN = (NSSOID *)NULL;
/*
* First, the public "wrappers"
*/
/*
* NSSOID_CreateFromBER
*
* This routine creates an NSSOID by decoding a BER- or DER-encoded
* OID. It may return NULL upon error, in which case it
* will have created an error stack.
*
* The error may be one of the following values:
* NSS_ERROR_INVALID_BER
* NSS_ERROR_NO_MEMORY
*
* Return value:
* NULL upon error
* An NSSOID upon success
*/
NSS_EXTERN NSSOID *
NSSOID_CreateFromBER
(
NSSBER *berOid
)
{
nss_ClearErrorStack();
#ifdef DEBUG
/*
* NSSBERs can be created by the user,
* so no pointer-tracking can be checked.
*/
if( (NSSBER *)NULL == berOid ) {
nss_SetError(NSS_ERROR_INVALID_BER);
return (NSSOID *)NULL;
}
if( (void *)NULL == berOid->data ) {
nss_SetError(NSS_ERROR_INVALID_BER);
return (NSSOID *)NULL;
}
#endif /* DEBUG */
return nssOID_CreateFromBER(berOid);
}
/*
* NSSOID_CreateFromUTF8
*
* This routine creates an NSSOID by decoding a UTF8 string
* representation of an OID in dotted-number format. The string may
* optionally begin with an octothorpe. It may return NULL
* upon error, in which case it will have created an error stack.
*
* The error may be one of the following values:
* NSS_ERROR_INVALID_UTF8
* NSS_ERROR_NO_MEMORY
*
* Return value:
* NULL upon error
* An NSSOID upon success
*/
NSS_EXTERN NSSOID *
NSSOID_CreateFromUTF8
(
NSSUTF8 *stringOid
)
{
nss_ClearErrorStack();
#ifdef DEBUG
/*
* NSSUTF8s can be created by the user,
* so no pointer-tracking can be checked.
*/
if( (NSSUTF8 *)NULL == stringOid ) {
nss_SetError(NSS_ERROR_INVALID_UTF8);
return (NSSOID *)NULL;
}
#endif /* DEBUG */
return nssOID_CreateFromUTF8(stringOid);
}
/*
* NSSOID_GetDEREncoding
*
* This routine returns the DER encoding of the specified NSSOID.
* If the optional arena argument is non-null, the memory used will
* be obtained from that arena; otherwise, the memory will be obtained
* from the heap. This routine may return return null upon error, in
* which case it will have created an error stack.
*
* The error may be one of the following values:
* NSS_ERROR_INVALID_NSSOID
* NSS_ERROR_NO_MEMORY
*
* Return value:
* NULL upon error
* The DER encoding of this NSSOID
*/
NSS_EXTERN NSSDER *
NSSOID_GetDEREncoding
(
const NSSOID *oid,
NSSDER *rvOpt,
NSSArena *arenaOpt
)
{
nss_ClearErrorStack();
#ifdef DEBUG
if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
return (NSSDER *)NULL;
}
if( (NSSArena *)NULL != arenaOpt ) {
if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
return (NSSDER *)NULL;
}
}
#endif /* DEBUG */
return nssOID_GetDEREncoding(oid, rvOpt, arenaOpt);
}
/*
* NSSOID_GetUTF8Encoding
*
* This routine returns a UTF8 string containing the dotted-number
* encoding of the specified NSSOID. If the optional arena argument
* is non-null, the memory used will be obtained from that arena;
* otherwise, the memory will be obtained from the heap. This routine
* may return null upon error, in which case it will have created an
* error stack.
*
* The error may be one of the following values:
* NSS_ERROR_INVALID_NSSOID
* NSS_ERROR_NO_MEMORY
*
* Return value:
* NULL upon error
* A pointer to a UTF8 string containing the dotted-digit encoding of
* this NSSOID
*/
NSS_EXTERN NSSUTF8 *
NSSOID_GetUTF8Encoding
(
const NSSOID *oid,
NSSArena *arenaOpt
)
{
nss_ClearErrorStack();
#ifdef DEBUG
if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
return (NSSUTF8 *)NULL;
}
if( (NSSArena *)NULL != arenaOpt ) {
if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
return (NSSUTF8 *)NULL;
}
}
#endif /* DEBUG */
return nssOID_GetUTF8Encoding(oid, arenaOpt);
}
/*
* Next, some internal bookkeeping; including the OID "tag" table
* and the debug-version pointer tracker.
*/
/*
* For implementation reasons (so NSSOIDs can be compared with ==),
* we hash all NSSOIDs. This is the hash table.
*/
static PLHashTable *oid_hash_table;
/*
* And this is its lock.
*/
static PZLock *oid_hash_lock;
/*
* This is the hash function. We simply XOR the encoded form with
* itself in sizeof(PLHashNumber)-byte chunks. Improving this
* routine is left as an excercise for the more mathematically
* inclined student.
*/
static PLHashNumber PR_CALLBACK
oid_hash
(
const void *key
)
{
const NSSItem *item = (const NSSItem *)key;
PLHashNumber rv = 0;
PRUint8 *data = (PRUint8 *)item->data;
PRUint32 i;
PRUint8 *rvc = (PRUint8 *)&rv;
for( i = 0; i < item->size; i++ ) {
rvc[ i % sizeof(rv) ] ^= *data;
data++;
}
return rv;
}
/*
* This is the key-compare function. It simply does a lexical
* comparison on the encoded OID form. This does not result in
* quite the same ordering as the "sequence of numbers" order,
* but heck it's only used internally by the hash table anyway.
*/
static PRIntn PR_CALLBACK
oid_hash_compare
(
const void *k1,
const void *k2
)
{
PRIntn rv;
const NSSItem *i1 = (const NSSItem *)k1;
const NSSItem *i2 = (const NSSItem *)k2;
PRUint32 size = (i1->size < i2->size) ? i1->size : i2->size;
rv = (PRIntn)nsslibc_memequal(i1->data, i2->data, size, (PRStatus *)NULL);
if( 0 == rv ) {
rv = i1->size - i2->size;
}
return !rv;
}
/*
* The pointer-tracking code
*/
#ifdef DEBUG
extern const NSSError NSS_ERROR_INTERNAL_ERROR;
static nssPointerTracker oid_pointer_tracker;
static PRStatus
oid_add_pointer
(
const NSSOID *oid
)
{
PRStatus rv;
rv = nssPointerTracker_initialize(&oid_pointer_tracker);
if( PR_SUCCESS != rv ) {
return rv;
}
rv = nssPointerTracker_add(&oid_pointer_tracker, oid);
if( PR_SUCCESS != rv ) {
NSSError e = NSS_GetError();
if( NSS_ERROR_NO_MEMORY != e ) {
nss_SetError(NSS_ERROR_INTERNAL_ERROR);
}
return rv;
}
return PR_SUCCESS;
}
#if defined(CAN_DELETE_OIDS)
/*
* We actually don't define NSSOID deletion, since we keep OIDs
* in a hash table for easy comparison. Were we to, this is
* what the pointer-removal function would look like.
*/
static PRStatus
oid_remove_pointer
(
const NSSOID *oid
)
{
PRStatus rv;
rv = nssPointerTracker_remove(&oid_pointer_tracker, oid);
if( PR_SUCCESS != rv ) {
nss_SetError(NSS_ERROR_INTERNAL_ERROR);
}
return rv;
}
#endif /* CAN_DELETE_OIDS */
#endif /* DEBUG */
/*
* All dynamically-added OIDs get their memory from one statically-
* declared arena here, merely so that any cleanup code will have
* an easier time of it.
*/
static NSSArena *oid_arena;
/*
* This is the call-once function which initializes the hashtable.
* It creates it, then prepopulates it with all of the builtin OIDs.
* It also creates the aforementioned NSSArena.
*/
static PRStatus PR_CALLBACK
oid_once_func
(
void
)
{
PRUint32 i;
/* Initialize the arena */
oid_arena = nssArena_Create();
if( (NSSArena *)NULL == oid_arena ) {
goto loser;
}
/* Create the hash table lock */
oid_hash_lock = PZ_NewLock(nssILockOID);
if( (PZLock *)NULL == oid_hash_lock ) {
nss_SetError(NSS_ERROR_NO_MEMORY);
goto loser;
}
/* Create the hash table */
oid_hash_table = PL_NewHashTable(0, oid_hash, oid_hash_compare,
PL_CompareValues,
(PLHashAllocOps *)0,
(void *)0);
if( (PLHashTable *)NULL == oid_hash_table ) {
nss_SetError(NSS_ERROR_NO_MEMORY);
goto loser;
}
/* And populate it with all the builtins */
for( i = 0; i < nss_builtin_oid_count; i++ ) {
NSSOID *oid = (NSSOID *)&nss_builtin_oids[i];
PLHashEntry *e = PL_HashTableAdd(oid_hash_table, &oid->data, oid);
if( (PLHashEntry *)NULL == e ) {
nss_SetError(NSS_ERROR_NO_MEMORY);
goto loser;
}
#ifdef DEBUG
if( PR_SUCCESS != oid_add_pointer(oid) ) {
goto loser;
}
#endif /* DEBUG */
}
return PR_SUCCESS;
loser:
if( (PLHashTable *)NULL != oid_hash_table ) {
PL_HashTableDestroy(oid_hash_table);
oid_hash_table = (PLHashTable *)NULL;
}
if( (PZLock *)NULL != oid_hash_lock ) {
PZ_DestroyLock(oid_hash_lock);
oid_hash_lock = (PZLock *)NULL;
}
if( (NSSArena *)NULL != oid_arena ) {
(void)nssArena_Destroy(oid_arena);
oid_arena = (NSSArena *)NULL;
}
return PR_FAILURE;
}
/*
* This is NSPR's once-block.
*/
static PRCallOnceType oid_call_once;
/*
* And this is our multiply-callable internal init routine, which
* will call-once our call-once function.
*/
static PRStatus
oid_init
(
void
)
{
return PR_CallOnce(&oid_call_once, oid_once_func);
}
#ifdef DEBUG
/*
* nssOID_verifyPointer
*
* This method is only present in debug builds.
*
* If the specified pointer is a valid pointer to an NSSOID object,
* this routine will return PR_SUCCESS. Otherwise, it will put an
* error on the error stack and return PR_FAILURE.
*
* The error may be one of the following values:
* NSS_ERROR_INVALID_NSSOID
* NSS_ERROR_NO_MEMORY
*
* Return value:
* PR_SUCCESS if the pointer is valid
* PR_FAILURE if it isn't
*/
NSS_EXTERN PRStatus
nssOID_verifyPointer
(
const NSSOID *oid
)
{
PRStatus rv;
rv = oid_init();
if( PR_SUCCESS != rv ) {
return PR_FAILURE;
}
rv = nssPointerTracker_initialize(&oid_pointer_tracker);
if( PR_SUCCESS != rv ) {
return PR_FAILURE;
}
rv = nssPointerTracker_verify(&oid_pointer_tracker, oid);
if( PR_SUCCESS != rv ) {
nss_SetError(NSS_ERROR_INVALID_NSSOID);
return PR_FAILURE;
}
return PR_SUCCESS;
}
#endif /* DEBUG */
/*
* oid_sanity_check_ber
*
* This routine merely applies some sanity-checking to the BER-encoded
* OID.
*/
static PRStatus
oid_sanity_check_ber
(
NSSBER *berOid
)
{
PRUint32 i;
PRUint8 *data = (PRUint8 *)berOid->data;
/*
* The size must be longer than zero bytes.
*/
if( berOid->size <= 0 ) {
return PR_FAILURE;
}
/*
* In general, we can't preclude any number from showing up
* someday. We could probably guess that top-level numbers
* won't get very big (beyond the current ccitt(0), iso(1),
* or joint-ccitt-iso(2)). However, keep in mind that the
* encoding rules wrap the first two numbers together, as
*
* (first * 40) + second
*
* Also, it is noted in the specs that this implies that the
* second number won't go above forty.
*
* 128 encodes 3.8, which seems pretty safe for now. Let's
* check that the first byte is less than that.
*
* XXX This is a "soft check" -- we may want to exclude it.
*/
if( data[0] >= 0x80 ) {
return PR_FAILURE;
}
/*
* In a normalised format, leading 0x80s will never show up.
* This means that no 0x80 will be preceeded by the final
* byte of a sequence, which would naturaly be less than 0x80.
* Our internal encoding for the single-digit OIDs uses 0x80,
* but the only places we use them (loading the builtin table,
* and adding a UTF8-encoded OID) bypass this check.
*/
for( i = 1; i < berOid->size; i++ ) {
if( (0x80 == data[i]) && (data[i-1] < 0x80) ) {
return PR_FAILURE;
}
}
/*
* The high bit of each octet indicates that following octets
* are included in the current number. Thus the last byte can't
* have the high bit set.
*/
if( data[ berOid->size-1 ] >= 0x80 ) {
return PR_FAILURE;
}
/*
* Other than that, any byte sequence is legit.
*/
return PR_SUCCESS;
}
/*
* nssOID_CreateFromBER
*
* This routine creates an NSSOID by decoding a BER- or DER-encoded
* OID. It may return NULL upon error, in which case it
* will have set an error on the error stack.
*
* The error may be one of the following values:
* NSS_ERROR_INVALID_BER
* NSS_ERROR_NO_MEMORY
*
* Return value:
* NULL upon error
* An NSSOID upon success
*/
NSS_EXTERN NSSOID *
nssOID_CreateFromBER
(
NSSBER *berOid
)
{
NSSOID *rv;
PLHashEntry *e;
if( PR_SUCCESS != oid_init() ) {
return (NSSOID *)NULL;
}
if( PR_SUCCESS != oid_sanity_check_ber(berOid) ) {
nss_SetError(NSS_ERROR_INVALID_BER);
return (NSSOID *)NULL;
}
/*
* Does it exist?
*/
PZ_Lock(oid_hash_lock);
rv = (NSSOID *)PL_HashTableLookup(oid_hash_table, berOid);
(void)PZ_Unlock(oid_hash_lock);
if( (NSSOID *)NULL != rv ) {
/* Found it! */
return rv;
}
/*
* Doesn't exist-- create it.
*/
rv = nss_ZNEW(oid_arena, NSSOID);
if( (NSSOID *)NULL == rv ) {
return (NSSOID *)NULL;
}
rv->data.data = nss_ZAlloc(oid_arena, berOid->size);
if( (void *)NULL == rv->data.data ) {
return (NSSOID *)NULL;
}
rv->data.size = berOid->size;
nsslibc_memcpy(rv->data.data, berOid->data, berOid->size);
#ifdef DEBUG
rv->tag = "<runtime>";
rv->expl = "(OID registered at runtime)";
#endif /* DEBUG */
PZ_Lock(oid_hash_lock);
e = PL_HashTableAdd(oid_hash_table, &rv->data, rv);
(void)PZ_Unlock(oid_hash_lock);
if( (PLHashEntry *)NULL == e ) {
nss_ZFreeIf(rv->data.data);
nss_ZFreeIf(rv);
nss_SetError(NSS_ERROR_NO_MEMORY);
return (NSSOID *)NULL;
}
#ifdef DEBUG
{
PRStatus st;
st = oid_add_pointer(rv);
if( PR_SUCCESS != st ) {
PZ_Lock(oid_hash_lock);
(void)PL_HashTableRemove(oid_hash_table, &rv->data);
(void)PZ_Unlock(oid_hash_lock);
(void)nss_ZFreeIf(rv->data.data);
(void)nss_ZFreeIf(rv);
return (NSSOID *)NULL;
}
}
#endif /* DEBUG */
return rv;
}
/*
* oid_sanity_check_utf8
*
* This routine merely applies some sanity-checking to the
* UTF8-encoded OID.
*/
static PRStatus
oid_sanity_check_utf8
(
NSSUTF8 *s
)
{
/*
* It may begin with an octothorpe, which we skip.
*/
if( '#' == *s ) {
s++;
}
/*
* It begins with a number
*/
if( (*s < '0') || (*s > '9') ) {
return PR_FAILURE;
}
/*
* First number is only one digit long
*
* XXX This is a "soft check" -- we may want to exclude it
*/
if( (s[1] != '.') && (s[1] != '\0') ) {
return PR_FAILURE;
}
/*
* Every character is either a digit or a period
*/
for( ; '\0' != *s; s++ ) {
if( ('.' != *s) && ((*s < '0') || (*s > '9')) ) {
return PR_FAILURE;
}
/* No two consecutive periods */
if( ('.' == *s) && ('.' == s[1]) ) {
return PR_FAILURE;
}
}
/*
* The last character isn't a period
*/
if( '.' == *--s ) {
return PR_FAILURE;
}
return PR_SUCCESS;
}
static PRUint32
oid_encode_number
(
PRUint32 n,
PRUint8 *dp,
PRUint32 nb
)
{
PRUint32 a[5];
PRUint32 i;
PRUint32 rv;
a[0] = (n >> 28) & 0x7f;
a[1] = (n >> 21) & 0x7f;
a[2] = (n >> 14) & 0x7f;
a[3] = (n >> 7) & 0x7f;
a[4] = n & 0x7f;
for( i = 0; i < 5; i++ ) {
if( 0 != a[i] ) {
break;
}
}
if( 5 == i ) {
i--;
}
rv = 5-i;
if( rv > nb ) {
return rv;
}
for( ; i < 4; i++ ) {
*dp = 0x80 | a[i];
dp++;
}
*dp = a[4];
return rv;
}
/*
* oid_encode_huge
*
* This routine will convert a huge decimal number into the DER
* encoding for oid numbers. It is not limited to numbers that will
* fit into some wordsize, like oid_encode_number. But it's not
* necessarily very fast, either. This is here in case some joker
* throws us an ASCII oid like 1.2.3.99999999999999999999999999.
*/
static PRUint32
oid_encode_huge
(
NSSUTF8 *s,
NSSUTF8 *e,
PRUint8 *dp,
PRUint32 nb
)
{
PRUint32 slen = (e-s);
PRUint32 blen = (slen+1)/2;
PRUint8 *st = (PRUint8 *)NULL;
PRUint8 *bd = (PRUint8 *)NULL;
PRUint32 i;
PRUint32 bitno;
PRUint8 *last;
PRUint8 *first;
PRUint32 byteno;
PRUint8 mask;
/* We'll be munging the data, so duplicate it */
st = (PRUint8 *)nss_ZAlloc((NSSArena *)NULL, slen);
if( (PRUint8 *)NULL == st ) {
return 0;
}
/* Don't know ahead of time exactly how long we'll need */
bd = (PRUint8 *)nss_ZAlloc((NSSArena *)NULL, blen);
if( (PRUint8 *)NULL == bd ) {
(void)nss_ZFreeIf(st);
return 0;
}
/* Copy the original, and convert ASCII to numbers */
for( i = 0; i < slen; i++ ) {
st[i] = (PRUint8)(s[i] - '0');
}
last = &st[slen-1];
first = &st[0];
/*
* The way we create the binary version is by looking at it
* bit by bit. Start with the least significant bit. If the
* number is odd, set that bit. Halve the number (with integer
* division), and go to the next least significant bit. Keep
* going until the number goes to zero.
*/
for( bitno = 0; ; bitno++ ) {
PRUint8 *d;
byteno = bitno/7;
mask = (PRUint8)(1 << (bitno%7));
/* Skip leading zeroes */
for( ; first < last; first ++ ) {
if( 0 != *first ) {
break;
}
}
/* Down to one number and it's a zero? Done. */
if( (first == last) && (0 == *last) ) {
break;
}
/* Last digit is odd? Set the bit */
if( *last & 1 ) {
bd[ byteno ] |= mask;
}
/*
* Divide the number in half. This is just a matter
* of going from the least significant digit upwards,
* halving each one. If any digit is odd (other than
* the last, which has already been handled), add five
* to the digit to its right.
*/
*last /= 2;
for( d = &last[-1]; d >= first; d-- ) {
if( *d & 1 ) {
d[1] += 5;
}
*d /= 2;
}
}
/* Is there room to write the encoded data? */
if( (byteno+1) > nb ) {
return (byteno+1);
}
/* Trim any leading zero that crept in there */
for( ; byteno > 0; byteno-- ) {
if( 0 != bd[ byteno ] ) {
break;
}
}
/* Copy all but the last, marking the "continue" bit */
for( i = 0; i < byteno; i++ ) {
dp[i] = bd[ byteno-i ] | 0x80;
}
/* And the last with the "continue" bit clear */
dp[byteno] = bd[0];
(void)nss_ZFreeIf(bd);
(void)nss_ZFreeIf(st);
return (byteno+1);
}
/*
* oid_encode_string
*
* This routine converts a dotted-number OID into a DER-encoded
* one. It assumes we've already sanity-checked the string.
*/
extern const NSSError NSS_ERROR_INTERNAL_ERROR;
static NSSOID *
oid_encode_string
(
NSSUTF8 *s
)
{
PRUint32 nn = 0; /* number of numbers */
PRUint32 nb = 0; /* number of bytes (estimated) */
NSSUTF8 *t;
PRUint32 nd = 0; /* number of digits */
NSSOID *rv;
PRUint8 *dp;
PRUint32 a, b;
PRUint32 inc;
/* Dump any octothorpe */
if( '#' == *s ) {
s++;
}
/* Count up the bytes needed */
for( t = s; '\0' != *t; t++ ) {
if( '.' == *t ) {
nb += (nd+1)/2; /* errs on the big side */
nd = 0;
nn++;
} else {
nd++;
}
}
nb += (nd+1)/2;
nn++;
if( 1 == nn ) {
/*
* We have our own "denormalised" encoding for these,
* which is only used internally.
*/
nb++;
}
/*
* Allocate. Note that we don't use the oid_arena here.. this is
* because there really isn't a "free()" for stuff allocated out of
* arenas (at least with the current implementation), so this would
* keep using up memory each time a UTF8-encoded OID were added.
* If need be (if this is the first time this oid has been seen),
* we'll copy it.
*/
rv = nss_ZNEW((NSSArena *)NULL, NSSOID);
if( (NSSOID *)NULL == rv ) {
return (NSSOID *)NULL;
}
rv->data.data = nss_ZAlloc((NSSArena *)NULL, nb);
if( (void *)NULL == rv->data.data ) {
(void)nss_ZFreeIf(rv);
return (NSSOID *)NULL;
}
dp = (PRUint8 *)rv->data.data;
a = atoi(s);
if( 1 == nn ) {
dp[0] = '\x80';
inc = oid_encode_number(a, &dp[1], nb-1);
if( inc >= nb ) {
goto loser;
}
} else {
for( t = s; '.' != *t; t++ ) {
;
}
t++;
b = atoi(t);
inc = oid_encode_number(a*40+b, dp, nb);
if( inc > nb ) {
goto loser;
}
dp += inc;
nb -= inc;
nn -= 2;
while( nn-- > 0 ) {
NSSUTF8 *u;
for( ; '.' != *t; t++ ) {
;
}
t++;
for( u = t; ('\0' != *u) && ('.' != *u); u++ ) {
;
}
if( (u-t > 9) ) {
/* In the billions. Rats. */
inc = oid_encode_huge(t, u, dp, nb);
} else {
b = atoi(t);
inc = oid_encode_number(b, dp, nb);
}
if( inc > nb ) {
goto loser;
}
dp += inc;
nb -= inc;
}
}
return rv;
loser:
nss_SetError(NSS_ERROR_INTERNAL_ERROR);
return (NSSOID *)NULL;
}
/*
* nssOID_CreateFromUTF8
*
* This routine creates an NSSOID by decoding a UTF8 string
* representation of an OID in dotted-number format. The string may
* optionally begin with an octothorpe. It may return NULL
* upon error, in which case it will have set an error on the error
* stack.
*
* The error may be one of the following values:
* NSS_ERROR_INVALID_STRING
* NSS_ERROR_NO_MEMORY
*
* Return value:
* NULL upon error
* An NSSOID upon success
*/
NSS_EXTERN NSSOID *
nssOID_CreateFromUTF8
(
NSSUTF8 *stringOid
)
{
NSSOID *rv = (NSSOID *)NULL;
NSSOID *candidate = (NSSOID *)NULL;
PLHashEntry *e;
if( PR_SUCCESS != oid_init() ) {
return (NSSOID *)NULL;
}
if( PR_SUCCESS != oid_sanity_check_utf8(stringOid) ) {
nss_SetError(NSS_ERROR_INVALID_STRING);
return (NSSOID *)NULL;
}
candidate = oid_encode_string(stringOid);
if( (NSSOID *)NULL == candidate ) {
/* Internal error only */
return rv;
}
/*
* Does it exist?
*/
PZ_Lock(oid_hash_lock);
rv = (NSSOID *)PL_HashTableLookup(oid_hash_table, &candidate->data);
(void)PZ_Unlock(oid_hash_lock);
if( (NSSOID *)NULL != rv ) {
/* Already exists. Delete my copy and return the original. */
(void)nss_ZFreeIf(candidate->data.data);
(void)nss_ZFreeIf(candidate);
return rv;
}
/*
* Nope. Add it. Remember to allocate it out of the oid arena.
*/
rv = nss_ZNEW(oid_arena, NSSOID);
if( (NSSOID *)NULL == rv ) {
goto loser;
}
rv->data.data = nss_ZAlloc(oid_arena, candidate->data.size);
if( (void *)NULL == rv->data.data ) {
goto loser;
}
rv->data.size = candidate->data.size;
nsslibc_memcpy(rv->data.data, candidate->data.data, rv->data.size);
(void)nss_ZFreeIf(candidate->data.data);
(void)nss_ZFreeIf(candidate);
#ifdef DEBUG
rv->tag = "<runtime>";
rv->expl = "(OID registered at runtime)";
#endif /* DEBUG */
PZ_Lock(oid_hash_lock);
e = PL_HashTableAdd(oid_hash_table, &rv->data, rv);
(void)PZ_Unlock(oid_hash_lock);
if( (PLHashEntry *)NULL == e ) {
nss_SetError(NSS_ERROR_NO_MEMORY);
goto loser;
}
#ifdef DEBUG
{
PRStatus st;
st = oid_add_pointer(rv);
if( PR_SUCCESS != st ) {
PZ_Lock(oid_hash_lock);
(void)PL_HashTableRemove(oid_hash_table, &rv->data);
(void)PZ_Unlock(oid_hash_lock);
goto loser;
}
}
#endif /* DEBUG */
return rv;
loser:
if( (NSSOID *)NULL != candidate ) {
(void)nss_ZFreeIf(candidate->data.data);
}
(void)nss_ZFreeIf(candidate);
if( (NSSOID *)NULL != rv ) {
(void)nss_ZFreeIf(rv->data.data);
}
(void)nss_ZFreeIf(rv);
return (NSSOID *)NULL;
}
/*
* nssOID_GetDEREncoding
*
* This routine returns the DER encoding of the specified NSSOID.
* If the optional arena argument is non-null, the memory used will
* be obtained from that arena; otherwise, the memory will be obtained
* from the heap. This routine may return return null upon error, in
* which case it will have set an error on the error stack.
*
* The error may be one of the following values:
* NSS_ERROR_INVALID_OID
* NSS_ERROR_NO_MEMORY
*
* Return value:
* NULL upon error
* The DER encoding of this NSSOID
*/
NSS_EXTERN NSSDER *
nssOID_GetDEREncoding
(
const NSSOID *oid,
NSSDER *rvOpt,
NSSArena *arenaOpt
)
{
const NSSItem *it;
NSSDER *rv;
if( PR_SUCCESS != oid_init() ) {
return (NSSDER *)NULL;
}
#ifdef NSSDEBUG
if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
return (NSSDER *)NULL;
}
if( (NSSArena *)NULL != arenaOpt ) {
if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
return (NSSDER *)NULL;
}
}
#endif /* NSSDEBUG */
it = &oid->data;
if( (NSSDER *)NULL == rvOpt ) {
rv = nss_ZNEW(arenaOpt, NSSDER);
if( (NSSDER *)NULL == rv ) {
return (NSSDER *)NULL;
}
} else {
rv = rvOpt;
}
rv->data = nss_ZAlloc(arenaOpt, it->size);
if( (void *)NULL == rv->data ) {
if( rv != rvOpt ) {
(void)nss_ZFreeIf(rv);
}
return (NSSDER *)NULL;
}
rv->size = it->size;
nsslibc_memcpy(rv->data, it->data, it->size);
return rv;
}
/*
* nssOID_GetUTF8Encoding
*
* This routine returns a UTF8 string containing the dotted-number
* encoding of the specified NSSOID. If the optional arena argument
* is non-null, the memory used will be obtained from that arena;
* otherwise, the memory will be obtained from the heap. This routine
* may return null upon error, in which case it will have set an error
* on the error stack.
*
* The error may be one of the following values:
* NSS_ERROR_INVALID_OID
* NSS_ERROR_NO_MEMORY
*
* Return value:
* NULL upon error
* A pointer to a UTF8 string containing the dotted-digit encoding of
* this NSSOID
*/
NSS_EXTERN NSSUTF8 *
nssOID_GetUTF8Encoding
(
const NSSOID *oid,
NSSArena *arenaOpt
)
{
NSSUTF8 *rv;
PRUint8 *end;
PRUint8 *d;
PRUint8 *e;
char *a;
char *b;
PRUint32 len;
if( PR_SUCCESS != oid_init() ) {
return (NSSUTF8 *)NULL;
}
#ifdef NSSDEBUG
if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
return (NSSUTF8 *)NULL;
}
if( (NSSArena *)NULL != arenaOpt ) {
if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
return (NSSUTF8 *)NULL;
}
}
#endif /* NSSDEBUG */
a = (char *)NULL;
/* d will point to the next sequence of bytes to decode */
d = (PRUint8 *)oid->data.data;
/* end points to one past the legitimate data */
end = &d[ oid->data.size ];
#ifdef NSSDEBUG
/*
* Guarantee that the for(e=d;e<end;e++) loop below will
* terminate. Our BER sanity-checking code above will prevent
* such a BER from being registered, so the only other way one
* might show up is if our dotted-decimal encoder above screws
* up or our generated list is wrong. So I'll wrap it with
* #ifdef NSSDEBUG and #endif.
*/
if( end[-1] & 0x80 ) {
nss_SetError(NSS_ERROR_INTERNAL_ERROR);
return (NSSUTF8 *)NULL;
}
#endif /* NSSDEBUG */
/*
* Check for our pseudo-encoded single-digit OIDs
*/
if( (*d == 0x80) && (2 == oid->data.size) ) {
/* Funky encoding. The second byte is the number */
a = PR_smprintf("%lu", (PRUint32)d[1]);
if( (char *)NULL == a ) {
nss_SetError(NSS_ERROR_NO_MEMORY);
return (NSSUTF8 *)NULL;
}
goto done;
}
for( ; d < end; d = &e[1] ) {
for( e = d; e < end; e++ ) {
if( 0 == (*e & 0x80) ) {
break;
}
}
if( ((e-d) > 4) || (((e-d) == 4) && (*d & 0x70)) ) {
/* More than a 32-bit number */
} else {
PRUint32 n = 0;
switch( e-d ) {
case 4:
n |= ((PRUint32)(e[-4] & 0x0f)) << 28;
case 3:
n |= ((PRUint32)(e[-3] & 0x7f)) << 21;
case 2:
n |= ((PRUint32)(e[-2] & 0x7f)) << 14;
case 1:
n |= ((PRUint32)(e[-1] & 0x7f)) << 7;
case 0:
n |= ((PRUint32)(e[-0] & 0x7f)) ;
}
if( (char *)NULL == a ) {
/* This is the first number.. decompose it */
PRUint32 one = (n/40), two = (n%40);
a = PR_smprintf("%lu.%lu", one, two);
if( (char *)NULL == a ) {
nss_SetError(NSS_ERROR_NO_MEMORY);
return (NSSUTF8 *)NULL;
}
} else {
b = PR_smprintf("%s.%lu", a, n);
if( (char *)NULL == b ) {
PR_smprintf_free(a);
nss_SetError(NSS_ERROR_NO_MEMORY);
return (NSSUTF8 *)NULL;
}
PR_smprintf_free(a);
a = b;
}
}
}
done:
/*
* Even if arenaOpt is NULL, we have to copy the data so that
* it'll be freed with the right version of free: ours, not
* PR_smprintf_free's.
*/
len = PL_strlen(a);
rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len);
if( (NSSUTF8 *)NULL == rv ) {
PR_smprintf_free(a);
return (NSSUTF8 *)NULL;
}
nsslibc_memcpy(rv, a, len);
PR_smprintf_free(a);
return rv;
}
/*
* nssOID_getExplanation
*
* This method is only present in debug builds.
*
* This routine will return a static pointer to a UTF8-encoded string
* describing (in English) the specified OID. The memory pointed to
* by the return value is not owned by the caller, and should not be
* freed or modified. Note that explanations are only provided for
* the OIDs built into the NSS library; there is no way to specify an
* explanation for dynamically created OIDs. This routine is intended
* only for use in debugging tools such as "derdump." This routine
* may return null upon error, in which case it will have placed an
* error on the error stack.
*
* The error may be one of the following values:
* NSS_ERROR_INVALID_NSSOID
*
* Return value:
* NULL upon error
* A static pointer to a readonly, non-caller-owned UTF8-encoded
* string explaining the specified OID.
*/
#ifdef DEBUG
NSS_EXTERN const NSSUTF8 *
nssOID_getExplanation
(
NSSOID *oid
)
{
if( PR_SUCCESS != oid_init() ) {
return (const NSSUTF8 *)NULL;
}
#ifdef NSSDEBUG
if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
return (NSSUTF8 *)NULL;
}
#endif /* NSSDEBUG */
return oid->expl;
}
extern const NSSError NSS_ERROR_INVALID_NSSOID;
#endif /* DEBUG */
/*
* nssOID_getTaggedUTF8
*
* This method is only present in debug builds.
*
* This routine will return a pointer to a caller-owned UTF8-encoded
* string containing a tagged encoding of the specified OID. Note
* that OID (component) tags are only provided for the OIDs built
* into the NSS library; there is no way to specify tags for
* dynamically created OIDs. This routine is intended for use in
* debugging tools such as "derdump." If the optional arena argument
* is non-null, the memory used will be obtained from that arena;
* otherwise, the memory will be obtained from the heap. This routine
* may return return null upon error, in which case it will have set
* an error on the error stack.
*
* The error may be one of the following values
* NSS_ERROR_INVALID_NSSOID
* NSS_ERROR_NO_MEMORY
*
* Return value:
* NULL upon error
* A pointer to a UTF8 string containing the tagged encoding of
* this NSSOID
*/
#ifdef DEBUG
NSS_EXTERN NSSUTF8 *
nssOID_getTaggedUTF8
(
NSSOID *oid,
NSSArena *arenaOpt
)
{
NSSUTF8 *rv;
char *raw;
char *c;
char *a = (char *)NULL;
char *b;
PRBool done = PR_FALSE;
PRUint32 len;
if( PR_SUCCESS != oid_init() ) {
return (NSSUTF8 *)NULL;
}
#ifdef NSSDEBUG
if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
return (NSSUTF8 *)NULL;
}
if( (NSSArena *)NULL != arenaOpt ) {
if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
return (NSSUTF8 *)NULL;
}
}
#endif /* NSSDEBUG */
a = PR_smprintf("{");
if( (char *)NULL == a ) {
nss_SetError(NSS_ERROR_NO_MEMORY);
return (NSSUTF8 *)NULL;
}
/*
* What I'm doing here is getting the text version of the OID,
* e.g. 1.2.12.92, then looking up each set of leading numbers
* as oids.. e.g. "1," then "1.2," then "1.2.12," etc. Each of
* those will have the leaf tag, and I just build up the string.
* I never said this was the most efficient way of doing it,
* but hey it's a debug-build thing, and I'm getting really tired
* of writing this stupid low-level PKI code.
*/
/* I know it's all ASCII, so I can use char */
raw = (char *)nssOID_GetUTF8Encoding(oid, (NSSArena *)NULL);
if( (char *)NULL == raw ) {
return (NSSUTF8 *)NULL;
}
for( c = raw; !done; c++ ) {
NSSOID *lead;
char *lastdot;
for( ; '.' != *c; c++ ) {
if( '\0' == *c ) {
done = PR_TRUE;
break;
}
}
*c = '\0';
lead = nssOID_CreateFromUTF8((NSSUTF8 *)raw);
if( (NSSOID *)NULL == lead ) {
PR_smprintf_free(a);
nss_ZFreeIf(raw);
nss_SetError(NSS_ERROR_NO_MEMORY);
return (NSSUTF8 *)NULL;
}
lastdot = PL_strrchr(raw, '.');
if( (char *)NULL == lastdot ) {
lastdot = raw;
}
b = PR_smprintf("%s %s(%s) ", a, lead->tag, &lastdot[1]);
if( (char *)NULL == b ) {
PR_smprintf_free(a);
nss_ZFreeIf(raw);
/* drop the OID reference on the floor */
nss_SetError(NSS_ERROR_NO_MEMORY);
return (NSSUTF8 *)NULL;
}
PR_smprintf_free(a);
a = b;
if( !done ) {
*c = '.';
}
}
nss_ZFreeIf(raw);
b = PR_smprintf("%s }", a);
if( (char *)NULL == b ) {
PR_smprintf_free(a);
nss_SetError(NSS_ERROR_NO_MEMORY);
return (NSSUTF8 *)NULL;
}
len = PL_strlen(b);
rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len+1);
if( (NSSUTF8 *)NULL == rv ) {
PR_smprintf_free(b);
return (NSSUTF8 *)NULL;
}
nsslibc_memcpy(rv, b, len);
PR_smprintf_free(b);
return rv;
}
extern const NSSError NSS_ERROR_INVALID_NSSOID;
extern const NSSError NSS_ERROR_NO_MEMORY;
#endif /* DEBUG */