diff options
Diffstat (limited to 'security/nss/lib/certdb')
23 files changed, 14582 insertions, 0 deletions
diff --git a/security/nss/lib/certdb/.cvsignore b/security/nss/lib/certdb/.cvsignore new file mode 100644 index 000000000..ec60123e5 --- /dev/null +++ b/security/nss/lib/certdb/.cvsignore @@ -0,0 +1 @@ +nscertinit.c diff --git a/security/nss/lib/certdb/Makefile b/security/nss/lib/certdb/Makefile new file mode 100644 index 000000000..12eff17ab --- /dev/null +++ b/security/nss/lib/certdb/Makefile @@ -0,0 +1,76 @@ +#! gmake +# +# 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. +# + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include config.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + +export:: private_export + diff --git a/security/nss/lib/certdb/alg1485.c b/security/nss/lib/certdb/alg1485.c new file mode 100644 index 000000000..e414f4eb4 --- /dev/null +++ b/security/nss/lib/certdb/alg1485.c @@ -0,0 +1,1248 @@ +/* + * 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. + */ + +#include "prprf.h" +#include "cert.h" +#include "xconst.h" +#include "genname.h" +#include "secitem.h" +#include "secerr.h" + +struct NameToKind { + char *name; + SECOidTag kind; +}; + +static struct NameToKind name2kinds[] = { + { "CN", SEC_OID_AVA_COMMON_NAME, }, + { "ST", SEC_OID_AVA_STATE_OR_PROVINCE, }, + { "OU", SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, }, + { "DC", SEC_OID_AVA_DC, }, + { "C", SEC_OID_AVA_COUNTRY_NAME, }, + { "O", SEC_OID_AVA_ORGANIZATION_NAME, }, + { "L", SEC_OID_AVA_LOCALITY, }, + { "dnQualifier", SEC_OID_AVA_DN_QUALIFIER, }, + { "E", SEC_OID_PKCS9_EMAIL_ADDRESS, }, + { "UID", SEC_OID_RFC1274_UID, }, + { "MAIL", SEC_OID_RFC1274_MAIL, }, + { 0, SEC_OID_UNKNOWN }, +}; + +#define C_DOUBLE_QUOTE '\042' + +#define C_BACKSLASH '\134' + +#define C_EQUAL '=' + +#define OPTIONAL_SPACE(c) \ + (((c) == ' ') || ((c) == '\r') || ((c) == '\n')) + +#define SPECIAL_CHAR(c) \ + (((c) == ',') || ((c) == '=') || ((c) == C_DOUBLE_QUOTE) || \ + ((c) == '\r') || ((c) == '\n') || ((c) == '+') || \ + ((c) == '<') || ((c) == '>') || ((c) == '#') || \ + ((c) == ';') || ((c) == C_BACKSLASH)) + + + + + + + +#if 0 +/* +** Find the start and end of a <string>. Strings can be wrapped in double +** quotes to protect special characters. +*/ +static int BracketThing(char **startp, char *end, char *result) +{ + char *start = *startp; + char c; + + /* Skip leading white space */ + while (start < end) { + c = *start++; + if (!OPTIONAL_SPACE(c)) { + start--; + break; + } + } + if (start == end) return 0; + + switch (*start) { + case '#': + /* Process hex thing */ + start++; + *startp = start; + while (start < end) { + c = *start++; + if (((c >= '0') && (c <= '9')) || + ((c >= 'a') && (c <= 'f')) || + ((c >= 'A') && (c <= 'F'))) { + continue; + } + break; + } + rv = IS_HEX; + break; + + case C_DOUBLE_QUOTE: + start++; + *startp = start; + while (start < end) { + c = *start++; + if (c == C_DOUBLE_QUOTE) { + break; + } + *result++ = c; + } + rv = IS_STRING; + break; + + default: + while (start < end) { + c = *start++; + if (SPECIAL_CHAR(c)) { + start--; + break; + } + *result++ = c; + } + rv = IS_STRING; + break; + } + + /* Terminate result string */ + *result = 0; + return start; +} + +static char *BracketSomething(char **startp, char* end, int spacesOK) +{ + char *start = *startp; + char c; + int stopAtDQ; + + /* Skip leading white space */ + while (start < end) { + c = *start; + if (!OPTIONAL_SPACE(c)) { + break; + } + start++; + } + if (start == end) return 0; + stopAtDQ = 0; + if (*start == C_DOUBLE_QUOTE) { + stopAtDQ = 1; + } + + /* + ** Find the end of the something. The something is terminated most of + ** the time by a space. However, if spacesOK is true then it is + ** terminated by a special character only. + */ + *startp = start; + while (start < end) { + c = *start; + if (stopAtDQ) { + if (c == C_DOUBLE_QUOTE) { + *start = ' '; + break; + } + } else { + if (SPECIAL_CHAR(c)) { + break; + } + if (!spacesOK && OPTIONAL_SPACE(c)) { + break; + } + } + start++; + } + return start; +} +#endif + +#define IS_PRINTABLE(c) \ + ((((c) >= 'a') && ((c) <= 'z')) || \ + (((c) >= 'A') && ((c) <= 'Z')) || \ + (((c) >= '0') && ((c) <= '9')) || \ + ((c) == ' ') || \ + ((c) == '\'') || \ + ((c) == '\050') || /* ( */ \ + ((c) == '\051') || /* ) */ \ + (((c) >= '+') && ((c) <= '/')) || /* + , - . / */ \ + ((c) == ':') || \ + ((c) == '=') || \ + ((c) == '?')) + +static PRBool +IsPrintable(unsigned char *data, unsigned len) +{ + unsigned char ch, *end; + + end = data + len; + while (data < end) { + ch = *data++; + if (!IS_PRINTABLE(ch)) { + return PR_FALSE; + } + } + return PR_TRUE; +} + +static PRBool +Is7Bit(unsigned char *data, unsigned len) +{ + unsigned char ch, *end; + + end = data + len; + while (data < end) { + ch = *data++; + if ((ch & 0x80)) { + return PR_FALSE; + } + } + return PR_TRUE; +} + +static void +skipSpace(char **pbp, char *endptr) +{ + char *bp = *pbp; + while (bp < endptr && OPTIONAL_SPACE(*bp)) { + bp++; + } + *pbp = bp; +} + +static SECStatus +scanTag(char **pbp, char *endptr, char *tagBuf, int tagBufSize) +{ + char *bp, *tagBufp; + int taglen; + + PORT_Assert(tagBufSize > 0); + + /* skip optional leading space */ + skipSpace(pbp, endptr); + if (*pbp == endptr) { + /* nothing left */ + return SECFailure; + } + + /* fill tagBuf */ + taglen = 0; + bp = *pbp; + tagBufp = tagBuf; + while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) { + if (++taglen >= tagBufSize) { + *pbp = bp; + return SECFailure; + } + *tagBufp++ = *bp++; + } + /* null-terminate tagBuf -- guaranteed at least one space left */ + *tagBufp++ = 0; + *pbp = bp; + + /* skip trailing spaces till we hit something - should be an equal sign */ + skipSpace(pbp, endptr); + if (*pbp == endptr) { + /* nothing left */ + return SECFailure; + } + if (**pbp != C_EQUAL) { + /* should be an equal sign */ + return SECFailure; + } + /* skip over the equal sign */ + (*pbp)++; + + return SECSuccess; +} + +static SECStatus +scanVal(char **pbp, char *endptr, char *valBuf, int valBufSize) +{ + char *bp, *valBufp; + int vallen; + PRBool isQuoted; + + PORT_Assert(valBufSize > 0); + + /* skip optional leading space */ + skipSpace(pbp, endptr); + if(*pbp == endptr) { + /* nothing left */ + return SECFailure; + } + + bp = *pbp; + + /* quoted? */ + if (*bp == C_DOUBLE_QUOTE) { + isQuoted = PR_TRUE; + /* skip over it */ + bp++; + } else { + isQuoted = PR_FALSE; + } + + valBufp = valBuf; + vallen = 0; + while (bp < endptr) { + char c = *bp; + if (c == C_BACKSLASH) { + /* escape character */ + bp++; + if (bp >= endptr) { + /* escape charater must appear with paired char */ + *pbp = bp; + return SECFailure; + } + } else if (!isQuoted && SPECIAL_CHAR(c)) { + /* unescaped special and not within quoted value */ + break; + } else if (c == C_DOUBLE_QUOTE) { + /* reached unescaped double quote */ + break; + } + /* append character */ + vallen++; + if (vallen >= valBufSize) { + *pbp = bp; + return SECFailure; + } + *valBufp++ = *bp++; + } + + /* stip trailing spaces from unquoted values */ + if (!isQuoted) { + if (valBufp > valBuf) { + valBufp--; + while ((valBufp > valBuf) && OPTIONAL_SPACE(*valBufp)) { + valBufp--; + } + valBufp++; + } + } + + if (isQuoted) { + /* insist that we stopped on a double quote */ + if (*bp != C_DOUBLE_QUOTE) { + *pbp = bp; + return SECFailure; + } + /* skip over the quote and skip optional space */ + bp++; + skipSpace(&bp, endptr); + } + + *pbp = bp; + + if (valBufp == valBuf) { + /* empty value -- not allowed */ + return SECFailure; + } + + /* null-terminate valBuf -- guaranteed at least one space left */ + *valBufp++ = 0; + + return SECSuccess; +} + +CERTAVA * +CERT_ParseRFC1485AVA(PRArenaPool *arena, char **pbp, char *endptr, + PRBool singleAVA) +{ + CERTAVA *a; + struct NameToKind *n2k; + int vt; + int valLen; + char *bp; + + char tagBuf[32]; + char valBuf[384]; + + if (scanTag(pbp, endptr, tagBuf, sizeof(tagBuf)) == SECFailure || + scanVal(pbp, endptr, valBuf, sizeof(valBuf)) == SECFailure) { + PORT_SetError(SEC_ERROR_INVALID_AVA); + return 0; + } + + /* insist that if we haven't finished we've stopped on a separator */ + bp = *pbp; + if (bp < endptr) { + if (singleAVA || (*bp != ',' && *bp != ';')) { + PORT_SetError(SEC_ERROR_INVALID_AVA); + *pbp = bp; + return 0; + } + /* ok, skip over separator */ + bp++; + } + *pbp = bp; + + for (n2k = name2kinds; n2k->name; n2k++) { + if (PORT_Strcasecmp(n2k->name, tagBuf) == 0) { + valLen = PORT_Strlen(valBuf); + if (n2k->kind == SEC_OID_AVA_COUNTRY_NAME) { + vt = SEC_ASN1_PRINTABLE_STRING; + if (valLen != 2) { + PORT_SetError(SEC_ERROR_INVALID_AVA); + return 0; + } + if (!IsPrintable((unsigned char*) valBuf, 2)) { + PORT_SetError(SEC_ERROR_INVALID_AVA); + return 0; + } + } else if ((n2k->kind == SEC_OID_PKCS9_EMAIL_ADDRESS) || + (n2k->kind == SEC_OID_RFC1274_MAIL)) { + vt = SEC_ASN1_IA5_STRING; + } else { + /* Hack -- for rationale see X.520 DirectoryString defn */ + if (IsPrintable((unsigned char*)valBuf, valLen)) { + vt = SEC_ASN1_PRINTABLE_STRING; + } else if (Is7Bit((unsigned char *)valBuf, valLen)) { + vt = SEC_ASN1_T61_STRING; + } else { + vt = SEC_ASN1_UNIVERSAL_STRING; + } + } + a = CERT_CreateAVA(arena, n2k->kind, vt, (char *) valBuf); + return a; + } + } + /* matched no kind -- invalid tag */ + PORT_SetError(SEC_ERROR_INVALID_AVA); + return 0; +} + +static CERTName * +ParseRFC1485Name(char *buf, int len) +{ + SECStatus rv; + CERTName *name; + char *bp, *e; + CERTAVA *ava; + CERTRDN *rdn; + + name = CERT_CreateName(NULL); + if (name == NULL) { + return NULL; + } + + e = buf + len; + bp = buf; + while (bp < e) { + ava = CERT_ParseRFC1485AVA(name->arena, &bp, e, PR_FALSE); + if (ava == 0) goto loser; + rdn = CERT_CreateRDN(name->arena, ava, 0); + if (rdn == 0) goto loser; + rv = CERT_AddRDN(name, rdn); + if (rv) goto loser; + skipSpace(&bp, e); + } + + if (name->rdns[0] == 0) { + /* empty name -- illegal */ + goto loser; + } + + /* Reverse order of RDNS to comply with RFC */ + { + CERTRDN **firstRdn; + CERTRDN **lastRdn; + CERTRDN *tmp; + + /* get first one */ + firstRdn = name->rdns; + + /* find last one */ + lastRdn = name->rdns; + while (*lastRdn) lastRdn++; + lastRdn--; + + /* reverse list */ + for ( ; firstRdn < lastRdn; firstRdn++, lastRdn--) { + tmp = *firstRdn; + *firstRdn = *lastRdn; + *lastRdn = tmp; + } + } + + /* return result */ + return name; + + loser: + CERT_DestroyName(name); + return NULL; +} + +CERTName * +CERT_AsciiToName(char *string) +{ + CERTName *name; + name = ParseRFC1485Name(string, PORT_Strlen(string)); + return name; +} + +/************************************************************************/ + +typedef struct stringBufStr { + char *buffer; + unsigned offset; + unsigned size; +} stringBuf; + +#define DEFAULT_BUFFER_SIZE 200 +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +static SECStatus +AppendStr(stringBuf *bufp, char *str) +{ + char *buf; + unsigned bufLen, bufSize, len; + int size = 0; + + /* Figure out how much to grow buf by (add in the '\0') */ + buf = bufp->buffer; + bufLen = bufp->offset; + len = PORT_Strlen(str); + bufSize = bufLen + len; + if (!buf) { + bufSize++; + size = MAX(DEFAULT_BUFFER_SIZE,bufSize*2); + buf = (char *) PORT_Alloc(size); + bufp->size = size; + } else if (bufp->size < bufSize) { + size = bufSize*2; + buf =(char *) PORT_Realloc(buf,size); + bufp->size = size; + } + if (!buf) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + bufp->buffer = buf; + bufp->offset = bufSize; + + /* Concatenate str onto buf */ + buf = buf + bufLen; + if (bufLen) buf--; /* stomp on old '\0' */ + PORT_Memcpy(buf, str, len+1); /* put in new null */ + return SECSuccess; +} + +SECStatus +CERT_RFC1485_EscapeAndQuote(char *dst, int dstlen, char *src, int srclen) +{ + int i, reqLen=0; + char *d = dst; + PRBool needsQuoting = PR_FALSE; + + /* need to make an initial pass to determine if quoting is needed */ + for (i = 0; i < srclen; i++) { + char c = src[i]; + reqLen++; + if (SPECIAL_CHAR(c)) { + /* entirety will need quoting */ + needsQuoting = PR_TRUE; + } + if (c == C_DOUBLE_QUOTE || c == C_BACKSLASH) { + /* this char will need escaping */ + reqLen++; + } + } + /* if it begins or ends in optional space it needs quoting */ + if (srclen > 0 && + (OPTIONAL_SPACE(src[srclen-1]) || OPTIONAL_SPACE(src[0]))) { + needsQuoting = PR_TRUE; + } + + if (needsQuoting) reqLen += 2; + + /* space for terminal null */ + reqLen++; + + if (reqLen > dstlen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + + d = dst; + if (needsQuoting) *d++ = C_DOUBLE_QUOTE; + for (i = 0; i < srclen; i++) { + char c = src[i]; + if (c == C_DOUBLE_QUOTE || c == C_BACKSLASH) { + /* escape it */ + *d++ = C_BACKSLASH; + } + *d++ = c; + } + if (needsQuoting) *d++ = C_DOUBLE_QUOTE; + *d++ = 0; + return SECSuccess; +} + +/* convert an OID to dotted-decimal representation */ +static char * +get_oid_string +( + SECItem *oid +) +{ + PRUint8 *end; + PRUint8 *d; + PRUint8 *e; + char *a; + char *b; + + a = (char *)NULL; + + /* d will point to the next sequence of bytes to decode */ + d = (PRUint8 *)oid->data; + /* end points to one past the legitimate data */ + end = &d[ oid->len ]; + + /* + * Check for our pseudo-encoded single-digit OIDs + */ + if( (*d == 0x80) && (2 == oid->len) ) { + /* Funky encoding. The second byte is the number */ + a = PR_smprintf("%lu", (PRUint32)d[1]); + if( (char *)NULL == a ) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return (char *)NULL; + } + return a; + } + + 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 = PR_MIN(n/40, 2); /* never > 2 */ + PRUint32 two = n - one * 40; + + a = PR_smprintf("%lu.%lu", one, two); + if( (char *)NULL == a ) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return (char *)NULL; + } + } else { + b = PR_smprintf("%s.%lu", a, n); + if( (char *)NULL == b ) { + PR_smprintf_free(a); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return (char *)NULL; + } + + PR_smprintf_free(a); + a = b; + } + } + } + + return a; +} + +/* convert DER-encoded hex to a string */ +static SECItem * +get_hex_string(SECItem *data) +{ + SECItem *rv; + unsigned int i, j; + static const char hex[] = { "0123456789ABCDEF" }; + + /* '#' + 2 chars per octet + terminator */ + rv = SECITEM_AllocItem(NULL, NULL, data->len*2 + 2); + if (!rv) { + return NULL; + } + rv->data[0] = '#'; + rv->len = 1 + 2 * data->len; + for (i=0; i<data->len; i++) { + j = data->data[i]; + rv->data[2*i+1] = hex[j >> 4]; + rv->data[2*i+2] = hex[j & 15]; + } + rv->data[rv->len] = 0; + return rv; +} + +static SECStatus +AppendAVA(stringBuf *bufp, CERTAVA *ava) +{ + char *tagName; + char tmpBuf[384]; + unsigned len, maxLen; + int tag; + SECStatus rv; + SECItem *avaValue = NULL; + char *unknownTag = NULL; + + tag = CERT_GetAVATag(ava); + switch (tag) { + case SEC_OID_AVA_COUNTRY_NAME: + tagName = "C"; + maxLen = 2; + break; + case SEC_OID_AVA_ORGANIZATION_NAME: + tagName = "O"; + maxLen = 64; + break; + case SEC_OID_AVA_COMMON_NAME: + tagName = "CN"; + maxLen = 64; + break; + case SEC_OID_AVA_LOCALITY: + tagName = "L"; + maxLen = 128; + break; + case SEC_OID_AVA_STATE_OR_PROVINCE: + tagName = "ST"; + maxLen = 128; + break; + case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME: + tagName = "OU"; + maxLen = 64; + break; + case SEC_OID_AVA_DC: + tagName = "DC"; + maxLen = 128; + break; + case SEC_OID_AVA_DN_QUALIFIER: + tagName = "dnQualifier"; + maxLen = 0x7fff; + break; + case SEC_OID_PKCS9_EMAIL_ADDRESS: + tagName = "E"; + maxLen = 128; + break; + case SEC_OID_RFC1274_UID: + tagName = "UID"; + maxLen = 256; + break; + case SEC_OID_RFC1274_MAIL: + tagName = "MAIL"; + maxLen = 256; + break; + default: + /* handle unknown attribute types per RFC 2253 */ + tagName = unknownTag = get_oid_string(&ava->type); + maxLen = 256; + break; + } + + avaValue = CERT_DecodeAVAValue(&ava->value); + if(!avaValue) { + /* the attribute value is not recognized, get the hex value */ + avaValue = get_hex_string(&ava->value); + if(!avaValue) { + if (unknownTag) PR_smprintf_free(unknownTag); + return SECFailure; + } + } + + /* Check value length */ + if (avaValue->len > maxLen) { + if (unknownTag) PR_smprintf_free(unknownTag); + PORT_SetError(SEC_ERROR_INVALID_AVA); + return SECFailure; + } + + len = PORT_Strlen(tagName); + if (len+1 > sizeof(tmpBuf)) { + if (unknownTag) PR_smprintf_free(unknownTag); + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + PORT_Memcpy(tmpBuf, tagName, len); + if (unknownTag) PR_smprintf_free(unknownTag); + tmpBuf[len++] = '='; + + /* escape and quote as necessary */ + rv = CERT_RFC1485_EscapeAndQuote(tmpBuf+len, sizeof(tmpBuf)-len, + (char *)avaValue->data, avaValue->len); + SECITEM_FreeItem(avaValue, PR_TRUE); + if (rv) return SECFailure; + + rv = AppendStr(bufp, tmpBuf); + return rv; +} + +char * +CERT_NameToAscii(CERTName *name) +{ + SECStatus rv; + CERTRDN** rdns; + CERTRDN** lastRdn; + CERTRDN** rdn; + CERTAVA** avas; + CERTAVA* ava; + PRBool first = PR_TRUE; + stringBuf strBuf = { NULL, 0, 0 }; + + rdns = name->rdns; + if (rdns == NULL) { + return NULL; + } + + /* find last RDN */ + lastRdn = rdns; + while (*lastRdn) lastRdn++; + lastRdn--; + + /* + * Loop over name contents in _reverse_ RDN order appending to string + */ + for (rdn = lastRdn; rdn >= rdns; rdn--) { + avas = (*rdn)->avas; + while ((ava = *avas++) != NULL) { + /* Put in comma separator */ + if (!first) { + rv = AppendStr(&strBuf, ", "); + if (rv) goto loser; + } else { + first = PR_FALSE; + } + + /* Add in tag type plus value into buf */ + rv = AppendAVA(&strBuf, ava); + if (rv) goto loser; + } + } + return strBuf.buffer; + loser: + if (strBuf.buffer) { + PORT_Free(strBuf.buffer); + } + return NULL; +} + +/* + * Return the string representation of a DER encoded distinguished name + * "dername" - The DER encoded name to convert + */ +char * +CERT_DerNameToAscii(SECItem *dername) +{ + int rv; + PRArenaPool *arena = NULL; + CERTName name; + char *retstr = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if ( arena == NULL) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem(arena, &name, CERT_NameTemplate, dername); + + if ( rv != SECSuccess ) { + goto loser; + } + + retstr = CERT_NameToAscii(&name); + +loser: + if ( arena != NULL ) { + PORT_FreeArena(arena, PR_FALSE); + } + + return(retstr); +} + +static char * +CERT_GetNameElement(PRArenaPool *arena, CERTName *name, int wantedTag) +{ + CERTRDN** rdns; + CERTRDN *rdn; + CERTAVA** avas; + CERTAVA* ava; + char *buf = 0; + int tag; + SECItem *decodeItem = NULL; + + rdns = name->rdns; + while ((rdn = *rdns++) != 0) { + avas = rdn->avas; + while ((ava = *avas++) != 0) { + tag = CERT_GetAVATag(ava); + if ( tag == wantedTag ) { + decodeItem = CERT_DecodeAVAValue(&ava->value); + if(!decodeItem) { + return NULL; + } + if (arena) { + buf = (char *)PORT_ArenaZAlloc(arena,decodeItem->len + 1); + } else { + buf = (char *)PORT_ZAlloc(decodeItem->len + 1); + } + if ( buf ) { + PORT_Memcpy(buf, decodeItem->data, decodeItem->len); + buf[decodeItem->len] = 0; + } + SECITEM_FreeItem(decodeItem, PR_TRUE); + goto done; + } + } + } + + done: + return buf; +} + +char * +CERT_GetCertificateEmailAddress(CERTCertificate *cert) +{ + char *rawEmailAddr = NULL; + SECItem subAltName; + SECStatus rv; + CERTGeneralName *nameList = NULL; + CERTGeneralName *current; + PRArenaPool *arena = NULL; + int i; + + subAltName.data = NULL; + + rawEmailAddr = CERT_GetNameElement(cert->arena, &(cert->subject), + SEC_OID_PKCS9_EMAIL_ADDRESS); + if ( rawEmailAddr == NULL ) { + rawEmailAddr = CERT_GetNameElement(cert->arena, &(cert->subject), + SEC_OID_RFC1274_MAIL); + } + if ( rawEmailAddr == NULL) { + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, + &subAltName); + if (rv != SECSuccess) { + goto finish; + } + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + goto finish; + } + nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName); + if (!nameList ) { + goto finish; + } + if (nameList != NULL) { + do { + if (current->type == certDirectoryName) { + rawEmailAddr = CERT_GetNameElement(cert->arena, + &(current->name.directoryName), + SEC_OID_PKCS9_EMAIL_ADDRESS); + if ( rawEmailAddr == NULL ) { + rawEmailAddr = CERT_GetNameElement(cert->arena, + &(current->name.directoryName), SEC_OID_RFC1274_MAIL); + } + } else if (current->type == certRFC822Name) { + rawEmailAddr = (char*)PORT_ArenaZAlloc(cert->arena, + current->name.other.len + 1); + if (!rawEmailAddr) { + goto finish; + } + PORT_Memcpy(rawEmailAddr, current->name.other.data, + current->name.other.len); + rawEmailAddr[current->name.other.len] = '\0'; + } + if (rawEmailAddr) { + break; + } + current = cert_get_next_general_name(current); + } while (current != nameList); + } + } + if (rawEmailAddr) { + for (i = 0; i <= (int) PORT_Strlen(rawEmailAddr); i++) { + rawEmailAddr[i] = tolower(rawEmailAddr[i]); + } + } + +finish: + + /* Don't free nameList, it's part of the arena. */ + + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + + if ( subAltName.data ) { + SECITEM_FreeItem(&subAltName, PR_FALSE); + } + + return(rawEmailAddr); +} + +static char * +appendStringToBuf(char *dest, char *src, PRUint32 *pRemaining) +{ + PRUint32 len; + if (dest && src && src[0] && *pRemaining > (len = PL_strlen(src))) { + PRUint32 i; + for (i = 0; i < len; ++i) + dest[i] = tolower(src[i]); + dest[len] = 0; + dest += len + 1; + *pRemaining -= len + 1; + } + return dest; +} + +static char * +appendItemToBuf(char *dest, SECItem *src, PRUint32 *pRemaining) +{ + if (dest && src && src->data && src->len && src->data[0] && + *pRemaining > src->len + 1 ) { + PRUint32 len = src->len; + PRUint32 i; + for (i = 0; i < len && src->data[i] ; ++i) + dest[i] = tolower(src->data[i]); + dest[len] = 0; + dest += len + 1; + *pRemaining -= len + 1; + } + return dest; +} + +/* Returns a pointer to an environment-like string, a series of +** null-terminated strings, terminated by a zero-length string. +** This function is intended to be internal to NSS. +*/ +char * +cert_GetCertificateEmailAddresses(CERTCertificate *cert) +{ + char * rawEmailAddr = NULL; + char * addrBuf = NULL; + char * pBuf = NULL; + PRArenaPool * tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + PRUint32 maxLen = 0; + PRInt32 finalLen = 0; + SECStatus rv; + SECItem subAltName; + + if (!tmpArena) + return addrBuf; + + subAltName.data = NULL; + maxLen = cert->derCert.len; + PORT_Assert(maxLen); + if (!maxLen) + maxLen = 2000; /* a guess, should never happen */ + + pBuf = addrBuf = (char *)PORT_ArenaZAlloc(tmpArena, maxLen + 1); + if (!addrBuf) + goto loser; + + rawEmailAddr = CERT_GetNameElement(tmpArena, &cert->subject, + SEC_OID_PKCS9_EMAIL_ADDRESS); + pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); + + rawEmailAddr = CERT_GetNameElement(tmpArena, &cert->subject, + SEC_OID_RFC1274_MAIL); + pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, + &subAltName); + if (rv == SECSuccess && subAltName.data) { + CERTGeneralName *nameList = NULL; + + if (!!(nameList = CERT_DecodeAltNameExtension(tmpArena, &subAltName))) { + CERTGeneralName *current = nameList; + do { + if (current->type == certDirectoryName) { + rawEmailAddr = CERT_GetNameElement(tmpArena, + ¤t->name.directoryName, + SEC_OID_PKCS9_EMAIL_ADDRESS); + pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); + + rawEmailAddr = CERT_GetNameElement(tmpArena, + ¤t->name.directoryName, + SEC_OID_RFC1274_MAIL); + pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); + } else if (current->type == certRFC822Name) { + pBuf = appendItemToBuf(pBuf, ¤t->name.other, &maxLen); + } + current = cert_get_next_general_name(current); + } while (current != nameList); + } + SECITEM_FreeItem(&subAltName, PR_FALSE); + /* Don't free nameList, it's part of the tmpArena. */ + } + /* now copy superstring to cert's arena */ + finalLen = (pBuf - addrBuf) + 1; + pBuf = PORT_ArenaAlloc(cert->arena, finalLen); + if (pBuf) { + PORT_Memcpy(pBuf, addrBuf, finalLen); + } + +loser: + if (tmpArena) + PORT_FreeArena(tmpArena, PR_FALSE); + + return pBuf; +} + +/* returns pointer to storage in cert's arena. Storage remains valid +** as long as cert's reference count doesn't go to zero. +** Caller should strdup or otherwise copy. +*/ +const char * /* const so caller won't muck with it. */ +CERT_GetFirstEmailAddress(CERTCertificate * cert) +{ + if (cert && cert->emailAddr && cert->emailAddr[0]) + return (const char *)cert->emailAddr; + return NULL; +} + +/* returns pointer to storage in cert's arena. Storage remains valid +** as long as cert's reference count doesn't go to zero. +** Caller should strdup or otherwise copy. +*/ +const char * /* const so caller won't muck with it. */ +CERT_GetNextEmailAddress(CERTCertificate * cert, const char * prev) +{ + if (cert && prev && prev[0]) { + PRUint32 len = PL_strlen(prev); + prev += len + 1; + if (prev && prev[0]) + return prev; + } + return NULL; +} + +/* This is seriously bogus, now that certs store their email addresses in +** subject Alternative Name extensions. +** Returns a string allocated by PORT_StrDup, which the caller must free. +*/ +char * +CERT_GetCertEmailAddress(CERTName *name) +{ + char *rawEmailAddr; + char *emailAddr; + + + rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_PKCS9_EMAIL_ADDRESS); + if ( rawEmailAddr == NULL ) { + rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_MAIL); + } + emailAddr = CERT_FixupEmailAddr(rawEmailAddr); + if ( rawEmailAddr ) { + PORT_Free(rawEmailAddr); + } + return(emailAddr); +} + +char * +CERT_GetCommonName(CERTName *name) +{ + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_COMMON_NAME)); +} + +char * +CERT_GetCountryName(CERTName *name) +{ + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_COUNTRY_NAME)); +} + +char * +CERT_GetLocalityName(CERTName *name) +{ + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_LOCALITY)); +} + +char * +CERT_GetStateName(CERTName *name) +{ + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_STATE_OR_PROVINCE)); +} + +char * +CERT_GetOrgName(CERTName *name) +{ + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATION_NAME)); +} + +char * +CERT_GetDomainComponentName(CERTName *name) +{ + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_DC)); +} + +char * +CERT_GetOrgUnitName(CERTName *name) +{ + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME)); +} + +char * +CERT_GetDnQualifier(CERTName *name) +{ + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_DN_QUALIFIER)); +} + +char * +CERT_GetCertUid(CERTName *name) +{ + return(CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_UID)); +} + diff --git a/security/nss/lib/certdb/cert.h b/security/nss/lib/certdb/cert.h new file mode 100644 index 000000000..1131b1633 --- /dev/null +++ b/security/nss/lib/certdb/cert.h @@ -0,0 +1,1493 @@ +/* + * 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. + */ + +/* + * cert.h - public data structures and prototypes for the certificate library + * + * $Id$ + */ + +#ifndef _CERT_H_ +#define _CERT_H_ + +#include "plarena.h" +#include "plhash.h" +#include "prlong.h" +#include "prlog.h" + +#include "seccomon.h" +#include "secdert.h" +#include "secoidt.h" +#include "keyt.h" +#include "certt.h" + +SEC_BEGIN_PROTOS + +/**************************************************************************** + * + * RFC1485 ascii to/from X.? RelativeDistinguishedName (aka CERTName) + * + ****************************************************************************/ + +/* +** Convert an ascii RFC1485 encoded name into its CERTName equivalent. +*/ +extern CERTName *CERT_AsciiToName(char *string); + +/* +** Convert an CERTName into its RFC1485 encoded equivalent. +*/ +extern char *CERT_NameToAscii(CERTName *name); + +extern CERTAVA *CERT_CopyAVA(PRArenaPool *arena, CERTAVA *src); + +/* +** Examine an AVA and return the tag that refers to it. The AVA tags are +** defined as SEC_OID_AVA*. +*/ +extern SECOidTag CERT_GetAVATag(CERTAVA *ava); + +/* +** Compare two AVA's, returning the difference between them. +*/ +extern SECComparison CERT_CompareAVA(CERTAVA *a, CERTAVA *b); + +/* +** Create an RDN (relative-distinguished-name). The argument list is a +** NULL terminated list of AVA's. +*/ +extern CERTRDN *CERT_CreateRDN(PRArenaPool *arena, CERTAVA *avas, ...); + +/* +** Make a copy of "src" storing it in "dest". +*/ +extern SECStatus CERT_CopyRDN(PRArenaPool *arena, CERTRDN *dest, CERTRDN *src); + +/* +** Destory an RDN object. +** "rdn" the RDN to destroy +** "freeit" if PR_TRUE then free the object as well as its sub-objects +*/ +extern void CERT_DestroyRDN(CERTRDN *rdn, PRBool freeit); + +/* +** Add an AVA to an RDN. +** "rdn" the RDN to add to +** "ava" the AVA to add +*/ +extern SECStatus CERT_AddAVA(PRArenaPool *arena, CERTRDN *rdn, CERTAVA *ava); + +/* +** Compare two RDN's, returning the difference between them. +*/ +extern SECComparison CERT_CompareRDN(CERTRDN *a, CERTRDN *b); + +/* +** Create an X.500 style name using a NULL terminated list of RDN's. +*/ +extern CERTName *CERT_CreateName(CERTRDN *rdn, ...); + +/* +** Make a copy of "src" storing it in "dest". Memory is allocated in +** "dest" for each of the appropriate sub objects. Memory is not freed in +** "dest" before allocation is done (use CERT_DestroyName(dest, PR_FALSE) to +** do that). +*/ +extern SECStatus CERT_CopyName(PRArenaPool *arena, CERTName *dest, CERTName *src); + +/* +** Destroy a Name object. +** "name" the CERTName to destroy +** "freeit" if PR_TRUE then free the object as well as its sub-objects +*/ +extern void CERT_DestroyName(CERTName *name); + +/* +** Add an RDN to a name. +** "name" the name to add the RDN to +** "rdn" the RDN to add to name +*/ +extern SECStatus CERT_AddRDN(CERTName *name, CERTRDN *rdn); + +/* +** Compare two names, returning the difference between them. +*/ +extern SECComparison CERT_CompareName(CERTName *a, CERTName *b); + +/* +** Convert a CERTName into something readable +*/ +extern char *CERT_FormatName (CERTName *name); + +/* +** Convert a der-encoded integer to a hex printable string form. +** Perhaps this should be a SEC function but it's only used for certs. +*/ +extern char *CERT_Hexify (SECItem *i, int do_colon); + +/****************************************************************************** + * + * Certificate handling operations + * + *****************************************************************************/ + +/* +** Create a new validity object given two unix time values. +** "notBefore" the time before which the validity is not valid +** "notAfter" the time after which the validity is not valid +*/ +extern CERTValidity *CERT_CreateValidity(int64 notBefore, int64 notAfter); + +/* +** Destroy a validity object. +** "v" the validity to destroy +** "freeit" if PR_TRUE then free the object as well as its sub-objects +*/ +extern void CERT_DestroyValidity(CERTValidity *v); + +/* +** Copy the "src" object to "dest". Memory is allocated in "dest" for +** each of the appropriate sub-objects. Memory in "dest" is not freed +** before memory is allocated (use CERT_DestroyValidity(v, PR_FALSE) to do +** that). +*/ +extern SECStatus CERT_CopyValidity + (PRArenaPool *arena, CERTValidity *dest, CERTValidity *src); + +/* +** The cert lib considers a cert or CRL valid if the "notBefore" time is +** in the not-too-distant future, e.g. within the next 24 hours. This +** prevents freshly issued certificates from being considered invalid +** because the local system's time zone is incorrectly set. +** The amount of "pending slop time" is adjustable by the application. +** Units of SlopTime are seconds. Default is 86400 (24 hours). +** Negative SlopTime values are not allowed. +*/ +PRInt32 CERT_GetSlopTime(void); + +SECStatus CERT_SetSlopTime(PRInt32 slop); + +/* +** Create a new certificate object. The result must be wrapped with an +** CERTSignedData to create a signed certificate. +** "serialNumber" the serial number +** "issuer" the name of the certificate issuer +** "validity" the validity period of the certificate +** "req" the certificate request that prompted the certificate issuance +*/ +extern CERTCertificate * +CERT_CreateCertificate (unsigned long serialNumber, CERTName *issuer, + CERTValidity *validity, CERTCertificateRequest *req); + +/* +** Destroy a certificate object +** "cert" the certificate to destroy +** NOTE: certificate's are reference counted. This call decrements the +** reference count, and if the result is zero, then the object is destroyed +** and optionally freed. +*/ +extern void CERT_DestroyCertificate(CERTCertificate *cert); + +/* +** Make a shallow copy of a certificate "c". Just increments the +** reference count on "c". +*/ +extern CERTCertificate *CERT_DupCertificate(CERTCertificate *c); + +/* +** Create a new certificate request. This result must be wrapped with an +** CERTSignedData to create a signed certificate request. +** "name" the subject name (who the certificate request is from) +** "spki" describes/defines the public key the certificate is for +** "attributes" if non-zero, some optional attribute data +*/ +extern CERTCertificateRequest * +CERT_CreateCertificateRequest (CERTName *name, CERTSubjectPublicKeyInfo *spki, + SECItem **attributes); + +/* +** Destroy a certificate-request object +** "r" the certificate-request to destroy +** "freeit" if PR_TRUE then free the object as well as its sub-objects +*/ +extern void CERT_DestroyCertificateRequest(CERTCertificateRequest *r); + +/* +** Extract a public key object from a SubjectPublicKeyInfo +*/ +extern SECKEYPublicKey *CERT_ExtractPublicKey(CERTCertificate *cert); + +/* + * used to get a public key with Key Material ID. Only used for fortezza V1 + * certificates. + */ +extern SECKEYPublicKey *CERT_KMIDPublicKey(CERTCertificate *cert); + + +/* +** Retrieve the Key Type associated with the cert we're dealing with +*/ + +extern KeyType CERT_GetCertKeyType (CERTSubjectPublicKeyInfo *spki); + +/* +** Initialize the certificate database. This is called to create +** the initial list of certificates in the database. +*/ +extern SECStatus CERT_InitCertDB(CERTCertDBHandle *handle); + +extern int CERT_GetDBContentVersion(CERTCertDBHandle *handle); + +/* +** Default certificate database routines +*/ +extern void CERT_SetDefaultCertDB(CERTCertDBHandle *handle); + +extern CERTCertDBHandle *CERT_GetDefaultCertDB(void); + +extern CERTCertList *CERT_GetCertChainFromCert(CERTCertificate *cert, + int64 time, + SECCertUsage usage); +extern CERTCertificate * +CERT_NewTempCertificate (CERTCertDBHandle *handle, SECItem *derCert, + char *nickname, PRBool isperm, PRBool copyDER); + + +/****************************************************************************** + * + * X.500 Name handling operations + * + *****************************************************************************/ + +/* +** Create an AVA (attribute-value-assertion) +** "arena" the memory arena to alloc from +** "kind" is one of SEC_OID_AVA_* +** "valueType" is one of DER_PRINTABLE_STRING, DER_IA5_STRING, or +** DER_T61_STRING +** "value" is the null terminated string containing the value +*/ +extern CERTAVA *CERT_CreateAVA + (PRArenaPool *arena, SECOidTag kind, int valueType, char *value); + +/* +** Extract the Distinguished Name from a DER encoded certificate +** "derCert" is the DER encoded certificate +** "derName" is the SECItem that the name is returned in +*/ +extern SECStatus CERT_NameFromDERCert(SECItem *derCert, SECItem *derName); + +/* +** Extract the Issuers Distinguished Name from a DER encoded certificate +** "derCert" is the DER encoded certificate +** "derName" is the SECItem that the name is returned in +*/ +extern SECStatus CERT_IssuerNameFromDERCert(SECItem *derCert, + SECItem *derName); + + + +/* +** Generate a database search key for a certificate, based on the +** issuer and serial number. +** "arena" the memory arena to alloc from +** "derCert" the DER encoded certificate +** "key" the returned key +*/ +extern SECStatus CERT_KeyFromDERCert(PRArenaPool *arena, SECItem *derCert, SECItem *key); + +extern SECStatus CERT_KeyFromIssuerAndSN(PRArenaPool *arena, SECItem *issuer, + SECItem *sn, SECItem *key); + +extern SECStatus CERT_SerialNumberFromDERCert(SECItem *derCert, + SECItem *derName); + + +/* +** Generate a database search key for a crl, based on the +** issuer. +** "arena" the memory arena to alloc from +** "derCrl" the DER encoded crl +** "key" the returned key +*/ +extern SECStatus CERT_KeyFromDERCrl(PRArenaPool *arena, SECItem *derCrl, SECItem *key); + +/* +** Open the certificate database. Use callback to get name of database. +*/ +extern SECStatus CERT_OpenCertDB(CERTCertDBHandle *handle, PRBool readOnly, + CERTDBNameFunc namecb, void *cbarg); + +/* Open the certificate database. Use given filename for database. */ +extern SECStatus CERT_OpenCertDBFilename(CERTCertDBHandle *handle, + char *certdbname, PRBool readOnly); + +/* +** Open and initialize a cert database that is entirely in memory. This +** can be used when the permanent database can not be opened or created. +*/ +extern SECStatus CERT_OpenVolatileCertDB(CERTCertDBHandle *handle); + +/* +** Check the hostname to make sure that it matches the shexp that +** is given in the common name of the certificate. +*/ +extern SECStatus CERT_VerifyCertName(CERTCertificate *cert, const char *hostname); + +/* +** Add a domain name to the list of names that the user has explicitly +** allowed (despite cert name mismatches) for use with a server cert. +*/ +extern SECStatus CERT_AddOKDomainName(CERTCertificate *cert, const char *hostname); + +/* +** Decode a DER encoded certificate into an CERTCertificate structure +** "derSignedCert" is the DER encoded signed certificate +** "copyDER" is true if the DER should be copied, false if the +** existing copy should be referenced +** "nickname" is the nickname to use in the database. If it is NULL +** then a temporary nickname is generated. +*/ +extern CERTCertificate * +CERT_DecodeDERCertificate (SECItem *derSignedCert, PRBool copyDER, char *nickname); +/* +** Decode a DER encoded CRL/KRL into an CERTSignedCrl structure +** "derSignedCrl" is the DER encoded signed crl/krl. +** "type" is this a CRL or KRL. +*/ +#define SEC_CRL_TYPE 1 +#define SEC_KRL_TYPE 0 + +extern CERTSignedCrl * +CERT_DecodeDERCrl (PRArenaPool *arena, SECItem *derSignedCrl,int type); + +/* + * same as CERT_DecodeDERCrl, plus allow options to be passed in + */ + +extern CERTSignedCrl * +CERT_DecodeDERCrlWithFlags(PRArenaPool *narena, SECItem *derSignedCrl, + int type, PRInt32 options); + +/* CRL options to pass */ + +#define CRL_DECODE_DEFAULT_OPTIONS 0x00000000 + +/* when CRL_DECODE_DONT_COPY_DER is set, the DER is not copied . The + application must then keep derSignedCrl until it destroys the + CRL . Ideally, it should allocate derSignedCrl in an arena + and pass that arena in as the first argument to + CERT_DecodeDERCrlWithFlags */ + +#define CRL_DECODE_DONT_COPY_DER 0x00000001 +#define CRL_DECODE_SKIP_ENTRIES 0x00000002 +#define CRL_DECODE_KEEP_BAD_CRL 0x00000004 + +/* complete the decoding of a partially decoded CRL, ie. decode the + entries. Note that entries is an optional field in a CRL, so the + "entries" pointer in CERTCrlStr may still be NULL even after + function returns SECSuccess */ + +extern SECStatus CERT_CompleteCRLDecodeEntries(CERTSignedCrl* crl); + +/* Validate CRL then import it to the dbase. If there is already a CRL with the + * same CA in the dbase, it will be replaced if derCRL is more up to date. + * If the process successes, a CRL will be returned. Otherwise, a NULL will + * be returned. The caller should call PORT_GetError() for the exactly error + * code. + */ +extern CERTSignedCrl * +CERT_ImportCRL (CERTCertDBHandle *handle, SECItem *derCRL, char *url, + int type, void * wincx); + +extern void CERT_DestroyCrl (CERTSignedCrl *crl); + +/* this is a hint to flush the CRL cache. crlKey is the DER subject of + the issuer (CA). */ +void CERT_CRLCacheRefreshIssuer(CERTCertDBHandle* dbhandle, SECItem* crlKey); + +/* +** Decode a certificate and put it into the temporary certificate database +*/ +extern CERTCertificate * +CERT_DecodeCertificate (SECItem *derCert, char *nickname,PRBool copyDER); + +/* +** Find a certificate in the database +** "key" is the database key to look for +*/ +extern CERTCertificate *CERT_FindCertByKey(CERTCertDBHandle *handle, SECItem *key); + +/* +** Find a certificate in the database by name +** "name" is the distinguished name to look up +*/ +extern CERTCertificate * +CERT_FindCertByName (CERTCertDBHandle *handle, SECItem *name); + +/* +** Find a certificate in the database by name +** "name" is the distinguished name to look up (in ascii) +*/ +extern CERTCertificate * +CERT_FindCertByNameString (CERTCertDBHandle *handle, char *name); + +/* +** Find a certificate in the database by name and keyid +** "name" is the distinguished name to look up +** "keyID" is the value of the subjectKeyID to match +*/ +extern CERTCertificate * +CERT_FindCertByKeyID (CERTCertDBHandle *handle, SECItem *name, SECItem *keyID); + +/* +** Generate a certificate key from the issuer and serialnumber, then look it +** up in the database. Return the cert if found. +** "issuerAndSN" is the issuer and serial number to look for +*/ +extern CERTCertificate * +CERT_FindCertByIssuerAndSN (CERTCertDBHandle *handle, CERTIssuerAndSN *issuerAndSN); + +/* +** Find a certificate in the database by a subject key ID +** "subjKeyID" is the subject Key ID to look for +*/ +extern CERTCertificate * +CERT_FindCertBySubjectKeyID (CERTCertDBHandle *handle, SECItem *subjKeyID); + +/* +** Find a certificate in the database by a nickname +** "nickname" is the ascii string nickname to look for +*/ +extern CERTCertificate * +CERT_FindCertByNickname (CERTCertDBHandle *handle, char *nickname); + +/* +** Find a certificate in the database by a DER encoded certificate +** "derCert" is the DER encoded certificate +*/ +extern CERTCertificate * +CERT_FindCertByDERCert(CERTCertDBHandle *handle, SECItem *derCert); + +/* +** Find a certificate in the database by a email address +** "emailAddr" is the email address to look up +*/ +CERTCertificate * +CERT_FindCertByEmailAddr(CERTCertDBHandle *handle, char *emailAddr); + +/* +** Find a certificate in the database by a email address or nickname +** "name" is the email address or nickname to look up +*/ +CERTCertificate * +CERT_FindCertByNicknameOrEmailAddr(CERTCertDBHandle *handle, char *name); + +/* +** Find a certificate in the database by a digest of a subject public key +** "spkDigest" is the digest to look up +*/ +extern CERTCertificate * +CERT_FindCertBySPKDigest(CERTCertDBHandle *handle, SECItem *spkDigest); + +/* + * Find the issuer of a cert + */ +CERTCertificate * +CERT_FindCertIssuer(CERTCertificate *cert, int64 validTime, SECCertUsage usage); + +/* +** Check the validity times of a certificate vs. time 't', allowing +** some slop for broken clocks and stuff. +** "cert" is the certificate to be checked +** "t" is the time to check against +** "allowOverride" if true then check to see if the invalidity has +** been overridden by the user. +*/ +extern SECCertTimeValidity CERT_CheckCertValidTimes(CERTCertificate *cert, + PRTime t, + PRBool allowOverride); + +/* +** WARNING - this function is depricated, and will either go away or have +** a new API in the near future. +** +** Check the validity times of a certificate vs. the current time, allowing +** some slop for broken clocks and stuff. +** "cert" is the certificate to be checked +*/ +extern SECStatus CERT_CertTimesValid(CERTCertificate *cert); + +/* +** Extract the validity times from a certificate +** "c" is the certificate +** "notBefore" is the start of the validity period +** "notAfter" is the end of the validity period +*/ +extern SECStatus +CERT_GetCertTimes (CERTCertificate *c, PRTime *notBefore, PRTime *notAfter); + +/* +** Extract the issuer and serial number from a certificate +*/ +extern CERTIssuerAndSN *CERT_GetCertIssuerAndSN(PRArenaPool *, + CERTCertificate *); + +/* +** verify the signature of a signed data object with a given certificate +** "sd" the signed data object to be verified +** "cert" the certificate to use to check the signature +*/ +extern SECStatus CERT_VerifySignedData(CERTSignedData *sd, + CERTCertificate *cert, + int64 t, + void *wincx); +/* +** verify the signature of a signed data object with the given DER publickey +*/ +extern SECStatus +CERT_VerifySignedDataWithPublicKeyInfo(CERTSignedData *sd, + CERTSubjectPublicKeyInfo *pubKeyInfo, + void *wincx); + +/* +** verify the signature of a signed data object with a SECKEYPublicKey. +*/ +extern SECStatus +CERT_VerifySignedDataWithPublicKey(CERTSignedData *sd, + SECKEYPublicKey *pubKey, void *wincx); + +/* +** NEW FUNCTIONS with new bit-field-FIELD SECCertificateUsage - please use +** verify a certificate by checking validity times against a certain time, +** that we trust the issuer, and that the signature on the certificate is +** valid. +** "cert" the certificate to verify +** "checkSig" only check signatures if true +*/ +extern SECStatus +CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertificateUsage requiredUsages, + int64 t, void *wincx, CERTVerifyLog *log, + SECCertificateUsage* returnedUsages); + +/* same as above, but uses current time */ +extern SECStatus +CERT_VerifyCertificateNow(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertificateUsage requiredUsages, + void *wincx, SECCertificateUsage* returnedUsages); + +/* +** Verify that a CA cert can certify some (unspecified) leaf cert for a given +** purpose. This is used by UI code to help identify where a chain may be +** broken and why. This takes identical parameters to CERT_VerifyCert +*/ +extern SECStatus +CERT_VerifyCACertForUsage(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, int64 t, + void *wincx, CERTVerifyLog *log); + +/* +** OLD OBSOLETE FUNCTIONS with enum SECCertUsage - DO NOT USE FOR NEW CODE +** verify a certificate by checking validity times against a certain time, +** that we trust the issuer, and that the signature on the certificate is +** valid. +** "cert" the certificate to verify +** "checkSig" only check signatures if true +*/ +extern SECStatus +CERT_VerifyCert(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, int64 t, + void *wincx, CERTVerifyLog *log); + +/* same as above, but uses current time */ +extern SECStatus +CERT_VerifyCertNow(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, void *wincx); + +SECStatus +CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, int64 t, + void *wincx, CERTVerifyLog *log); + +/* +** This must only be called on a cert that is known to have an issuer +** with an invalid time +*/ +extern CERTCertificate * +CERT_FindExpiredIssuer (CERTCertDBHandle *handle, CERTCertificate *cert); + +/* +** Read a base64 ascii encoded DER certificate and convert it to our +** internal format. +** "certstr" is a null-terminated string containing the certificate +*/ +extern CERTCertificate *CERT_ConvertAndDecodeCertificate(char *certstr); + +/* +** Read a certificate in some foreign format, and convert it to our +** internal format. +** "certbuf" is the buffer containing the certificate +** "certlen" is the length of the buffer +** NOTE - currently supports netscape base64 ascii encoded raw certs +** and netscape binary DER typed files. +*/ +extern CERTCertificate *CERT_DecodeCertFromPackage(char *certbuf, int certlen); + +extern SECStatus +CERT_ImportCAChain (SECItem *certs, int numcerts, SECCertUsage certUsage); + +extern SECStatus +CERT_ImportCAChainTrusted(SECItem *certs, int numcerts, SECCertUsage certUsage); + +/* +** Read a certificate chain in some foreign format, and pass it to a +** callback function. +** "certbuf" is the buffer containing the certificate +** "certlen" is the length of the buffer +** "f" is the callback function +** "arg" is the callback argument +*/ +typedef SECStatus (PR_CALLBACK *CERTImportCertificateFunc) + (void *arg, SECItem **certs, int numcerts); + +extern SECStatus +CERT_DecodeCertPackage(char *certbuf, int certlen, CERTImportCertificateFunc f, + void *arg); + +/* +** Pretty print a certificate in HTML +** "cert" is the certificate to print +** "showImages" controls whether or not to use about:security URLs +** for subject and issuer images. This should only be true +** in the browser. +*/ +extern char *CERT_HTMLCertInfo(CERTCertificate *cert, PRBool showImages, + PRBool showIssuer); + +/* +** Returns the value of an AVA. This was a formerly static +** function that has been exposed due to the need to decode +** and convert unicode strings to UTF8. +** +** XXX This function resides in certhtml.c, should it be +** moved elsewhere? +*/ +extern SECItem *CERT_DecodeAVAValue(SECItem *derAVAValue); + +/* + * take a DER certificate and decode it into a certificate structure + */ +CERTCertificate * +CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER, + char *nickname); + + + +/* +** extract various element strings from a distinguished name. +** "name" the distinguished name +*/ +extern char *CERT_GetCommonName(CERTName *name); + +extern char *CERT_GetCertificateEmailAddress(CERTCertificate *cert); + +extern char *CERT_GetCertEmailAddress(CERTName *name); + +extern const char * CERT_GetFirstEmailAddress(CERTCertificate * cert); + +extern const char * CERT_GetNextEmailAddress(CERTCertificate * cert, + const char * prev); + +extern char *CERT_GetCommonName(CERTName *name); + +extern char *CERT_GetCountryName(CERTName *name); + +extern char *CERT_GetLocalityName(CERTName *name); + +extern char *CERT_GetStateName(CERTName *name); + +extern char *CERT_GetOrgName(CERTName *name); + +extern char *CERT_GetOrgUnitName(CERTName *name); + +extern char *CERT_GetDomainComponentName(CERTName *name); + +extern char *CERT_GetCertUid(CERTName *name); + +/* manipulate the trust parameters of a certificate */ + +extern SECStatus CERT_GetCertTrust(CERTCertificate *cert, CERTCertTrust *trust); + +extern SECStatus +CERT_ChangeCertTrust (CERTCertDBHandle *handle, CERTCertificate *cert, + CERTCertTrust *trust); + +extern SECStatus +CERT_ChangeCertTrustByUsage(CERTCertDBHandle *certdb, CERTCertificate *cert, + SECCertUsage usage); + +/************************************************************************* + * + * manipulate the extensions of a certificate + * + ************************************************************************/ + +/* +** Set up a cert for adding X509v3 extensions. Returns an opaque handle +** used by the next two routines. +** "cert" is the certificate we are adding extensions to +*/ +extern void *CERT_StartCertExtensions(CERTCertificate *cert); + +/* +** Add an extension to a certificate. +** "exthandle" is the handle returned by the previous function +** "idtag" is the integer tag for the OID that should ID this extension +** "value" is the value of the extension +** "critical" is the critical extension flag +** "copyData" is a flag indicating whether the value data should be +** copied. +*/ +extern SECStatus CERT_AddExtension (void *exthandle, int idtag, + SECItem *value, PRBool critical, PRBool copyData); + +extern SECStatus CERT_AddExtensionByOID (void *exthandle, SECItem *oid, + SECItem *value, PRBool critical, PRBool copyData); + +extern SECStatus CERT_EncodeAndAddExtension + (void *exthandle, int idtag, void *value, PRBool critical, + const SEC_ASN1Template *atemplate); + +extern SECStatus CERT_EncodeAndAddBitStrExtension + (void *exthandle, int idtag, SECItem *value, PRBool critical); + + +extern SECStatus +CERT_EncodeAltNameExtension(PRArenaPool *arena, CERTGeneralName *value, SECItem *encodedValue); + + +/* +** Finish adding cert extensions. Does final processing on extension +** data, putting it in the right format, and freeing any temporary +** storage. +** "exthandle" is the handle used to add extensions to a certificate +*/ +extern SECStatus CERT_FinishExtensions(void *exthandle); + + +/* If the extension is found, return its criticality and value. +** This allocate storage for the returning extension value. +*/ +extern SECStatus CERT_GetExtenCriticality + (CERTCertExtension **extensions, int tag, PRBool *isCritical); + +extern void +CERT_DestroyOidSequence(CERTOidSequence *oidSeq); + +/**************************************************************************** + * + * DER encode and decode extension values + * + ****************************************************************************/ + +/* Encode the value of the basicConstraint extension. +** arena - where to allocate memory for the encoded value. +** value - extension value to encode +** encodedValue - output encoded value +*/ +extern SECStatus CERT_EncodeBasicConstraintValue + (PRArenaPool *arena, CERTBasicConstraints *value, SECItem *encodedValue); + +/* +** Encode the value of the authorityKeyIdentifier extension. +*/ +extern SECStatus CERT_EncodeAuthKeyID + (PRArenaPool *arena, CERTAuthKeyID *value, SECItem *encodedValue); + +/* +** Encode the value of the crlDistributionPoints extension. +*/ +extern SECStatus CERT_EncodeCRLDistributionPoints + (PRArenaPool *arena, CERTCrlDistributionPoints *value,SECItem *derValue); + +/* +** Decodes a DER encoded basicConstaint extension value into a readable format +** value - decoded value +** encodedValue - value to decoded +*/ +extern SECStatus CERT_DecodeBasicConstraintValue + (CERTBasicConstraints *value, SECItem *encodedValue); + +/* Decodes a DER encoded authorityKeyIdentifier extension value into a +** readable format. +** arena - where to allocate memory for the decoded value +** encodedValue - value to be decoded +** Returns a CERTAuthKeyID structure which contains the decoded value +*/ +extern CERTAuthKeyID *CERT_DecodeAuthKeyID + (PRArenaPool *arena, SECItem *encodedValue); + + +/* Decodes a DER encoded crlDistributionPoints extension value into a +** readable format. +** arena - where to allocate memory for the decoded value +** der - value to be decoded +** Returns a CERTCrlDistributionPoints structure which contains the +** decoded value +*/ +extern CERTCrlDistributionPoints * CERT_DecodeCRLDistributionPoints + (PRArenaPool *arena, SECItem *der); + +/* Extract certain name type from a generalName */ +extern void *CERT_GetGeneralNameByType + (CERTGeneralName *genNames, CERTGeneralNameType type, PRBool derFormat); + + +extern CERTOidSequence * +CERT_DecodeOidSequence(SECItem *seqItem); + + + + +/**************************************************************************** + * + * Find extension values of a certificate + * + ***************************************************************************/ + +extern SECStatus CERT_FindCertExtension + (CERTCertificate *cert, int tag, SECItem *value); + +extern SECStatus CERT_FindNSCertTypeExtension + (CERTCertificate *cert, SECItem *value); + +extern char * CERT_FindNSStringExtension (CERTCertificate *cert, int oidtag); + +extern SECStatus CERT_FindIssuerCertExtension + (CERTCertificate *cert, int tag, SECItem *value); + +extern SECStatus CERT_FindCertExtensionByOID + (CERTCertificate *cert, SECItem *oid, SECItem *value); + +extern char *CERT_FindCertURLExtension (CERTCertificate *cert, int tag, + int catag); + +/* Returns the decoded value of the authKeyID extension. +** Note that this uses passed in the arena to allocate storage for the result +*/ +extern CERTAuthKeyID * CERT_FindAuthKeyIDExten (PRArenaPool *arena,CERTCertificate *cert); + +/* Returns the decoded value of the basicConstraint extension. + */ +extern SECStatus CERT_FindBasicConstraintExten + (CERTCertificate *cert, CERTBasicConstraints *value); + +/* Returns the decoded value of the crlDistributionPoints extension. +** Note that the arena in cert is used to allocate storage for the result +*/ +extern CERTCrlDistributionPoints * CERT_FindCRLDistributionPoints + (CERTCertificate *cert); + +/* Returns value of the keyUsage extension. This uses PR_Alloc to allocate +** buffer for the decoded value, The caller should free up the storage +** allocated in value->data. +*/ +extern SECStatus CERT_FindKeyUsageExtension (CERTCertificate *cert, + SECItem *value); + +/* Return the decoded value of the subjectKeyID extension. The caller should +** free up the storage allocated in retItem->data. +*/ +extern SECStatus CERT_FindSubjectKeyIDExtension (CERTCertificate *cert, + SECItem *retItem); + +/* +** If cert is a v3 certificate, and a critical keyUsage extension is included, +** then check the usage against the extension value. If a non-critical +** keyUsage extension is included, this will return SECSuccess without +** checking, since the extension is an advisory field, not a restriction. +** If cert is not a v3 certificate, this will return SECSuccess. +** cert - certificate +** usage - one of the x.509 v3 the Key Usage Extension flags +*/ +extern SECStatus CERT_CheckCertUsage (CERTCertificate *cert, + unsigned char usage); + +/**************************************************************************** + * + * CRL v2 Extensions supported routines + * + ****************************************************************************/ + +extern SECStatus CERT_FindCRLExtensionByOID + (CERTCrl *crl, SECItem *oid, SECItem *value); + +extern SECStatus CERT_FindCRLExtension + (CERTCrl *crl, int tag, SECItem *value); + +extern SECStatus + CERT_FindInvalidDateExten (CERTCrl *crl, int64 *value); + +extern void *CERT_StartCRLExtensions (CERTCrl *crl); + +extern CERTCertNicknames *CERT_GetCertNicknames (CERTCertDBHandle *handle, + int what, void *wincx); + +/* +** Finds the crlNumber extension and decodes its value into 'value' +*/ +extern SECStatus CERT_FindCRLNumberExten (CERTCrl *crl, CERTCrlNumber *value); + +extern void CERT_FreeNicknames(CERTCertNicknames *nicknames); + +extern PRBool CERT_CompareCerts(CERTCertificate *c1, CERTCertificate *c2); + +extern PRBool CERT_CompareCertsForRedirection(CERTCertificate *c1, + CERTCertificate *c2); + +/* +** Generate an array of the Distinguished Names that the given cert database +** "trusts" +*/ +extern CERTDistNames *CERT_GetSSLCACerts(CERTCertDBHandle *handle); + +extern void CERT_FreeDistNames(CERTDistNames *names); + +/* +** Generate an array of Distinguished names from an array of nicknames +*/ +extern CERTDistNames *CERT_DistNamesFromNicknames + (CERTCertDBHandle *handle, char **nicknames, int nnames); + +/* +** Generate a certificate chain from a certificate. +*/ +extern CERTCertificateList * +CERT_CertChainFromCert(CERTCertificate *cert, SECCertUsage usage, + PRBool includeRoot); + +extern CERTCertificateList * +CERT_CertListFromCert(CERTCertificate *cert); + +extern CERTCertificateList * +CERT_DupCertList(CERTCertificateList * oldList); + +extern void CERT_DestroyCertificateList(CERTCertificateList *list); + +/* +** is cert a user cert? i.e. does it have CERTDB_USER trust, +** i.e. a private key? +*/ +PRBool CERT_IsUserCert(CERTCertificate* cert); + +/* is cert a newer than cert b? */ +PRBool CERT_IsNewer(CERTCertificate *certa, CERTCertificate *certb); + +/* currently a stub for address book */ +PRBool +CERT_IsCertRevoked(CERTCertificate *cert); + +void +CERT_DestroyCertArray(CERTCertificate **certs, unsigned int ncerts); + +/* convert an email address to lower case */ +char *CERT_FixupEmailAddr(char *emailAddr); + +/* decode string representation of trust flags into trust struct */ +SECStatus +CERT_DecodeTrustString(CERTCertTrust *trust, char *trusts); + +/* encode trust struct into string representation of trust flags */ +char * +CERT_EncodeTrustString(CERTCertTrust *trust); + +/* find the next or prev cert in a subject list */ +CERTCertificate * +CERT_PrevSubjectCert(CERTCertificate *cert); +CERTCertificate * +CERT_NextSubjectCert(CERTCertificate *cert); + +/* + * import a collection of certs into the temporary or permanent cert + * database + */ +SECStatus +CERT_ImportCerts(CERTCertDBHandle *certdb, SECCertUsage usage, + unsigned int ncerts, SECItem **derCerts, + CERTCertificate ***retCerts, PRBool keepCerts, + PRBool caOnly, char *nickname); + +SECStatus +CERT_SaveImportedCert(CERTCertificate *cert, SECCertUsage usage, + PRBool caOnly, char *nickname); + +char * +CERT_MakeCANickname(CERTCertificate *cert); + +PRBool +CERT_IsCACert(CERTCertificate *cert, unsigned int *rettype); + +PRBool +CERT_IsCADERCert(SECItem *derCert, unsigned int *rettype); + +PRBool +CERT_IsRootDERCert(SECItem *derCert); + +SECStatus +CERT_SaveSMimeProfile(CERTCertificate *cert, SECItem *emailProfile, + SECItem *profileTime); + +/* + * find the smime symmetric capabilities profile for a given cert + */ +SECItem * +CERT_FindSMimeProfile(CERTCertificate *cert); + +SECStatus +CERT_AddNewCerts(CERTCertDBHandle *handle); + +CERTPackageType +CERT_CertPackageType(SECItem *package, SECItem *certitem); + +CERTCertificatePolicies * +CERT_DecodeCertificatePoliciesExtension(SECItem *extnValue); + +void +CERT_DestroyCertificatePoliciesExtension(CERTCertificatePolicies *policies); + +CERTUserNotice * +CERT_DecodeUserNotice(SECItem *noticeItem); + +void +CERT_DestroyUserNotice(CERTUserNotice *userNotice); + +typedef char * (* CERTPolicyStringCallback)(char *org, + unsigned long noticeNumber, + void *arg); +void +CERT_SetCAPolicyStringCallback(CERTPolicyStringCallback cb, void *cbarg); + +char * +CERT_GetCertCommentString(CERTCertificate *cert); + +PRBool +CERT_GovtApprovedBitSet(CERTCertificate *cert); + +SECStatus +CERT_AddPermNickname(CERTCertificate *cert, char *nickname); + +/* + * Given a cert, find the cert with the same subject name that + * has the given key usage. If the given cert has the correct keyUsage, then + * return it, otherwise search the list in order. + */ +CERTCertificate * +CERT_FindCertByUsage(CERTCertificate *basecert, unsigned int requiredKeyUsage); + + +CERTCertList * +CERT_MatchUserCert(CERTCertDBHandle *handle, + SECCertUsage usage, + int nCANames, char **caNames, + void *proto_win); + +CERTCertList * +CERT_NewCertList(void); + +void +CERT_DestroyCertList(CERTCertList *certs); + +/* remove the node and free the cert */ +void +CERT_RemoveCertListNode(CERTCertListNode *node); + +SECStatus +CERT_AddCertToListTail(CERTCertList *certs, CERTCertificate *cert); + +SECStatus +CERT_AddCertToListHead(CERTCertList *certs, CERTCertificate *cert); + +SECStatus +CERT_AddCertToListTailWithData(CERTCertList *certs, CERTCertificate *cert, + void *appData); + +SECStatus +CERT_AddCertToListHeadWithData(CERTCertList *certs, CERTCertificate *cert, + void *appData); + +typedef PRBool (* CERTSortCallback)(CERTCertificate *certa, + CERTCertificate *certb, + void *arg); +SECStatus +CERT_AddCertToListSorted(CERTCertList *certs, CERTCertificate *cert, + CERTSortCallback f, void *arg); + +/* callback for CERT_AddCertToListSorted that sorts based on validity + * period and a given time. + */ +PRBool +CERT_SortCBValidity(CERTCertificate *certa, + CERTCertificate *certb, + void *arg); + +SECStatus +CERT_CheckForEvilCert(CERTCertificate *cert); + +CERTGeneralName * +CERT_GetCertificateNames(CERTCertificate *cert, PRArenaPool *arena); + +int +CERT_GetNamesLength(CERTGeneralName *names); + +CERTCertificate * +CERT_CompareNameSpace(CERTCertificate *cert, + CERTGeneralName *namesList, + SECItem *namesListIndex, + PRArenaPool *arena, + CERTCertDBHandle *handle); + +SECStatus +CERT_EncodeSubjectKeyID(PRArenaPool *arena, char *value, int len, SECItem *encodedValue); + +char * +CERT_GetNickName(CERTCertificate *cert, CERTCertDBHandle *handle, PRArenaPool *nicknameArena); + +/* + * Creates or adds to a list of all certs with a give subject name, sorted by + * validity time, newest first. Invalid certs are considered older than + * valid certs. If validOnly is set, do not include invalid certs on list. + */ +CERTCertList * +CERT_CreateSubjectCertList(CERTCertList *certList, CERTCertDBHandle *handle, + SECItem *name, int64 sorttime, PRBool validOnly); + +/* + * Creates or adds to a list of all certs with a give nickname, sorted by + * validity time, newest first. Invalid certs are considered older than valid + * certs. If validOnly is set, do not include invalid certs on list. + */ +CERTCertList * +CERT_CreateNicknameCertList(CERTCertList *certList, CERTCertDBHandle *handle, + char *nickname, int64 sorttime, PRBool validOnly); + +/* + * Creates or adds to a list of all certs with a give email addr, sorted by + * validity time, newest first. Invalid certs are considered older than valid + * certs. If validOnly is set, do not include invalid certs on list. + */ +CERTCertList * +CERT_CreateEmailAddrCertList(CERTCertList *certList, CERTCertDBHandle *handle, + char *emailAddr, int64 sorttime, PRBool validOnly); + +/* + * remove certs from a list that don't have keyUsage and certType + * that match the given usage. + */ +SECStatus +CERT_FilterCertListByUsage(CERTCertList *certList, SECCertUsage usage, + PRBool ca); + +/* + * check the key usage of a cert against a set of required values + */ +SECStatus +CERT_CheckKeyUsage(CERTCertificate *cert, unsigned int requiredUsage); + +/* + * return required key usage and cert type based on cert usage + */ +SECStatus +CERT_KeyUsageAndTypeForCertUsage(SECCertUsage usage, + PRBool ca, + unsigned int *retKeyUsage, + unsigned int *retCertType); +/* + * return required trust flags for various cert usages for CAs + */ +SECStatus +CERT_TrustFlagsForCACertUsage(SECCertUsage usage, + unsigned int *retFlags, + SECTrustType *retTrustType); + +/* + * Find all user certificates that match the given criteria. + * + * "handle" - database to search + * "usage" - certificate usage to match + * "oneCertPerName" - if set then only return the "best" cert per + * name + * "validOnly" - only return certs that are curently valid + * "proto_win" - window handle passed to pkcs11 + */ +CERTCertList * +CERT_FindUserCertsByUsage(CERTCertDBHandle *handle, + SECCertUsage usage, + PRBool oneCertPerName, + PRBool validOnly, + void *proto_win); + +/* + * Find a user certificate that matchs the given criteria. + * + * "handle" - database to search + * "nickname" - nickname to match + * "usage" - certificate usage to match + * "validOnly" - only return certs that are curently valid + * "proto_win" - window handle passed to pkcs11 + */ +CERTCertificate * +CERT_FindUserCertByUsage(CERTCertDBHandle *handle, + char *nickname, + SECCertUsage usage, + PRBool validOnly, + void *proto_win); + +/* + * Filter a list of certificates, removing those certs that do not have + * one of the named CA certs somewhere in their cert chain. + * + * "certList" - the list of certificates to filter + * "nCANames" - number of CA names + * "caNames" - array of CA names in string(rfc 1485) form + * "usage" - what use the certs are for, this is used when + * selecting CA certs + */ +SECStatus +CERT_FilterCertListByCANames(CERTCertList *certList, int nCANames, + char **caNames, SECCertUsage usage); + +/* + * Filter a list of certificates, removing those certs that aren't user certs + */ +SECStatus +CERT_FilterCertListForUserCerts(CERTCertList *certList); + +/* + * Collect the nicknames from all certs in a CertList. If the cert is not + * valid, append a string to that nickname. + * + * "certList" - the list of certificates + * "expiredString" - the string to append to the nickname of any expired cert + * "notYetGoodString" - the string to append to the nickname of any cert + * that is not yet valid + */ +CERTCertNicknames * +CERT_NicknameStringsFromCertList(CERTCertList *certList, char *expiredString, + char *notYetGoodString); + +/* + * Extract the nickname from a nickmake string that may have either + * expiredString or notYetGoodString appended. + * + * Args: + * "namestring" - the string containing the nickname, and possibly + * one of the validity label strings + * "expiredString" - the expired validity label string + * "notYetGoodString" - the not yet good validity label string + * + * Returns the raw nickname + */ +char * +CERT_ExtractNicknameString(char *namestring, char *expiredString, + char *notYetGoodString); + +/* + * Given a certificate, return a string containing the nickname, and possibly + * one of the validity strings, based on the current validity state of the + * certificate. + * + * "arena" - arena to allocate returned string from. If NULL, then heap + * is used. + * "cert" - the cert to get nickname from + * "expiredString" - the string to append to the nickname if the cert is + * expired. + * "notYetGoodString" - the string to append to the nickname if the cert is + * not yet good. + */ +char * +CERT_GetCertNicknameWithValidity(PRArenaPool *arena, CERTCertificate *cert, + char *expiredString, char *notYetGoodString); + +/* + * Return the string representation of a DER encoded distinguished name + * "dername" - The DER encoded name to convert + */ +char * +CERT_DerNameToAscii(SECItem *dername); + +/* + * Supported usage values and types: + * certUsageSSLClient + * certUsageSSLServer + * certUsageSSLServerWithStepUp + * certUsageEmailSigner + * certUsageEmailRecipient + * certUsageObjectSigner + */ + +CERTCertificate * +CERT_FindMatchingCert(CERTCertDBHandle *handle, SECItem *derName, + CERTCertOwner owner, SECCertUsage usage, + PRBool preferTrusted, int64 validTime, PRBool validOnly); + + +/*********************************************************************/ +/* A thread safe implementation of General Names */ +/*********************************************************************/ + +/* Destroy a Single CERTGeneralName */ +void +CERT_DestroyGeneralName(CERTGeneralName *name); + +/* Destroys a CERTGeneralNameList */ +void +CERT_DestroyGeneralNameList(CERTGeneralNameList *list); + +/* Creates a CERTGeneralNameList */ +CERTGeneralNameList * +CERT_CreateGeneralNameList(CERTGeneralName *name); + +/* Compares two CERTGeneralNameList */ +SECStatus +CERT_CompareGeneralNameLists(CERTGeneralNameList *a, CERTGeneralNameList *b); + +/* returns a copy of the first name of the type requested */ +void * +CERT_GetGeneralNameFromListByType(CERTGeneralNameList *list, + CERTGeneralNameType type, + PRArenaPool *arena); + +/* Adds a name to the tail of the list */ +void +CERT_AddGeneralNameToList(CERTGeneralNameList *list, + CERTGeneralNameType type, + void *data, SECItem *oid); + +/* returns a duplicate of the CERTGeneralNameList */ +CERTGeneralNameList * +CERT_DupGeneralNameList(CERTGeneralNameList *list); + +/* returns the length of a CERTGeneralName */ +int +CERT_GetNamesLength(CERTGeneralName *names); + +/* + * Acquire the global lock on the cert database. + * This lock is currently used for the following operations: + * adding or deleting a cert to either the temp or perm databases + * converting a temp to perm or perm to temp + * changing(maybe just adding?) the trust of a cert + * adjusting the reference count of a cert + */ +void +CERT_LockDB(CERTCertDBHandle *handle); + +/* + * Free the global cert database lock. + */ +void +CERT_UnlockDB(CERTCertDBHandle *handle); + +/* + * Get the certificate status checking configuratino data for + * the certificate database + */ +CERTStatusConfig * +CERT_GetStatusConfig(CERTCertDBHandle *handle); + +/* + * Set the certificate status checking information for the + * database. The input structure becomes part of the certificate + * database and will be freed by calling the 'Destroy' function in + * the configuration object. + */ +void +CERT_SetStatusConfig(CERTCertDBHandle *handle, CERTStatusConfig *config); + + + +/* + * Acquire the cert reference count lock + * There is currently one global lock for all certs, but I'm putting a cert + * arg here so that it will be easy to make it per-cert in the future if + * that turns out to be necessary. + */ +void +CERT_LockCertRefCount(CERTCertificate *cert); + +/* + * Free the cert reference count lock + */ +void +CERT_UnlockCertRefCount(CERTCertificate *cert); + +/* + * Acquire the cert trust lock + * There is currently one global lock for all certs, but I'm putting a cert + * arg here so that it will be easy to make it per-cert in the future if + * that turns out to be necessary. + */ +void +CERT_LockCertTrust(CERTCertificate *cert); + +/* + * Free the cert trust lock + */ +void +CERT_UnlockCertTrust(CERTCertificate *cert); + +/* + * Digest the cert's subject public key using the specified algorithm. + * The necessary storage for the digest data is allocated. If "fill" is + * non-null, the data is put there, otherwise a SECItem is allocated. + * Allocation from "arena" if it is non-null, heap otherwise. Any problem + * results in a NULL being returned (and an appropriate error set). + */ +extern SECItem * +CERT_SPKDigestValueForCert(PRArenaPool *arena, CERTCertificate *cert, + SECOidTag digestAlg, SECItem *fill); + +/* + * fill in nsCertType field of the cert based on the cert extension + */ +extern SECStatus cert_GetCertType(CERTCertificate *cert); + + +SECStatus CERT_CheckCRL(CERTCertificate* cert, CERTCertificate* issuer, + SECItem* dp, int64 t, void* wincx); + + +SEC_END_PROTOS + +#endif /* _CERT_H_ */ diff --git a/security/nss/lib/certdb/certdb.c b/security/nss/lib/certdb/certdb.c new file mode 100644 index 000000000..76c13e2de --- /dev/null +++ b/security/nss/lib/certdb/certdb.c @@ -0,0 +1,2918 @@ +/* + * 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. + */ + +/* + * Certificate handling code + * + * $Id$ + */ + +#include "nssilock.h" +#include "prmon.h" +#include "prtime.h" +#include "cert.h" +#include "certi.h" +#include "secder.h" +#include "secoid.h" +#include "secasn1.h" +#include "genname.h" +#include "keyhi.h" +#include "secitem.h" +#include "mcom_db.h" +#include "certdb.h" +#include "prprf.h" +#include "sechash.h" +#include "prlong.h" +#include "certxutl.h" +#include "portreg.h" +#include "secerr.h" +#include "sslerr.h" +#include "nsslocks.h" +#include "pk11func.h" +#include "xconst.h" /* for CERT_DecodeAltNameExtension */ + +#ifndef NSS_3_4_CODE +#define NSS_3_4_CODE +#endif /* NSS_3_4_CODE */ +#include "pki.h" +#include "pki3hack.h" + +/* + * Certificate database handling code + */ + + +const SEC_ASN1Template CERT_CertExtensionTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCertExtension) }, + { SEC_ASN1_OBJECT_ID, + offsetof(CERTCertExtension,id) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */ + offsetof(CERTCertExtension,critical) }, + { SEC_ASN1_OCTET_STRING, + offsetof(CERTCertExtension,value) }, + { 0, } +}; + +const SEC_ASN1Template CERT_SequenceOfCertExtensionTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, 0, CERT_CertExtensionTemplate } +}; + +const SEC_ASN1Template CERT_CertificateTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCertificate) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 0, /* XXX DER_DEFAULT */ + offsetof(CERTCertificate,version), + SEC_IntegerTemplate }, + { SEC_ASN1_INTEGER, + offsetof(CERTCertificate,serialNumber) }, + { SEC_ASN1_INLINE, + offsetof(CERTCertificate,signature), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_SAVE, + offsetof(CERTCertificate,derIssuer) }, + { SEC_ASN1_INLINE, + offsetof(CERTCertificate,issuer), + CERT_NameTemplate }, + { SEC_ASN1_INLINE, + offsetof(CERTCertificate,validity), + CERT_ValidityTemplate }, + { SEC_ASN1_SAVE, + offsetof(CERTCertificate,derSubject) }, + { SEC_ASN1_INLINE, + offsetof(CERTCertificate,subject), + CERT_NameTemplate }, + { SEC_ASN1_SAVE, + offsetof(CERTCertificate,derPublicKey) }, + { SEC_ASN1_INLINE, + offsetof(CERTCertificate,subjectPublicKeyInfo), + CERT_SubjectPublicKeyInfoTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(CERTCertificate,issuerID), + SEC_ObjectIDTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2, + offsetof(CERTCertificate,subjectID), + SEC_ObjectIDTemplate }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 3, + offsetof(CERTCertificate,extensions), + CERT_SequenceOfCertExtensionTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_SignedCertificateTemplate[] = +{ + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCertificate) }, + { SEC_ASN1_SAVE, + offsetof(CERTCertificate,signatureWrap.data) }, + { SEC_ASN1_INLINE, + 0, CERT_CertificateTemplate }, + { SEC_ASN1_INLINE, + offsetof(CERTCertificate,signatureWrap.signatureAlgorithm), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_BIT_STRING, + offsetof(CERTCertificate,signatureWrap.signature) }, + { 0 } +}; + +/* + * Find the subjectName in a DER encoded certificate + */ +const SEC_ASN1Template SEC_CertSubjectTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SECItem) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + 0, SEC_SkipTemplate }, /* version */ + { SEC_ASN1_SKIP }, /* serial number */ + { SEC_ASN1_SKIP }, /* signature algorithm */ + { SEC_ASN1_SKIP }, /* issuer */ + { SEC_ASN1_SKIP }, /* validity */ + { SEC_ASN1_ANY, 0, NULL }, /* subject */ + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +/* + * Find the issuerName in a DER encoded certificate + */ +const SEC_ASN1Template SEC_CertIssuerTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SECItem) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + 0, SEC_SkipTemplate }, /* version */ + { SEC_ASN1_SKIP }, /* serial number */ + { SEC_ASN1_SKIP }, /* signature algorithm */ + { SEC_ASN1_ANY, 0, NULL }, /* issuer */ + { SEC_ASN1_SKIP_REST }, + { 0 } +}; +/* + * Find the subjectName in a DER encoded certificate + */ +const SEC_ASN1Template SEC_CertSerialNumberTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SECItem) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + 0, SEC_SkipTemplate }, /* version */ + { SEC_ASN1_ANY, 0, NULL }, /* serial number */ + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +/* + * Find the issuer and serialNumber in a DER encoded certificate. + * This data is used as the database lookup key since its the unique + * identifier of a certificate. + */ +const SEC_ASN1Template CERT_CertKeyTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCertKey) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + 0, SEC_SkipTemplate }, /* version */ + { SEC_ASN1_INTEGER, + offsetof(CERTCertKey,serialNumber) }, + { SEC_ASN1_SKIP }, /* signature algorithm */ + { SEC_ASN1_ANY, + offsetof(CERTCertKey,derIssuer) }, + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SignedCertificateTemplate) + +SECStatus +CERT_KeyFromIssuerAndSN(PRArenaPool *arena, SECItem *issuer, SECItem *sn, + SECItem *key) +{ + key->len = sn->len + issuer->len; + + if ((sn->data == NULL) || (issuer->data == NULL)) { + goto loser; + } + + key->data = (unsigned char*)PORT_ArenaAlloc(arena, key->len); + if ( !key->data ) { + goto loser; + } + + /* copy the serialNumber */ + PORT_Memcpy(key->data, sn->data, sn->len); + + /* copy the issuer */ + PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len); + + return(SECSuccess); + +loser: + return(SECFailure); +} + + +/* + * Extract the subject name from a DER certificate + */ +SECStatus +CERT_NameFromDERCert(SECItem *derCert, SECItem *derName) +{ + int rv; + PRArenaPool *arena; + CERTSignedData sd; + void *tmpptr; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if ( ! arena ) { + return(SECFailure); + } + + PORT_Memset(&sd, 0, sizeof(CERTSignedData)); + rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert); + + if ( rv ) { + goto loser; + } + + PORT_Memset(derName, 0, sizeof(SECItem)); + rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertSubjectTemplate, &sd.data); + + if ( rv ) { + goto loser; + } + + tmpptr = derName->data; + derName->data = (unsigned char*)PORT_Alloc(derName->len); + if ( derName->data == NULL ) { + goto loser; + } + + PORT_Memcpy(derName->data, tmpptr, derName->len); + + PORT_FreeArena(arena, PR_FALSE); + return(SECSuccess); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return(SECFailure); +} + +SECStatus +CERT_IssuerNameFromDERCert(SECItem *derCert, SECItem *derName) +{ + int rv; + PRArenaPool *arena; + CERTSignedData sd; + void *tmpptr; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if ( ! arena ) { + return(SECFailure); + } + + PORT_Memset(&sd, 0, sizeof(CERTSignedData)); + rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert); + + if ( rv ) { + goto loser; + } + + PORT_Memset(derName, 0, sizeof(SECItem)); + rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertIssuerTemplate, &sd.data); + + if ( rv ) { + goto loser; + } + + tmpptr = derName->data; + derName->data = (unsigned char*)PORT_Alloc(derName->len); + if ( derName->data == NULL ) { + goto loser; + } + + PORT_Memcpy(derName->data, tmpptr, derName->len); + + PORT_FreeArena(arena, PR_FALSE); + return(SECSuccess); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return(SECFailure); +} + +SECStatus +CERT_SerialNumberFromDERCert(SECItem *derCert, SECItem *derName) +{ + int rv; + PRArenaPool *arena; + CERTSignedData sd; + void *tmpptr; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if ( ! arena ) { + return(SECFailure); + } + + PORT_Memset(&sd, 0, sizeof(CERTSignedData)); + rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert); + + if ( rv ) { + goto loser; + } + + PORT_Memset(derName, 0, sizeof(SECItem)); + rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertSerialNumberTemplate, &sd.data); + + if ( rv ) { + goto loser; + } + + tmpptr = derName->data; + derName->data = (unsigned char*)PORT_Alloc(derName->len); + if ( derName->data == NULL ) { + goto loser; + } + + PORT_Memcpy(derName->data, tmpptr, derName->len); + + PORT_FreeArena(arena, PR_FALSE); + return(SECSuccess); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return(SECFailure); +} + +/* + * Generate a database key, based on serial number and issuer, from a + * DER certificate. + */ +SECStatus +CERT_KeyFromDERCert(PRArenaPool *arena, SECItem *derCert, SECItem *key) +{ + int rv; + CERTSignedData sd; + CERTCertKey certkey; + + PORT_Memset(&sd, 0, sizeof(CERTSignedData)); + rv = SEC_ASN1DecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert); + + if ( rv ) { + goto loser; + } + + PORT_Memset(&certkey, 0, sizeof(CERTCertKey)); + rv = SEC_ASN1DecodeItem(arena, &certkey, CERT_CertKeyTemplate, &sd.data); + + if ( rv ) { + goto loser; + } + + return(CERT_KeyFromIssuerAndSN(arena, &certkey.derIssuer, + &certkey.serialNumber, key)); +loser: + return(SECFailure); +} + +/* + * fill in keyUsage field of the cert based on the cert extension + * if the extension is not critical, then we allow all uses + */ +static SECStatus +GetKeyUsage(CERTCertificate *cert) +{ + SECStatus rv; + SECItem tmpitem; + + rv = CERT_FindKeyUsageExtension(cert, &tmpitem); + if ( rv == SECSuccess ) { + /* remember the actual value of the extension */ + cert->rawKeyUsage = tmpitem.data[0]; + cert->keyUsagePresent = PR_TRUE; + cert->keyUsage = tmpitem.data[0]; + + PORT_Free(tmpitem.data); + tmpitem.data = NULL; + + } else { + /* if the extension is not present, then we allow all uses */ + cert->keyUsage = KU_ALL; + cert->rawKeyUsage = KU_ALL; + cert->keyUsagePresent = PR_FALSE; + } + + if ( CERT_GovtApprovedBitSet(cert) ) { + cert->keyUsage |= KU_NS_GOVT_APPROVED; + cert->rawKeyUsage |= KU_NS_GOVT_APPROVED; + } + + return(SECSuccess); +} + + +/* + * determine if a fortezza V1 Cert is a CA or not. + */ +static PRBool +fortezzaIsCA( CERTCertificate *cert) { + PRBool isCA = PR_FALSE; + CERTSubjectPublicKeyInfo *spki = &cert->subjectPublicKeyInfo; + int tag; + + tag = SECOID_GetAlgorithmTag(&spki->algorithm); + if ((tag == SEC_OID_MISSI_KEA_DSS_OLD) || + (tag == SEC_OID_MISSI_KEA_DSS) || + (tag == SEC_OID_MISSI_DSS_OLD) || + (tag == SEC_OID_MISSI_DSS) ) { + SECItem rawkey; + unsigned char *rawptr; + unsigned char *end; + int len; + + rawkey = spki->subjectPublicKey; + DER_ConvertBitString(&rawkey); + rawptr = rawkey.data; + end = rawkey.data + rawkey.len; + + /* version */ + rawptr += sizeof(((SECKEYPublicKey*)0)->u.fortezza.KMID)+2; + + /* clearance (the string up to the first byte with the hi-bit on */ + while ((rawptr < end) && (*rawptr++ & 0x80)); + if (rawptr >= end) { return PR_FALSE; } + + /* KEAPrivilege (the string up to the first byte with the hi-bit on */ + while ((rawptr < end) && (*rawptr++ & 0x80)); + if (rawptr >= end) { return PR_FALSE; } + + /* skip the key */ + len = (*rawptr << 8) | rawptr[1]; + rawptr += 2 + len; + + /* shared key */ + if (rawptr >= end) { return PR_FALSE; } + /* DSS Version is next */ + rawptr += 2; + + /* DSSPrivilege (the string up to the first byte with the hi-bit on */ + if (*rawptr & 0x30) isCA = PR_TRUE; + + } + return isCA; +} + +static SECStatus +findOIDinOIDSeqByTagNum(CERTOidSequence *seq, SECOidTag tagnum) +{ + SECItem **oids; + SECItem *oid; + SECStatus rv = SECFailure; + + if (seq != NULL) { + oids = seq->oids; + while (oids != NULL && *oids != NULL) { + oid = *oids; + if (SECOID_FindOIDTag(oid) == tagnum) { + rv = SECSuccess; + break; + } + oids++; + } + } + return rv; +} + +/* + * fill in nsCertType field of the cert based on the cert extension + */ +SECStatus +cert_GetCertType(CERTCertificate *cert) +{ + SECStatus rv; + SECItem tmpitem; + SECItem encodedExtKeyUsage; + CERTOidSequence *extKeyUsage = NULL; + PRBool basicConstraintPresent = PR_FALSE; + CERTBasicConstraints basicConstraint; + unsigned int nsCertType = 0; + + if (cert->nsCertType) { + /* once set, no need to recalculate */ + return SECSuccess; + } + + tmpitem.data = NULL; + CERT_FindNSCertTypeExtension(cert, &tmpitem); + rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE, + &encodedExtKeyUsage); + if (rv == SECSuccess) { + extKeyUsage = CERT_DecodeOidSequence(&encodedExtKeyUsage); + } + rv = CERT_FindBasicConstraintExten(cert, &basicConstraint); + if (rv == SECSuccess) { + basicConstraintPresent = PR_TRUE; + } + if (tmpitem.data != NULL || extKeyUsage != NULL) { + if (tmpitem.data == NULL) { + nsCertType = 0; + } else { + nsCertType = tmpitem.data[0]; + } + + /* free tmpitem data pointer to avoid memory leak */ + PORT_Free(tmpitem.data); + tmpitem.data = NULL; + + /* + * for this release, we will allow SSL certs with an email address + * to be used for email + */ + if ( ( nsCertType & NS_CERT_TYPE_SSL_CLIENT ) && + cert->emailAddr ) { + nsCertType |= NS_CERT_TYPE_EMAIL; + } + /* + * for this release, we will allow SSL intermediate CAs to be + * email intermediate CAs too. + */ + if ( nsCertType & NS_CERT_TYPE_SSL_CA ) { + nsCertType |= NS_CERT_TYPE_EMAIL_CA; + } + /* + * allow a cert with the extended key usage of EMail Protect + * to be used for email or as an email CA, if basic constraints + * indicates that it is a CA. + */ + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT) == + SECSuccess) { + if (basicConstraintPresent == PR_TRUE && + (basicConstraint.isCA)) { + nsCertType |= NS_CERT_TYPE_EMAIL_CA; + } else { + nsCertType |= NS_CERT_TYPE_EMAIL; + } + } + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_EXT_KEY_USAGE_SERVER_AUTH) == + SECSuccess){ + if (basicConstraintPresent == PR_TRUE && + (basicConstraint.isCA)) { + nsCertType |= NS_CERT_TYPE_SSL_CA; + } else { + nsCertType |= NS_CERT_TYPE_SSL_SERVER; + } + } + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH) == + SECSuccess){ + if (basicConstraintPresent == PR_TRUE && + (basicConstraint.isCA)) { + nsCertType |= NS_CERT_TYPE_SSL_CA; + } else { + nsCertType |= NS_CERT_TYPE_SSL_CLIENT; + } + } + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_EXT_KEY_USAGE_CODE_SIGN) == + SECSuccess) { + if (basicConstraintPresent == PR_TRUE && + (basicConstraint.isCA)) { + nsCertType |= NS_CERT_TYPE_OBJECT_SIGNING_CA; + } else { + nsCertType |= NS_CERT_TYPE_OBJECT_SIGNING; + } + } + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_EXT_KEY_USAGE_TIME_STAMP) == + SECSuccess) { + nsCertType |= EXT_KEY_USAGE_TIME_STAMP; + } + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_OCSP_RESPONDER) == + SECSuccess) { + nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER; + } + } else { + /* if no extension, then allow any ssl or email (no ca or object + * signing) + */ + nsCertType = NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER | + NS_CERT_TYPE_EMAIL; + + /* if the basic constraint extension says the cert is a CA, then + allow SSL CA and EMAIL CA and Status Responder */ + if ((basicConstraintPresent == PR_TRUE) + && (basicConstraint.isCA)) { + nsCertType |= NS_CERT_TYPE_SSL_CA; + nsCertType |= NS_CERT_TYPE_EMAIL_CA; + nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER; + } else if (CERT_IsCACert(cert, NULL) == PR_TRUE) { + nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER; + } + + /* if the cert is a fortezza CA cert, then allow SSL CA and EMAIL CA */ + if (fortezzaIsCA(cert)) { + nsCertType |= NS_CERT_TYPE_SSL_CA; + nsCertType |= NS_CERT_TYPE_EMAIL_CA; + } + } + + if (extKeyUsage != NULL) { + PORT_Free(encodedExtKeyUsage.data); + CERT_DestroyOidSequence(extKeyUsage); + } + PR_AtomicSet(&cert->nsCertType, nsCertType); + return(SECSuccess); +} + +/* + * cert_GetKeyID() - extract or generate the subjectKeyID from a certificate + */ +SECStatus +cert_GetKeyID(CERTCertificate *cert) +{ + SECItem tmpitem; + SECStatus rv; + SECKEYPublicKey *key; + + cert->subjectKeyID.len = 0; + + /* see of the cert has a key identifier extension */ + rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem); + if ( rv == SECSuccess ) { + cert->subjectKeyID.data = (unsigned char*) PORT_ArenaAlloc(cert->arena, tmpitem.len); + if ( cert->subjectKeyID.data != NULL ) { + PORT_Memcpy(cert->subjectKeyID.data, tmpitem.data, tmpitem.len); + cert->subjectKeyID.len = tmpitem.len; + cert->keyIDGenerated = PR_FALSE; + } + + PORT_Free(tmpitem.data); + } + + /* if the cert doesn't have a key identifier extension and the cert is + * a V1 fortezza certificate, use the cert's 8 byte KMID as the + * key identifier. */ + key = CERT_KMIDPublicKey(cert); + + if (key != NULL) { + + if (key->keyType == fortezzaKey) { + + cert->subjectKeyID.data = (unsigned char *)PORT_ArenaAlloc(cert->arena, 8); + if ( cert->subjectKeyID.data != NULL ) { + PORT_Memcpy(cert->subjectKeyID.data, key->u.fortezza.KMID, 8); + cert->subjectKeyID.len = 8; + cert->keyIDGenerated = PR_FALSE; + } + } + + SECKEY_DestroyPublicKey(key); + } + + /* if the cert doesn't have a key identifier extension, then generate one*/ + if ( cert->subjectKeyID.len == 0 ) { + /* + * pkix says that if the subjectKeyID is not present, then we should + * use the SHA-1 hash of the DER-encoded publicKeyInfo from the cert + */ + cert->subjectKeyID.data = (unsigned char *)PORT_ArenaAlloc(cert->arena, SHA1_LENGTH); + if ( cert->subjectKeyID.data != NULL ) { + rv = PK11_HashBuf(SEC_OID_SHA1,cert->subjectKeyID.data, + cert->derPublicKey.data, + cert->derPublicKey.len); + if ( rv == SECSuccess ) { + cert->subjectKeyID.len = SHA1_LENGTH; + } + } + } + + if ( cert->subjectKeyID.len == 0 ) { + return(SECFailure); + } + return(SECSuccess); + +} + +static PRBool +cert_IsRootCert(CERTCertificate *cert) +{ + SECStatus rv; + SECItem tmpitem; + + /* cache the authKeyID extension, if present */ + cert->authKeyID = CERT_FindAuthKeyIDExten(cert->arena, cert); + + /* it MUST be self-issued to be a root */ + if (cert->derIssuer.len == 0 || + !SECITEM_ItemsAreEqual(&cert->derIssuer, &cert->derSubject)) + { + return PR_FALSE; + } + + /* check the authKeyID extension */ + if (cert->authKeyID) { + /* authority key identifier is present */ + if (cert->authKeyID->keyID.len > 0) { + /* the keyIdentifier field is set, look for subjectKeyID */ + rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem); + if (rv == SECSuccess) { + PRBool match; + /* also present, they MUST match for it to be a root */ + match = SECITEM_ItemsAreEqual(&cert->authKeyID->keyID, + &tmpitem); + PORT_Free(tmpitem.data); + if (!match) return PR_FALSE; /* else fall through */ + } else { + /* the subject key ID is required when AKI is present */ + return PR_FALSE; + } + } + if (cert->authKeyID->authCertIssuer) { + SECItem *caName; + caName = (SECItem *)CERT_GetGeneralNameByType( + cert->authKeyID->authCertIssuer, + certDirectoryName, PR_TRUE); + if (caName) { + if (!SECITEM_ItemsAreEqual(&cert->derIssuer, caName)) { + return PR_FALSE; + } /* else fall through */ + } /* else ??? could not get general name as directory name? */ + } + if (cert->authKeyID->authCertSerialNumber.len > 0) { + if (!SECITEM_ItemsAreEqual(&cert->serialNumber, + &cert->authKeyID->authCertSerialNumber)) { + return PR_FALSE; + } /* else fall through */ + } + /* all of the AKI fields that were present passed the test */ + return PR_TRUE; + } + /* else the AKI was not present, so this is a root */ + return PR_TRUE; +} + +/* + * take a DER certificate and decode it into a certificate structure + */ +CERTCertificate * +CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER, + char *nickname) +{ + CERTCertificate *cert; + PRArenaPool *arena; + void *data; + int rv; + int len; + char *tmpname; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if ( !arena ) { + return 0; + } + + /* allocate the certificate structure */ + cert = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate)); + + if ( !cert ) { + goto loser; + } + + cert->arena = arena; + + if ( copyDER ) { + /* copy the DER data for the cert into this arena */ + data = (void *)PORT_ArenaAlloc(arena, derSignedCert->len); + if ( !data ) { + goto loser; + } + cert->derCert.data = (unsigned char *)data; + cert->derCert.len = derSignedCert->len; + PORT_Memcpy(data, derSignedCert->data, derSignedCert->len); + } else { + /* point to passed in DER data */ + cert->derCert = *derSignedCert; + } + + /* decode the certificate info */ + rv = SEC_QuickDERDecodeItem(arena, cert, SEC_SignedCertificateTemplate, + &cert->derCert); + + if ( rv ) { + goto loser; + } + + if (cert_HasUnknownCriticalExten (cert->extensions) == PR_TRUE) { + PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); + goto loser; + } + + /* generate and save the database key for the cert */ + rv = CERT_KeyFromIssuerAndSN(arena, &cert->derIssuer, &cert->serialNumber, + &cert->certKey); + if ( rv ) { + goto loser; + } + + /* set the nickname */ + if ( nickname == NULL ) { + cert->nickname = NULL; + } else { + /* copy and install the nickname */ + len = PORT_Strlen(nickname) + 1; + cert->nickname = (char*)PORT_ArenaAlloc(arena, len); + if ( cert->nickname == NULL ) { + goto loser; + } + + PORT_Memcpy(cert->nickname, nickname, len); + } + + /* set the email address */ + cert->emailAddr = cert_GetCertificateEmailAddresses(cert); + + /* initialize the subjectKeyID */ + rv = cert_GetKeyID(cert); + if ( rv != SECSuccess ) { + goto loser; + } + + /* initialize keyUsage */ + rv = GetKeyUsage(cert); + if ( rv != SECSuccess ) { + goto loser; + } + + /* initialize the certType */ + rv = cert_GetCertType(cert); + if ( rv != SECSuccess ) { + goto loser; + } + + /* determine if this is a root cert */ + cert->isRoot = cert_IsRootCert(cert); + + tmpname = CERT_NameToAscii(&cert->subject); + if ( tmpname != NULL ) { + cert->subjectName = PORT_ArenaStrdup(cert->arena, tmpname); + PORT_Free(tmpname); + } + + tmpname = CERT_NameToAscii(&cert->issuer); + if ( tmpname != NULL ) { + cert->issuerName = PORT_ArenaStrdup(cert->arena, tmpname); + PORT_Free(tmpname); + } + + cert->referenceCount = 1; + cert->slot = NULL; + cert->pkcs11ID = CK_INVALID_HANDLE; + cert->dbnickname = NULL; + + return(cert); + +loser: + + if ( arena ) { + PORT_FreeArena(arena, PR_FALSE); + } + + return(0); +} + +CERTCertificate * +__CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER, + char *nickname) +{ + return CERT_DecodeDERCertificate(derSignedCert, copyDER, nickname); +} + + +/* +** Amount of time that a certifiate is allowed good before it is actually +** good. This is used for pending certificates, ones that are about to be +** valid. The slop is designed to allow for some variance in the clocks +** of the machine checking the certificate. +*/ +#define PENDING_SLOP (24L*60L*60L) /* seconds per day */ +static PRInt32 pendingSlop = PENDING_SLOP; /* seconds */ + +PRInt32 +CERT_GetSlopTime(void) +{ + return pendingSlop; /* seconds */ +} + +SECStatus +CERT_SetSlopTime(PRInt32 slop) /* seconds */ +{ + if (slop < 0) + return SECFailure; + pendingSlop = slop; + return SECSuccess; +} + +SECStatus +CERT_GetCertTimes(CERTCertificate *c, PRTime *notBefore, PRTime *notAfter) +{ + int rv; + + /* convert DER not-before time */ + rv = DER_UTCTimeToTime(notBefore, &c->validity.notBefore); + if (rv) { + return(SECFailure); + } + + /* convert DER not-after time */ + rv = DER_UTCTimeToTime(notAfter, &c->validity.notAfter); + if (rv) { + return(SECFailure); + } + + return(SECSuccess); +} + +/* + * Check the validity times of a certificate + */ +SECCertTimeValidity +CERT_CheckCertValidTimes(CERTCertificate *c, PRTime t, PRBool allowOverride) +{ + PRTime notBefore, notAfter, llPendingSlop, tmp1; + SECStatus rv; + + /* if cert is already marked OK, then don't bother to check */ + if ( allowOverride && c->timeOK ) { + return(secCertTimeValid); + } + + rv = CERT_GetCertTimes(c, ¬Before, ¬After); + + if (rv) { + return(secCertTimeExpired); /*XXX is this the right thing to do here?*/ + } + + LL_I2L(llPendingSlop, pendingSlop); + /* convert to micro seconds */ + LL_UI2L(tmp1, PR_USEC_PER_SEC); + LL_MUL(llPendingSlop, llPendingSlop, tmp1); + LL_SUB(notBefore, notBefore, llPendingSlop); + if ( LL_CMP( t, <, notBefore ) ) { + PORT_SetError(SEC_ERROR_EXPIRED_CERTIFICATE); + return(secCertTimeNotValidYet); + } + if ( LL_CMP( t, >, notAfter) ) { + PORT_SetError(SEC_ERROR_EXPIRED_CERTIFICATE); + return(secCertTimeExpired); + } + + return(secCertTimeValid); +} + +SECStatus +SEC_GetCrlTimes(CERTCrl *date, PRTime *notBefore, PRTime *notAfter) +{ + int rv; + + /* convert DER not-before time */ + rv = DER_UTCTimeToTime(notBefore, &date->lastUpdate); + if (rv) { + return(SECFailure); + } + + /* convert DER not-after time */ + if (date->nextUpdate.data) { + rv = DER_UTCTimeToTime(notAfter, &date->nextUpdate); + if (rv) { + return(SECFailure); + } + } + else { + LL_I2L(*notAfter, 0L); + } + return(SECSuccess); +} + +/* These routines should probably be combined with the cert + * routines using an common extraction routine. + */ +SECCertTimeValidity +SEC_CheckCrlTimes(CERTCrl *crl, PRTime t) { + PRTime notBefore, notAfter, llPendingSlop, tmp1; + SECStatus rv; + + rv = SEC_GetCrlTimes(crl, ¬Before, ¬After); + + if (rv) { + return(secCertTimeExpired); + } + + LL_I2L(llPendingSlop, pendingSlop); + /* convert to micro seconds */ + LL_I2L(tmp1, PR_USEC_PER_SEC); + LL_MUL(llPendingSlop, llPendingSlop, tmp1); + LL_SUB(notBefore, notBefore, llPendingSlop); + if ( LL_CMP( t, <, notBefore ) ) { + return(secCertTimeNotValidYet); + } + + /* If next update is omitted and the test for notBefore passes, then + we assume that the crl is up to date. + */ + if ( LL_IS_ZERO(notAfter) ) { + return(secCertTimeValid); + } + + if ( LL_CMP( t, >, notAfter) ) { + return(secCertTimeExpired); + } + + return(secCertTimeValid); +} + +PRBool +SEC_CrlIsNewer(CERTCrl *inNew, CERTCrl *old) { + PRTime newNotBefore, newNotAfter; + PRTime oldNotBefore, oldNotAfter; + SECStatus rv; + + /* problems with the new CRL? reject it */ + rv = SEC_GetCrlTimes(inNew, &newNotBefore, &newNotAfter); + if (rv) return PR_FALSE; + + /* problems with the old CRL? replace it */ + rv = SEC_GetCrlTimes(old, &oldNotBefore, &oldNotAfter); + if (rv) return PR_TRUE; + + /* Question: what about the notAfter's? */ + return ((PRBool)LL_CMP(oldNotBefore, <, newNotBefore)); +} + +/* + * return required key usage and cert type based on cert usage + */ +SECStatus +CERT_KeyUsageAndTypeForCertUsage(SECCertUsage usage, + PRBool ca, + unsigned int *retKeyUsage, + unsigned int *retCertType) +{ + unsigned int requiredKeyUsage = 0; + unsigned int requiredCertType = 0; + + if ( ca ) { + switch ( usage ) { + case certUsageSSLServerWithStepUp: + requiredKeyUsage = KU_NS_GOVT_APPROVED | KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_SSL_CA; + break; + case certUsageSSLClient: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_SSL_CA; + break; + case certUsageSSLServer: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_SSL_CA; + break; + case certUsageSSLCA: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_SSL_CA; + break; + case certUsageEmailSigner: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_EMAIL_CA; + break; + case certUsageEmailRecipient: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_EMAIL_CA; + break; + case certUsageObjectSigner: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING_CA; + break; + case certUsageAnyCA: + case certUsageStatusResponder: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING_CA | + NS_CERT_TYPE_EMAIL_CA | + NS_CERT_TYPE_SSL_CA; + break; + default: + PORT_Assert(0); + goto loser; + } + } else { + switch ( usage ) { + case certUsageSSLClient: + requiredKeyUsage = KU_DIGITAL_SIGNATURE; + requiredCertType = NS_CERT_TYPE_SSL_CLIENT; + break; + case certUsageSSLServer: + requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT; + requiredCertType = NS_CERT_TYPE_SSL_SERVER; + break; + case certUsageSSLServerWithStepUp: + requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT | + KU_NS_GOVT_APPROVED; + requiredCertType = NS_CERT_TYPE_SSL_SERVER; + break; + case certUsageSSLCA: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_SSL_CA; + break; + case certUsageEmailSigner: + requiredKeyUsage = KU_DIGITAL_SIGNATURE; + requiredCertType = NS_CERT_TYPE_EMAIL; + break; + case certUsageEmailRecipient: + requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT; + requiredCertType = NS_CERT_TYPE_EMAIL; + break; + case certUsageObjectSigner: + requiredKeyUsage = KU_DIGITAL_SIGNATURE; + requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING; + break; + case certUsageStatusResponder: + requiredKeyUsage = KU_DIGITAL_SIGNATURE; + requiredCertType = EXT_KEY_USAGE_STATUS_RESPONDER; + break; + default: + PORT_Assert(0); + goto loser; + } + } + + if ( retKeyUsage != NULL ) { + *retKeyUsage = requiredKeyUsage; + } + if ( retCertType != NULL ) { + *retCertType = requiredCertType; + } + + return(SECSuccess); +loser: + return(SECFailure); +} + +/* + * check the key usage of a cert against a set of required values + */ +SECStatus +CERT_CheckKeyUsage(CERTCertificate *cert, unsigned int requiredUsage) +{ + SECKEYPublicKey *key; + + /* choose between key agreement or key encipherment based on key + * type in cert + */ + if ( requiredUsage & KU_KEY_AGREEMENT_OR_ENCIPHERMENT ) { + key = CERT_ExtractPublicKey(cert); + if (!key) + return SECFailure; + if ( ( key->keyType == keaKey ) || ( key->keyType == fortezzaKey ) || + ( key->keyType == dhKey ) ) { + requiredUsage |= KU_KEY_AGREEMENT; + } else { + requiredUsage |= KU_KEY_ENCIPHERMENT; + } + + /* now turn off the special bit */ + requiredUsage &= (~KU_KEY_AGREEMENT_OR_ENCIPHERMENT); + + SECKEY_DestroyPublicKey(key); + } + + if ( ( cert->keyUsage & requiredUsage ) != requiredUsage ) { + return(SECFailure); + } + return(SECSuccess); +} + + +CERTCertificate * +CERT_DupCertificate(CERTCertificate *c) +{ + if (c) { +#ifdef NSS_CLASSIC + CERT_LockCertRefCount(c); + ++c->referenceCount; + CERT_UnlockCertRefCount(c); +#else + NSSCertificate *tmp = STAN_GetNSSCertificate(c); + nssCertificate_AddRef(tmp); +#endif + } + return c; +} + +/* + * Allow use of default cert database, so that apps(such as mozilla) don't + * have to pass the handle all over the place. + */ +static CERTCertDBHandle *default_cert_db_handle = 0; + +void +CERT_SetDefaultCertDB(CERTCertDBHandle *handle) +{ + default_cert_db_handle = handle; + + return; +} + +CERTCertDBHandle * +CERT_GetDefaultCertDB(void) +{ + return(default_cert_db_handle); +} + +/* XXX this would probably be okay/better as an xp routine? */ +static void +sec_lower_string(char *s) +{ + if ( s == NULL ) { + return; + } + + while ( *s ) { + *s = PORT_Tolower(*s); + s++; + } + + return; +} + +/* +** Add a domain name to the list of names that the user has explicitly +** allowed (despite cert name mismatches) for use with a server cert. +*/ +SECStatus +CERT_AddOKDomainName(CERTCertificate *cert, const char *hn) +{ + CERTOKDomainName *domainOK; + int newNameLen; + + if (!hn || !(newNameLen = strlen(hn))) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + domainOK = (CERTOKDomainName *)PORT_ArenaZAlloc(cert->arena, + (sizeof *domainOK) + newNameLen); + if (!domainOK) + return SECFailure; /* error code is already set. */ + + PORT_Strcpy(domainOK->name, hn); + sec_lower_string(domainOK->name); + + /* put at head of list. */ + domainOK->next = cert->domainOK; + cert->domainOK = domainOK; + return SECSuccess; +} + +/* returns SECSuccess if hn matches pattern cn, +** returns SECFailure with SSL_ERROR_BAD_CERT_DOMAIN if no match, +** returns SECFailure with some other error code if another error occurs. +** +** may modify cn, so caller must pass a modifiable copy. +*/ +static SECStatus +cert_TestHostName(char * cn, const char * hn) +{ + char * hndomain; + int regvalid; + + if ((hndomain = PORT_Strchr(hn, '.')) == NULL) { + /* No domain in URI host name */ + char * cndomain; + if ((cndomain = PORT_Strchr(cn, '.')) != NULL && + (cndomain - cn) > 0) { + /* there is a domain in the cn string, so chop it off */ + *cndomain = '\0'; + } + } + + regvalid = PORT_RegExpValid(cn); + if (regvalid != NON_SXP) { + SECStatus rv; + /* cn is a regular expression, try to match the shexp */ + int match = PORT_RegExpCaseSearch(hn, cn); + + if ( match == 0 ) { + rv = SECSuccess; + } else { + PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); + rv = SECFailure; + } + return rv; + } + /* cn is not a regular expression */ + + /* compare entire hn with cert name */ + if (PORT_Strcasecmp(hn, cn) == 0) { + return SECSuccess; + } + + if ( hndomain ) { + /* compare just domain name with cert name */ + if ( PORT_Strcasecmp(hndomain+1, cn) == 0 ) { + return SECSuccess; + } + } + + PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); + return SECFailure; +} + + +SECStatus +cert_VerifySubjectAltName(CERTCertificate *cert, const char *hn) +{ + PRArenaPool * arena = NULL; + CERTGeneralName * nameList = NULL; + CERTGeneralName * current; + char * cn; + int cnBufLen; + unsigned int hnLen; + int DNSextCount = 0; + int IPextCount = 0; + PRBool isIPaddr; + SECStatus rv = SECFailure; + SECItem subAltName; + PRNetAddr netAddr; + char cnbuf[128]; + + subAltName.data = NULL; + hnLen = strlen(hn); + cn = cnbuf; + cnBufLen = sizeof cnbuf; + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, + &subAltName); + if (rv != SECSuccess) { + goto finish; + } + isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr)); + rv = SECFailure; + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) + goto finish; + + nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName); + if (!current) + goto finish; + + do { + switch (current->type) { + case certDNSName: + if (!isIPaddr) { + /* DNS name current->name.other.data is not null terminated. + ** so must copy it. + */ + int cnLen = current->name.other.len; + if (cnLen + 1 > cnBufLen) { + cnBufLen = cnLen + 1; + cn = (char *)PORT_ArenaAlloc(arena, cnBufLen); + if (!cn) + goto finish; + } + PORT_Memcpy(cn, current->name.other.data, cnLen); + cn[cnLen] = 0; + rv = cert_TestHostName(cn ,hn); + if (rv == SECSuccess) + goto finish; + } + DNSextCount++; + break; + case certIPAddress: + if (isIPaddr) { + int match = 0; + PRIPv6Addr v6Addr; + if (current->name.other.len == 4 && /* IP v4 address */ + netAddr.inet.family == PR_AF_INET) { + match = !memcmp(&netAddr.inet.ip, + current->name.other.data, 4); + } else if (current->name.other.len == 16 && /* IP v6 address */ + netAddr.ipv6.family == PR_AF_INET6) { + match = !memcmp(&netAddr.ipv6.ip, + current->name.other.data, 16); + } else if (current->name.other.len == 16 && /* IP v6 address */ + netAddr.inet.family == PR_AF_INET) { + /* convert netAddr to ipv6, then compare. */ + /* ipv4 must be in Network Byte Order on input. */ + PR_ConvertIPv4AddrToIPv6(netAddr.inet.ip, &v6Addr); + match = !memcmp(&v6Addr, current->name.other.data, 16); + } else if (current->name.other.len == 4 && /* IP v4 address */ + netAddr.inet.family == PR_AF_INET6) { + /* convert netAddr to ipv6, then compare. */ + PRUint32 ipv4 = (current->name.other.data[0] << 24) | + (current->name.other.data[1] << 16) | + (current->name.other.data[2] << 8) | + current->name.other.data[3]; + /* ipv4 must be in Network Byte Order on input. */ + PR_ConvertIPv4AddrToIPv6(PR_htonl(ipv4), &v6Addr); + match = !memcmp(&netAddr.ipv6.ip, &v6Addr, 16); + } + if (match) { + rv = SECSuccess; + goto finish; + } + } + IPextCount++; + break; + default: + break; + } + current = cert_get_next_general_name(current); + } while (current != nameList); + + if ((!isIPaddr && !DNSextCount) || (isIPaddr && !IPextCount)) { + /* no relevant value in the extension was found. */ + PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); + } else { + PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); + } + rv = SECFailure; + +finish: + + /* Don't free nameList, it's part of the arena. */ + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + + if (subAltName.data) { + SECITEM_FreeItem(&subAltName, PR_FALSE); + } + + return rv; +} + + +/* Make sure that the name of the host we are connecting to matches the + * name that is incoded in the common-name component of the certificate + * that they are using. + */ +SECStatus +CERT_VerifyCertName(CERTCertificate *cert, const char *hn) +{ + char * cn; + SECStatus rv; + CERTOKDomainName *domainOK; + + if (!hn || !strlen(hn)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* if the name is one that the user has already approved, it's OK. */ + for (domainOK = cert->domainOK; domainOK; domainOK = domainOK->next) { + if (0 == PORT_Strcasecmp(hn, domainOK->name)) { + return SECSuccess; + } + } + + /* Per RFC 2818, if the SubjectAltName extension is present, it must + ** be used as the cert's identity. + */ + rv = cert_VerifySubjectAltName(cert, hn); + if (rv == SECSuccess || PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) + return rv; + + /* try the cert extension first, then the common name */ + cn = CERT_FindNSStringExtension(cert, SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME); + if ( !cn ) { + cn = CERT_GetCommonName(&cert->subject); + } + if ( cn ) { + rv = cert_TestHostName(cn, hn); + PORT_Free(cn); + } else + PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); + return rv; +} + +PRBool +CERT_CompareCerts(CERTCertificate *c1, CERTCertificate *c2) +{ + SECComparison comp; + + comp = SECITEM_CompareItem(&c1->derCert, &c2->derCert); + if ( comp == SECEqual ) { /* certs are the same */ + return(PR_TRUE); + } else { + return(PR_FALSE); + } +} + +static SECStatus +StringsEqual(char *s1, char *s2) { + if ( ( s1 == NULL ) || ( s2 == NULL ) ) { + if ( s1 != s2 ) { /* only one is null */ + return(SECFailure); + } + return(SECSuccess); /* both are null */ + } + + if ( PORT_Strcmp( s1, s2 ) != 0 ) { + return(SECFailure); /* not equal */ + } + + return(SECSuccess); /* strings are equal */ +} + + +PRBool +CERT_CompareCertsForRedirection(CERTCertificate *c1, CERTCertificate *c2) +{ + SECComparison comp; + char *c1str, *c2str; + SECStatus eq; + + comp = SECITEM_CompareItem(&c1->derCert, &c2->derCert); + if ( comp == SECEqual ) { /* certs are the same */ + return(PR_TRUE); + } + + /* check if they are issued by the same CA */ + comp = SECITEM_CompareItem(&c1->derIssuer, &c2->derIssuer); + if ( comp != SECEqual ) { /* different issuer */ + return(PR_FALSE); + } + + /* check country name */ + c1str = CERT_GetCountryName(&c1->subject); + c2str = CERT_GetCountryName(&c2->subject); + eq = StringsEqual(c1str, c2str); + PORT_Free(c1str); + PORT_Free(c2str); + if ( eq != SECSuccess ) { + return(PR_FALSE); + } + + /* check locality name */ + c1str = CERT_GetLocalityName(&c1->subject); + c2str = CERT_GetLocalityName(&c2->subject); + eq = StringsEqual(c1str, c2str); + PORT_Free(c1str); + PORT_Free(c2str); + if ( eq != SECSuccess ) { + return(PR_FALSE); + } + + /* check state name */ + c1str = CERT_GetStateName(&c1->subject); + c2str = CERT_GetStateName(&c2->subject); + eq = StringsEqual(c1str, c2str); + PORT_Free(c1str); + PORT_Free(c2str); + if ( eq != SECSuccess ) { + return(PR_FALSE); + } + + /* check org name */ + c1str = CERT_GetOrgName(&c1->subject); + c2str = CERT_GetOrgName(&c2->subject); + eq = StringsEqual(c1str, c2str); + PORT_Free(c1str); + PORT_Free(c2str); + if ( eq != SECSuccess ) { + return(PR_FALSE); + } + +#ifdef NOTDEF + /* check orgUnit name */ + /* + * We need to revisit this and decide which fields should be allowed to be + * different + */ + c1str = CERT_GetOrgUnitName(&c1->subject); + c2str = CERT_GetOrgUnitName(&c2->subject); + eq = StringsEqual(c1str, c2str); + PORT_Free(c1str); + PORT_Free(c2str); + if ( eq != SECSuccess ) { + return(PR_FALSE); + } +#endif + + return(PR_TRUE); /* all fields but common name are the same */ +} + + +/* CERT_CertChainFromCert and CERT_DestroyCertificateList moved + to certhigh.c */ + + +CERTIssuerAndSN * +CERT_GetCertIssuerAndSN(PRArenaPool *arena, CERTCertificate *cert) +{ + CERTIssuerAndSN *result; + SECStatus rv; + + if ( arena == NULL ) { + arena = cert->arena; + } + + result = (CERTIssuerAndSN*)PORT_ArenaZAlloc(arena, sizeof(*result)); + if (result == NULL) { + PORT_SetError (SEC_ERROR_NO_MEMORY); + return NULL; + } + + rv = SECITEM_CopyItem(arena, &result->derIssuer, &cert->derIssuer); + if (rv != SECSuccess) + return NULL; + + rv = CERT_CopyName(arena, &result->issuer, &cert->issuer); + if (rv != SECSuccess) + return NULL; + + rv = SECITEM_CopyItem(arena, &result->serialNumber, &cert->serialNumber); + if (rv != SECSuccess) + return NULL; + + return result; +} + +char * +CERT_MakeCANickname(CERTCertificate *cert) +{ + char *firstname = NULL; + char *org = NULL; + char *nickname = NULL; + int count; + CERTCertificate *dummycert; + CERTCertDBHandle *handle; + + handle = cert->dbhandle; + + nickname = CERT_GetNickName(cert, handle, cert->arena); + if (nickname == NULL) { + firstname = CERT_GetCommonName(&cert->subject); + if ( firstname == NULL ) { + firstname = CERT_GetOrgUnitName(&cert->subject); + } + + org = CERT_GetOrgName(&cert->issuer); + if (org == NULL) { + org = CERT_GetDomainComponentName(&cert->issuer); + if (org == NULL) { + if (firstname) { + org = firstname; + firstname = NULL; + } else { + org = PORT_Strdup("Unknown CA"); + } + } + } + + /* can only fail if PORT_Strdup fails, in which case + * we're having memory problems. */ + if (org == NULL) { + goto loser; + } + + + count = 1; + while ( 1 ) { + + if ( firstname ) { + if ( count == 1 ) { + nickname = PR_smprintf("%s - %s", firstname, org); + } else { + nickname = PR_smprintf("%s - %s #%d", firstname, org, count); + } + } else { + if ( count == 1 ) { + nickname = PR_smprintf("%s", org); + } else { + nickname = PR_smprintf("%s #%d", org, count); + } + } + if ( nickname == NULL ) { + goto loser; + } + + /* look up the nickname to make sure it isn't in use already */ + dummycert = CERT_FindCertByNickname(handle, nickname); + + if ( dummycert == NULL ) { + goto done; + } + + /* found a cert, destroy it and loop */ + CERT_DestroyCertificate(dummycert); + + /* free the nickname */ + PORT_Free(nickname); + + count++; + } + } +loser: + if ( nickname ) { + PORT_Free(nickname); + } + + nickname = ""; + +done: + if ( firstname ) { + PORT_Free(firstname); + } + if ( org ) { + PORT_Free(org); + } + + return(nickname); +} + +/* CERT_Import_CAChain moved to certhigh.c */ + +void +CERT_DestroyCrl (CERTSignedCrl *crl) +{ + SEC_DestroyCrl (crl); +} + + + +/* + * Does a cert belong to a CA? We decide based on perm database trust + * flags, Netscape Cert Type Extension, and KeyUsage Extension. + */ +PRBool +CERT_IsCACert(CERTCertificate *cert, unsigned int *rettype) +{ + CERTCertTrust *trust; + SECStatus rv; + unsigned int type; + PRBool ret; + + ret = PR_FALSE; + type = 0; + + if ( cert->trust && (cert->trust->sslFlags|cert->trust->emailFlags| + cert->trust->objectSigningFlags)) { + trust = cert->trust; + if ( ( ( trust->sslFlags & CERTDB_VALID_CA ) == CERTDB_VALID_CA ) || + ( ( trust->sslFlags & CERTDB_TRUSTED_CA ) == CERTDB_TRUSTED_CA ) ) { + ret = PR_TRUE; + type |= NS_CERT_TYPE_SSL_CA; + } + + if ( ( ( trust->emailFlags & CERTDB_VALID_CA ) == CERTDB_VALID_CA ) || + ( ( trust->emailFlags & CERTDB_TRUSTED_CA ) == CERTDB_TRUSTED_CA ) ) { + ret = PR_TRUE; + type |= NS_CERT_TYPE_EMAIL_CA; + } + + if ( ( ( trust->objectSigningFlags & CERTDB_VALID_CA ) + == CERTDB_VALID_CA ) || + ( ( trust->objectSigningFlags & CERTDB_TRUSTED_CA ) + == CERTDB_TRUSTED_CA ) ) { + ret = PR_TRUE; + type |= NS_CERT_TYPE_OBJECT_SIGNING_CA; + } + } else { + if ( cert->nsCertType & + ( NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA | + NS_CERT_TYPE_OBJECT_SIGNING_CA ) ) { + ret = PR_TRUE; + type = (cert->nsCertType & NS_CERT_TYPE_CA); + } else { + CERTBasicConstraints constraints; + rv = CERT_FindBasicConstraintExten(cert, &constraints); + if ( rv == SECSuccess ) { + if ( constraints.isCA ) { + ret = PR_TRUE; + type = (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA); + } + } + } + + /* finally check if it's a FORTEZZA V1 CA */ + if (ret == PR_FALSE) { + if (fortezzaIsCA(cert)) { + ret = PR_TRUE; + type = (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA); + } + } + } + + /* the isRoot flag trumps all */ + if (cert->isRoot) { + ret = PR_TRUE; + /* set only these by default, same as above */ + type = (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA); + } + + if ( rettype != NULL ) { + *rettype = type; + } + + return(ret); +} + +PRBool +CERT_IsCADERCert(SECItem *derCert, unsigned int *type) { + CERTCertificate *cert; + PRBool isCA; + + /* This is okay -- only looks at extensions */ + cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); + if (cert == NULL) return PR_FALSE; + + isCA = CERT_IsCACert(cert,type); + CERT_DestroyCertificate (cert); + return isCA; +} + +PRBool +CERT_IsRootDERCert(SECItem *derCert) +{ + CERTCertificate *cert; + PRBool isRoot; + + /* This is okay -- only looks at extensions */ + cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); + if (cert == NULL) return PR_FALSE; + + isRoot = cert->isRoot; + CERT_DestroyCertificate (cert); + return isRoot; +} + + +/* + * is certa newer than certb? If one is expired, pick the other one. + */ +PRBool +CERT_IsNewer(CERTCertificate *certa, CERTCertificate *certb) +{ + PRTime notBeforeA, notAfterA, notBeforeB, notAfterB, now; + SECStatus rv; + PRBool newerbefore, newerafter; + + rv = CERT_GetCertTimes(certa, ¬BeforeA, ¬AfterA); + if ( rv != SECSuccess ) { + return(PR_FALSE); + } + + rv = CERT_GetCertTimes(certb, ¬BeforeB, ¬AfterB); + if ( rv != SECSuccess ) { + return(PR_TRUE); + } + + newerbefore = PR_FALSE; + if ( LL_CMP(notBeforeA, >, notBeforeB) ) { + newerbefore = PR_TRUE; + } + + newerafter = PR_FALSE; + if ( LL_CMP(notAfterA, >, notAfterB) ) { + newerafter = PR_TRUE; + } + + if ( newerbefore && newerafter ) { + return(PR_TRUE); + } + + if ( ( !newerbefore ) && ( !newerafter ) ) { + return(PR_FALSE); + } + + /* get current UTC time */ + now = PR_Now(); + + if ( newerbefore ) { + /* cert A was issued after cert B, but expires sooner */ + /* if A is expired, then pick B */ + if ( LL_CMP(notAfterA, <, now ) ) { + return(PR_FALSE); + } + return(PR_TRUE); + } else { + /* cert B was issued after cert A, but expires sooner */ + /* if B is expired, then pick A */ + if ( LL_CMP(notAfterB, <, now ) ) { + return(PR_TRUE); + } + return(PR_FALSE); + } +} + +void +CERT_DestroyCertArray(CERTCertificate **certs, unsigned int ncerts) +{ + unsigned int i; + + if ( certs ) { + for ( i = 0; i < ncerts; i++ ) { + if ( certs[i] ) { + CERT_DestroyCertificate(certs[i]); + } + } + + PORT_Free(certs); + } + + return; +} + +char * +CERT_FixupEmailAddr(char *emailAddr) +{ + char *retaddr; + char *str; + + if ( emailAddr == NULL ) { + return(NULL); + } + + /* copy the string */ + str = retaddr = PORT_Strdup(emailAddr); + if ( str == NULL ) { + return(NULL); + } + + /* make it lower case */ + while ( *str ) { + *str = tolower( *str ); + str++; + } + + return(retaddr); +} + +/* + * NOTE - don't allow encode of govt-approved or invisible bits + */ +SECStatus +CERT_DecodeTrustString(CERTCertTrust *trust, char *trusts) +{ + unsigned int i; + unsigned int *pflags; + + trust->sslFlags = 0; + trust->emailFlags = 0; + trust->objectSigningFlags = 0; + + pflags = &trust->sslFlags; + + for (i=0; i < PORT_Strlen(trusts); i++) { + switch (trusts[i]) { + case 'p': + *pflags = *pflags | CERTDB_VALID_PEER; + break; + + case 'P': + *pflags = *pflags | CERTDB_TRUSTED | CERTDB_VALID_PEER; + break; + + case 'w': + *pflags = *pflags | CERTDB_SEND_WARN; + break; + + case 'c': + *pflags = *pflags | CERTDB_VALID_CA; + break; + + case 'T': + *pflags = *pflags | CERTDB_TRUSTED_CLIENT_CA | CERTDB_VALID_CA; + break; + + case 'C' : + *pflags = *pflags | CERTDB_TRUSTED_CA | CERTDB_VALID_CA; + break; + + case 'u': + *pflags = *pflags | CERTDB_USER; + break; + +#ifdef DEBUG_NSSTEAM_ONLY + case 'i': + *pflags = *pflags | CERTDB_INVISIBLE_CA; + break; + case 'g': + *pflags = *pflags | CERTDB_GOVT_APPROVED_CA; + break; +#endif /* DEBUG_NSSTEAM_ONLY */ + + case ',': + if ( pflags == &trust->sslFlags ) { + pflags = &trust->emailFlags; + } else { + pflags = &trust->objectSigningFlags; + } + break; + default: + return SECFailure; + } + } + + return SECSuccess; +} + +static void +EncodeFlags(char *trusts, unsigned int flags) +{ + if (flags & CERTDB_VALID_CA) + if (!(flags & CERTDB_TRUSTED_CA) && + !(flags & CERTDB_TRUSTED_CLIENT_CA)) + PORT_Strcat(trusts, "c"); + if (flags & CERTDB_VALID_PEER) + if (!(flags & CERTDB_TRUSTED)) + PORT_Strcat(trusts, "p"); + if (flags & CERTDB_TRUSTED_CA) + PORT_Strcat(trusts, "C"); + if (flags & CERTDB_TRUSTED_CLIENT_CA) + PORT_Strcat(trusts, "T"); + if (flags & CERTDB_TRUSTED) + PORT_Strcat(trusts, "P"); + if (flags & CERTDB_USER) + PORT_Strcat(trusts, "u"); + if (flags & CERTDB_SEND_WARN) + PORT_Strcat(trusts, "w"); + if (flags & CERTDB_INVISIBLE_CA) + PORT_Strcat(trusts, "I"); + if (flags & CERTDB_GOVT_APPROVED_CA) + PORT_Strcat(trusts, "G"); + return; +} + +char * +CERT_EncodeTrustString(CERTCertTrust *trust) +{ + char tmpTrustSSL[32]; + char tmpTrustEmail[32]; + char tmpTrustSigning[32]; + char *retstr = NULL; + + if ( trust ) { + tmpTrustSSL[0] = '\0'; + tmpTrustEmail[0] = '\0'; + tmpTrustSigning[0] = '\0'; + + EncodeFlags(tmpTrustSSL, trust->sslFlags); + EncodeFlags(tmpTrustEmail, trust->emailFlags); + EncodeFlags(tmpTrustSigning, trust->objectSigningFlags); + + retstr = PR_smprintf("%s,%s,%s", tmpTrustSSL, tmpTrustEmail, + tmpTrustSigning); + } + + return(retstr); +} + +/* in 3.4, this will only set trust */ +SECStatus +CERT_SaveImportedCert(CERTCertificate *cert, SECCertUsage usage, + PRBool caOnly, char *nickname) +{ + SECStatus rv; + PRBool saveit; + CERTCertTrust trust; + PRBool isCA; + unsigned int certtype; + + isCA = CERT_IsCACert(cert, NULL); + if ( caOnly && ( !isCA ) ) { + return(SECSuccess); + } + /* In NSS 3.4, certs are given zero trust upon import. However, this + * function needs to set up default CA trust (CERTDB_VALID_CA), or + * PKCS#12 imported certs will not show up correctly. In the case of a + * CA cert with zero trust, continue with this function. But if the cert + * does already have some trust bits, exit and do not change them. + */ + if (isCA && cert->trust && + (cert->trust->sslFlags | + cert->trust->emailFlags | + cert->trust->objectSigningFlags)) { + return(SECSuccess); + } + + saveit = PR_TRUE; + + PORT_Memset((void *)&trust, 0, sizeof(trust)); + + certtype = cert->nsCertType; + + /* if no CA bits in cert type, then set all CA bits */ + if ( isCA && ( ! ( certtype & NS_CERT_TYPE_CA ) ) ) { + certtype |= NS_CERT_TYPE_CA; + } + + /* if no app bits in cert type, then set all app bits */ + if ( ( !isCA ) && ( ! ( certtype & NS_CERT_TYPE_APP ) ) ) { + certtype |= NS_CERT_TYPE_APP; + } + + switch ( usage ) { + case certUsageEmailSigner: + case certUsageEmailRecipient: + if ( isCA ) { + if ( certtype & NS_CERT_TYPE_EMAIL_CA ) { + trust.emailFlags = CERTDB_VALID_CA; + } + } else { + if ( cert->emailAddr == NULL ) { + saveit = PR_FALSE; + } + + if ( certtype & NS_CERT_TYPE_EMAIL ) { + trust.emailFlags = CERTDB_VALID_PEER; + if ( ! ( cert->rawKeyUsage & KU_KEY_ENCIPHERMENT ) ) { + /* don't save it if KeyEncipherment is not allowed */ + saveit = PR_FALSE; + } + } + } + break; + case certUsageUserCertImport: + if ( isCA ) { + if ( certtype & NS_CERT_TYPE_SSL_CA ) { + trust.sslFlags = CERTDB_VALID_CA; + } + + if ( certtype & NS_CERT_TYPE_EMAIL_CA ) { + trust.emailFlags = CERTDB_VALID_CA; + } + + if ( certtype & NS_CERT_TYPE_OBJECT_SIGNING_CA ) { + trust.objectSigningFlags = CERTDB_VALID_CA; + } + + } else { + if ( certtype & NS_CERT_TYPE_SSL_CLIENT ) { + trust.sslFlags = CERTDB_VALID_PEER; + } + + if ( certtype & NS_CERT_TYPE_EMAIL ) { + trust.emailFlags = CERTDB_VALID_PEER; + } + + if ( certtype & NS_CERT_TYPE_OBJECT_SIGNING ) { + trust.objectSigningFlags = CERTDB_VALID_PEER; + } + } + break; + case certUsageAnyCA: + trust.sslFlags = CERTDB_VALID_CA; + break; + case certUsageSSLCA: + trust.sslFlags = CERTDB_VALID_CA | + CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA; + break; + default: /* XXX added to quiet warnings; no other cases needed? */ + break; + } + + if ( saveit ) { + rv = CERT_ChangeCertTrust(cert->dbhandle, cert, &trust); + if ( rv != SECSuccess ) { + goto loser; + } + } + + rv = SECSuccess; + goto done; + +loser: + rv = SECFailure; +done: + + return(rv); +} + +SECStatus +CERT_ImportCerts(CERTCertDBHandle *certdb, SECCertUsage usage, + unsigned int ncerts, SECItem **derCerts, + CERTCertificate ***retCerts, PRBool keepCerts, + PRBool caOnly, char *nickname) +{ + unsigned int i; + CERTCertificate **certs = NULL; + SECStatus rv; + unsigned int fcerts = 0; + + if ( ncerts ) { + certs = (CERTCertificate**)PORT_ZAlloc(sizeof(CERTCertificate *) * ncerts ); + if ( certs == NULL ) { + return(SECFailure); + } + + /* decode all of the certs into the temporary DB */ + for ( i = 0, fcerts= 0; i < ncerts; i++) { + certs[fcerts] = CERT_NewTempCertificate(certdb, + derCerts[i], + NULL, + PR_FALSE, + PR_TRUE); + if (certs[fcerts]) fcerts++; + } + + if ( keepCerts ) { + for ( i = 0; i < fcerts; i++ ) { + char* canickname = NULL; + PRBool freeNickname = PR_FALSE; + + SECKEY_UpdateCertPQG(certs[i]); + + if ( CERT_IsCACert(certs[i], NULL) ) { + canickname = CERT_MakeCANickname(certs[i]); + if ( canickname != NULL ) { + freeNickname = PR_TRUE; + } + } + + if(CERT_IsCACert(certs[i], NULL) && (fcerts > 1)) { + /* if we are importing only a single cert and specifying + * a nickname, we want to use that nickname if it a CA, + * otherwise if there are more than one cert, we don't + * know which cert it belongs to. But we still may try + * the individual canickname from the cert itself. + */ + rv = CERT_AddTempCertToPerm(certs[i], canickname, NULL); + } else { + rv = CERT_AddTempCertToPerm(certs[i], + nickname?nickname:canickname, NULL); + } + if (rv == SECSuccess) { + CERT_SaveImportedCert(certs[i], usage, caOnly, NULL); + } + + if (PR_TRUE == freeNickname) { + PORT_Free(canickname); + } + /* don't care if it fails - keep going */ + } + } + } + + if ( retCerts ) { + *retCerts = certs; + } else { + if (certs) { + CERT_DestroyCertArray(certs, fcerts); + } + } + + return(SECSuccess); + +#if 0 /* dead code here - why ?? XXX */ +loser: + if ( retCerts ) { + *retCerts = NULL; + } + if ( certs ) { + CERT_DestroyCertArray(certs, ncerts); + } + return(SECFailure); +#endif +} + +/* + * a real list of certificates - need to convert CERTCertificateList + * stuff and ASN 1 encoder/decoder over to using this... + */ +CERTCertList * +CERT_NewCertList(void) +{ + PRArenaPool *arena = NULL; + CERTCertList *ret = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( arena == NULL ) { + goto loser; + } + + ret = (CERTCertList *)PORT_ArenaZAlloc(arena, sizeof(CERTCertList)); + if ( ret == NULL ) { + goto loser; + } + + ret->arena = arena; + + PR_INIT_CLIST(&ret->list); + + return(ret); + +loser: + if ( arena != NULL ) { + PORT_FreeArena(arena, PR_FALSE); + } + + return(NULL); +} + +void +CERT_DestroyCertList(CERTCertList *certs) +{ + PRCList *node; + + while( !PR_CLIST_IS_EMPTY(&certs->list) ) { + node = PR_LIST_HEAD(&certs->list); + CERT_DestroyCertificate(((CERTCertListNode *)node)->cert); + PR_REMOVE_LINK(node); + } + + PORT_FreeArena(certs->arena, PR_FALSE); + + return; +} + +void +CERT_RemoveCertListNode(CERTCertListNode *node) +{ + CERT_DestroyCertificate(node->cert); + PR_REMOVE_LINK(&node->links); + return; +} + + +SECStatus +CERT_AddCertToListTailWithData(CERTCertList *certs, + CERTCertificate *cert, void *appData) +{ + CERTCertListNode *node; + + node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena, + sizeof(CERTCertListNode)); + if ( node == NULL ) { + goto loser; + } + + PR_INSERT_BEFORE(&node->links, &certs->list); + /* certs->count++; */ + node->cert = cert; + node->appData = appData; + return(SECSuccess); + +loser: + return(SECFailure); +} + +SECStatus +CERT_AddCertToListTail(CERTCertList *certs, CERTCertificate *cert) +{ + return CERT_AddCertToListTailWithData(certs, cert, NULL); +} + +SECStatus +CERT_AddCertToListHeadWithData(CERTCertList *certs, + CERTCertificate *cert, void *appData) +{ + CERTCertListNode *node; + CERTCertListNode *head; + + head = CERT_LIST_HEAD(certs); + + if (head == NULL) return CERT_AddCertToListTail(certs,cert); + + node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena, + sizeof(CERTCertListNode)); + if ( node == NULL ) { + goto loser; + } + + PR_INSERT_BEFORE(&node->links, &head->links); + /* certs->count++; */ + node->cert = cert; + node->appData = appData; + return(SECSuccess); + +loser: + return(SECFailure); +} + +SECStatus +CERT_AddCertToListHead(CERTCertList *certs, CERTCertificate *cert) +{ + return CERT_AddCertToListHeadWithData(certs, cert, NULL); +} + +/* + * Sort callback function to determine if cert a is newer than cert b. + * Not valid certs are considered older than valid certs. + */ +PRBool +CERT_SortCBValidity(CERTCertificate *certa, + CERTCertificate *certb, + void *arg) +{ + PRTime sorttime; + PRTime notBeforeA, notAfterA, notBeforeB, notAfterB; + SECStatus rv; + PRBool newerbefore, newerafter; + PRBool aNotValid = PR_FALSE, bNotValid = PR_FALSE; + + sorttime = *(PRTime *)arg; + + rv = CERT_GetCertTimes(certa, ¬BeforeA, ¬AfterA); + if ( rv != SECSuccess ) { + return(PR_FALSE); + } + + rv = CERT_GetCertTimes(certb, ¬BeforeB, ¬AfterB); + if ( rv != SECSuccess ) { + return(PR_TRUE); + } + newerbefore = PR_FALSE; + if ( LL_CMP(notBeforeA, >, notBeforeB) ) { + newerbefore = PR_TRUE; + } + newerafter = PR_FALSE; + if ( LL_CMP(notAfterA, >, notAfterB) ) { + newerafter = PR_TRUE; + } + + /* check if A is valid at sorttime */ + if ( CERT_CheckCertValidTimes(certa, sorttime, PR_FALSE) + != secCertTimeValid ) { + aNotValid = PR_TRUE; + } + + /* check if B is valid at sorttime */ + if ( CERT_CheckCertValidTimes(certb, sorttime, PR_FALSE) + != secCertTimeValid ) { + bNotValid = PR_TRUE; + } + + /* a is valid, b is not */ + if ( bNotValid && ( ! aNotValid ) ) { + return(PR_TRUE); + } + + /* b is valid, a is not */ + if ( aNotValid && ( ! bNotValid ) ) { + return(PR_FALSE); + } + + /* a and b are either valid or not valid */ + if ( newerbefore && newerafter ) { + return(PR_TRUE); + } + + if ( ( !newerbefore ) && ( !newerafter ) ) { + return(PR_FALSE); + } + + if ( newerbefore ) { + /* cert A was issued after cert B, but expires sooner */ + return(PR_TRUE); + } else { + /* cert B was issued after cert A, but expires sooner */ + return(PR_FALSE); + } +} + + +SECStatus +CERT_AddCertToListSorted(CERTCertList *certs, + CERTCertificate *cert, + CERTSortCallback f, + void *arg) +{ + CERTCertListNode *node; + CERTCertListNode *head; + PRBool ret; + + node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena, + sizeof(CERTCertListNode)); + if ( node == NULL ) { + goto loser; + } + + head = CERT_LIST_HEAD(certs); + + while ( !CERT_LIST_END(head, certs) ) { + + /* if cert is already in the list, then don't add it again */ + if ( cert == head->cert ) { + /*XXX*/ + /* don't keep a reference */ + CERT_DestroyCertificate(cert); + goto done; + } + + ret = (* f)(cert, head->cert, arg); + /* if sort function succeeds, then insert before current node */ + if ( ret ) { + PR_INSERT_BEFORE(&node->links, &head->links); + goto done; + } + + head = CERT_LIST_NEXT(head); + } + /* if we get to the end, then just insert it at the tail */ + PR_INSERT_BEFORE(&node->links, &certs->list); + +done: + /* certs->count++; */ + node->cert = cert; + return(SECSuccess); + +loser: + return(SECFailure); +} + +/* This routine is here because pcertdb.c still has a call to it. + * The SMIME profile code in pcertdb.c should be split into high (find + * the email cert) and low (store the profile) code. At that point, we + * can move this to certhigh.c where it belongs. + * + * remove certs from a list that don't have keyUsage and certType + * that match the given usage. + */ +SECStatus +CERT_FilterCertListByUsage(CERTCertList *certList, SECCertUsage usage, + PRBool ca) +{ + unsigned int requiredKeyUsage; + unsigned int requiredCertType; + CERTCertListNode *node, *savenode; + PRBool bad; + SECStatus rv; + unsigned int certType; + PRBool dummyret; + + if (certList == NULL) goto loser; + + rv = CERT_KeyUsageAndTypeForCertUsage(usage, ca, &requiredKeyUsage, + &requiredCertType); + if ( rv != SECSuccess ) { + goto loser; + } + + node = CERT_LIST_HEAD(certList); + + while ( !CERT_LIST_END(node, certList) ) { + + bad = PR_FALSE; + + /* bad key usage */ + if ( CERT_CheckKeyUsage(node->cert, requiredKeyUsage ) + != SECSuccess ) { + bad = PR_TRUE; + } + /* bad cert type */ + if ( ca ) { + /* This function returns a more comprehensive cert type that + * takes trust flags into consideration. Should probably + * fix the cert decoding code to do this. + */ + dummyret = CERT_IsCACert(node->cert, &certType); + } else { + certType = node->cert->nsCertType; + } + + if ( ! ( certType & requiredCertType ) ) { + bad = PR_TRUE; + } + + if ( bad ) { + /* remove the node if it is bad */ + savenode = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(node); + node = savenode; + } else { + node = CERT_LIST_NEXT(node); + } + } + return(SECSuccess); + +loser: + return(SECFailure); +} + +PRBool CERT_IsUserCert(CERTCertificate* cert) +{ + if ( (cert->trust->sslFlags & CERTDB_USER ) || + (cert->trust->emailFlags & CERTDB_USER ) || + (cert->trust->objectSigningFlags & CERTDB_USER ) ) { + return PR_TRUE; + } else { + return PR_FALSE; + } +} + +SECStatus +CERT_FilterCertListForUserCerts(CERTCertList *certList) +{ + CERTCertListNode *node, *freenode; + CERTCertificate *cert; + + if (!certList) { + return SECFailure; + } + + node = CERT_LIST_HEAD(certList); + + while ( ! CERT_LIST_END(node, certList) ) { + cert = node->cert; + if ( PR_TRUE != CERT_IsUserCert(cert) ) { + /* Not a User Cert, so remove this cert from the list */ + freenode = node; + node = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(freenode); + } else { + /* Is a User cert, so leave it in the list */ + node = CERT_LIST_NEXT(node); + } + } + + return(SECSuccess); +} + +static PZLock *certRefCountLock = NULL; + +/* + * Acquire the cert reference count lock + * There is currently one global lock for all certs, but I'm putting a cert + * arg here so that it will be easy to make it per-cert in the future if + * that turns out to be necessary. + */ +void +CERT_LockCertRefCount(CERTCertificate *cert) +{ + if ( certRefCountLock == NULL ) { + nss_InitLock(&certRefCountLock, nssILockRefLock); + PORT_Assert(certRefCountLock != NULL); + } + + PZ_Lock(certRefCountLock); + return; +} + +/* + * Free the cert reference count lock + */ +void +CERT_UnlockCertRefCount(CERTCertificate *cert) +{ + PRStatus prstat; + + PORT_Assert(certRefCountLock != NULL); + + prstat = PZ_Unlock(certRefCountLock); + + PORT_Assert(prstat == PR_SUCCESS); + + return; +} + +static PZLock *certTrustLock = NULL; + +/* + * Acquire the cert trust lock + * There is currently one global lock for all certs, but I'm putting a cert + * arg here so that it will be easy to make it per-cert in the future if + * that turns out to be necessary. + */ +void +CERT_LockCertTrust(CERTCertificate *cert) +{ + if ( certTrustLock == NULL ) { + nss_InitLock(&certTrustLock, nssILockCertDB); + PORT_Assert(certTrustLock != NULL); + } + + PZ_Lock(certTrustLock); + return; +} + +/* + * Free the cert trust lock + */ +void +CERT_UnlockCertTrust(CERTCertificate *cert) +{ + PRStatus prstat; + + PORT_Assert(certTrustLock != NULL); + + prstat = PZ_Unlock(certTrustLock); + + PORT_Assert(prstat == PR_SUCCESS); + + return; +} + + +/* + * Get the StatusConfig data for this handle + */ +CERTStatusConfig * +CERT_GetStatusConfig(CERTCertDBHandle *handle) +{ + return handle->statusConfig; +} + +/* + * Set the StatusConfig data for this handle. There + * should not be another configuration set. + */ +void +CERT_SetStatusConfig(CERTCertDBHandle *handle, CERTStatusConfig *statusConfig) +{ + PORT_Assert(handle->statusConfig == NULL); + handle->statusConfig = statusConfig; +} + +/* + * Code for dealing with subjKeyID to cert mappings. + */ + +static PLHashTable *gSubjKeyIDHash = NULL; +static PRLock *gSubjKeyIDLock = NULL; + +static void *cert_AllocTable(void *pool, PRSize size) +{ + return PORT_Alloc(size); +} + +static void cert_FreeTable(void *pool, void *item) +{ + PORT_Free(item); +} + +static PLHashEntry* cert_AllocEntry(void *pool, const void *key) +{ + return PORT_New(PLHashEntry); +} + +static void cert_FreeEntry(void *pool, PLHashEntry *he, PRUintn flag) +{ + SECITEM_FreeItem((SECItem*)(he->value), PR_TRUE); + if (flag == HT_FREE_ENTRY) { + SECITEM_FreeItem((SECItem*)(he->key), PR_TRUE); + PORT_Free(he); + } +} + +static PLHashAllocOps cert_AllocOps = { + cert_AllocTable, cert_FreeTable, cert_AllocEntry, cert_FreeEntry +}; + +SECStatus +cert_CreateSubjectKeyIDHashTable(void) +{ + gSubjKeyIDHash = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare, + SECITEM_HashCompare, + &cert_AllocOps, NULL); + if (!gSubjKeyIDHash) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + gSubjKeyIDLock = PR_NewLock(); + if (!gSubjKeyIDLock) { + PL_HashTableDestroy(gSubjKeyIDHash); + gSubjKeyIDHash = NULL; + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + return SECSuccess; + +} + +SECStatus +cert_AddSubjectKeyIDMapping(SECItem *subjKeyID, CERTCertificate *cert) +{ + SECItem *newKeyID, *oldVal, *newVal; + SECStatus rv = SECFailure; + + if (!gSubjKeyIDLock) { + /* If one is created, then both are there. So only check for one. */ + return SECFailure; + } + + newVal = SECITEM_DupItem(&cert->derCert); + if (!newVal) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto done; + } + newKeyID = SECITEM_DupItem(subjKeyID); + if (!newKeyID) { + SECITEM_FreeItem(newVal, PR_TRUE); + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto done; + } + + PR_Lock(gSubjKeyIDLock); + /* The hash table implementation does not free up the memory + * associated with the key of an already existing entry if we add a + * duplicate, so we would wind up leaking the previously allocated + * key if we don't remove before adding. + */ + oldVal = (SECItem*)PL_HashTableLookup(gSubjKeyIDHash, subjKeyID); + if (oldVal) { + PL_HashTableRemove(gSubjKeyIDHash, subjKeyID); + } + + rv = (PL_HashTableAdd(gSubjKeyIDHash, newKeyID, newVal)) ? SECSuccess : + SECFailure; + PR_Unlock(gSubjKeyIDLock); +done: + return rv; +} + +SECStatus +cert_RemoveSubjectKeyIDMapping(SECItem *subjKeyID) +{ + SECStatus rv; + if (!gSubjKeyIDLock) + return SECFailure; + + PR_Lock(gSubjKeyIDLock); + rv = (PL_HashTableRemove(gSubjKeyIDHash, subjKeyID)) ? SECSuccess : + SECFailure; + PR_Unlock(gSubjKeyIDLock); + return rv; +} + +SECStatus +cert_DestroySubjectKeyIDHashTable(void) +{ + if (gSubjKeyIDHash) { + PR_Lock(gSubjKeyIDLock); + PL_HashTableDestroy(gSubjKeyIDHash); + gSubjKeyIDHash = NULL; + PR_Unlock(gSubjKeyIDLock); + PR_DestroyLock(gSubjKeyIDLock); + gSubjKeyIDLock = NULL; + } + return SECSuccess; +} + +SECItem* +cert_FindDERCertBySubjectKeyID(SECItem *subjKeyID) +{ + SECItem *val; + + if (!gSubjKeyIDLock) + return NULL; + + PR_Lock(gSubjKeyIDLock); + val = (SECItem*)PL_HashTableLookup(gSubjKeyIDHash, subjKeyID); + if (val) { + val = SECITEM_DupItem(val); + } + PR_Unlock(gSubjKeyIDLock); + return val; +} + +CERTCertificate* +CERT_FindCertBySubjectKeyID(CERTCertDBHandle *handle, SECItem *subjKeyID) +{ + CERTCertificate *cert = NULL; + SECItem *derCert; + + derCert = cert_FindDERCertBySubjectKeyID(subjKeyID); + if (derCert) { + cert = CERT_FindCertByDERCert(handle, derCert); + SECITEM_FreeItem(derCert, PR_TRUE); + } + return cert; +} diff --git a/security/nss/lib/certdb/certdb.h b/security/nss/lib/certdb/certdb.h new file mode 100644 index 000000000..7340961c2 --- /dev/null +++ b/security/nss/lib/certdb/certdb.h @@ -0,0 +1,159 @@ +/* + * 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. + */ + +#ifndef _CERTDB_H_ +#define _CERTDB_H_ + + +/* common flags for all types of certificates */ +#define CERTDB_VALID_PEER (1<<0) +#define CERTDB_TRUSTED (1<<1) +#define CERTDB_SEND_WARN (1<<2) +#define CERTDB_VALID_CA (1<<3) +#define CERTDB_TRUSTED_CA (1<<4) /* trusted for issuing server certs */ +#define CERTDB_NS_TRUSTED_CA (1<<5) +#define CERTDB_USER (1<<6) +#define CERTDB_TRUSTED_CLIENT_CA (1<<7) /* trusted for issuing client certs */ +#define CERTDB_INVISIBLE_CA (1<<8) /* don't show in UI */ +#define CERTDB_GOVT_APPROVED_CA (1<<9) /* can do strong crypto in export ver */ + + +SEC_BEGIN_PROTOS + +CERTSignedCrl * +SEC_FindCrlByKey(CERTCertDBHandle *handle, SECItem *crlKey, int type); + +CERTSignedCrl * +SEC_FindCrlByName(CERTCertDBHandle *handle, SECItem *crlKey, int type); + +CERTSignedCrl * +SEC_FindCrlByDERCert(CERTCertDBHandle *handle, SECItem *derCrl, int type); + +PRBool +SEC_CertNicknameConflict(char *nickname, SECItem *derSubject, + CERTCertDBHandle *handle); +CERTSignedCrl * +SEC_NewCrl(CERTCertDBHandle *handle, char *url, SECItem *derCrl, int type); + +SECStatus +SEC_DeletePermCRL(CERTSignedCrl *crl); + + +SECStatus +SEC_LookupCrls(CERTCertDBHandle *handle, CERTCrlHeadNode **nodes, int type); + +SECStatus +SEC_DestroyCrl(CERTSignedCrl *crl); + +SECStatus +CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname, + CERTCertTrust *trust); + +SECStatus SEC_DeletePermCertificate(CERTCertificate *cert); + +PRBool +SEC_CrlIsNewer(CERTCrl *inNew, CERTCrl *old); + +SECCertTimeValidity +SEC_CheckCrlTimes(CERTCrl *crl, PRTime t); + +#ifdef notdef +/* +** Add a DER encoded certificate to the permanent database. +** "derCert" is the DER encoded certificate. +** "nickname" is the nickname to use for the cert +** "trust" is the trust parameters for the cert +*/ +SECStatus SEC_AddPermCertificate(PCERTCertDBHandle *handle, SECItem *derCert, + char *nickname, PCERTCertTrust *trust); + +certDBEntryCert * +SEC_FindPermCertByKey(PCERTCertDBHandle *handle, SECItem *certKey); + +certDBEntryCert +*SEC_FindPermCertByName(PCERTCertDBHandle *handle, SECItem *name); + +SECStatus SEC_OpenPermCertDB(PCERTCertDBHandle *handle, + PRBool readOnly, + PCERTDBNameFunc namecb, + void *cbarg); + + +typedef SECStatus (PR_CALLBACK * PermCertCallback)(PCERTCertificate *cert, + SECItem *k, void *pdata); +/* +** Traverse the entire permanent database, and pass the certs off to a +** user supplied function. +** "certfunc" is the user function to call for each certificate +** "udata" is the user's data, which is passed through to "certfunc" +*/ +SECStatus +PCERT_TraversePermCerts(PCERTCertDBHandle *handle, + PermCertCallback certfunc, + void *udata ); + +SECStatus +SEC_AddTempNickname(PCERTCertDBHandle *handle, char *nickname, SECItem *certKey); + +SECStatus +SEC_DeleteTempNickname(PCERTCertDBHandle *handle, char *nickname); + + +PRBool +SEC_CertDBKeyConflict(SECItem *derCert, PCERTCertDBHandle *handle); + +SECStatus +SEC_GetCrlTimes(PCERTCrl *dates, PRTime *notBefore, PRTime *notAfter); + +PCERTSignedCrl * +SEC_AddPermCrlToTemp(PCERTCertDBHandle *handle, certDBEntryRevocation *entry); + +SECStatus +SEC_DeleteTempCrl(PCERTSignedCrl *crl); + + +SECStatus +SEC_CheckKRL(PCERTCertDBHandle *handle,SECKEYLowPublicKey *key, + PCERTCertificate *rootCert, int64 t, void *wincx); + +SECStatus +SEC_CheckCRL(PCERTCertDBHandle *handle,PCERTCertificate *cert, + PCERTCertificate *caCert, int64 t, void *wincx); + +SECStatus +SEC_CrlReplaceUrl(PCERTSignedCrl *crl,char *url); +#endif + +SEC_END_PROTOS + +#endif /* _CERTDB_H_ */ diff --git a/security/nss/lib/certdb/certi.h b/security/nss/lib/certdb/certi.h new file mode 100644 index 000000000..d22acab3d --- /dev/null +++ b/security/nss/lib/certdb/certi.h @@ -0,0 +1,213 @@ +/* + * 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. + */ +/* + * certi.h - private data structures for the certificate library + * + * $Id$ + */ +#ifndef _CERTI_H_ +#define _CERTI_H_ + +#include "certt.h" +#include "nssrwlkt.h" + +#define USE_RWLOCK 1 + +/* all definitions in this file are subject to change */ + +typedef struct OpaqueCRLFieldsStr OpaqueCRLFields; +typedef struct CRLEntryCacheStr CRLEntryCache; +typedef struct CRLDPCacheStr CRLDPCache; +typedef struct CRLIssuerCacheStr CRLIssuerCache; +typedef struct CRLCacheStr CRLCache; + +struct OpaqueCRLFieldsStr { + PRBool partial; + PRBool badEntries; + PRBool bad; + PRBool badDER; + PRBool badExtensions; + PRBool deleted; + PRBool heapDER; + PRBool unverified; +}; + +typedef struct PreAllocatorStr PreAllocator; + +struct PreAllocatorStr +{ + PRSize len; + void* data; + PRSize used; + PRArenaPool* arena; + PRSize extra; +}; + +/* CRL entry cache. + This is the same as an entry plus the next/prev pointers for the hash table +*/ + +struct CRLEntryCacheStr { + CERTCrlEntry entry; + CRLEntryCache *prev, *next; +}; + +#define CRL_CACHE_INVALID_CRLS 0x0001 /* this state will be set + if we have CRL objects with an invalid DER or signature. Can be + cleared if the invalid objects are deleted from the token */ +#define CRL_CACHE_LAST_FETCH_FAILED 0x0002 /* this state will be set + if the last CRL fetch encountered an error. Can be cleared if a + new fetch succeeds */ + +#define CRL_CACHE_OUT_OF_MEMORY 0x0004 /* this state will be set + if we don't have enough memory to build the hash table of entries */ + +/* CRL distribution point cache object + This is a cache of CRL entries for a given distribution point of an issuer + It is built from a collection of one full and 0 or more delta CRLs. +*/ + +struct CRLDPCacheStr { +#ifdef USE_RWLOCK + NSSRWLock* lock; +#else + PRLock* lock; +#endif + CERTCertificate* issuer; /* cert issuer */ + SECItem* subject; /* DER of issuer subject */ + SECItem* distributionPoint; /* DER of distribution point. This may be + NULL when distribution points aren't + in use (ie. the CA has a single CRL) */ + + /* hash table of entries. We use a PLHashTable and pre-allocate the + required amount of memory in one shot, so that our allocator can + simply pass offsets into it when hashing. + + This won't work anymore when we support delta CRLs and iCRLs, because + the size of the hash table will vary over time. At that point, the best + solution will be to allocate large CRLEntry structures by modifying + the DER decoding template. The extra space would be for next/prev + pointers. This would allow entries from different CRLs to be mixed in + the same hash table. + */ + PLHashTable* entries; + PreAllocator* prebuffer; /* big pre-allocated buffer mentioned above */ + + /* array of CRLs matching this distribution point */ + PRUint32 ncrls; /* total number of CRLs in crls */ + CERTSignedCrl** crls; /* array of all matching DER CRLs + from all tokens */ + /* XCRL With iCRLs and multiple DPs, the CRL can be shared accross several + issuers. In the future, we'll need to globally recycle the CRL in a + separate list in order to avoid extra lookups, decodes, and copies */ + + /* pointers to good decoded CRLs used to build the cache */ + CERTSignedCrl* full; /* full CRL used for the cache */ +#if 0 + /* for future use */ + PRInt32 numdeltas; /* number of delta CRLs used for the cache */ + CERTSignedCrl** deltas; /* delta CRLs used for the cache */ +#endif + /* invalidity bitflag */ + PRUint16 invalid; /* this state will be set if either + CRL_CACHE_INVALID_CRLS or CRL_CACHE_LAST_FETCH_FAILED is set. + In those cases, all certs are considered revoked as a + security precaution. The invalid state can only be cleared + during an update if all error states are cleared */ +}; + +/* CRL issuer cache object + This object tracks all the distribution point caches for a given issuer. + XCRL once we support multiple issuing distribution points, this object + will be a hash table. For now, it just holds the single CRL distribution + point cache structure. +*/ + +struct CRLIssuerCacheStr { + SECItem* subject; /* DER of issuer subject */ + CRLDPCache dp; /* DER of distribution point */ + CRLDPCache* dpp; +#if 0 + /* XCRL for future use. + We don't need to lock at the moment because we only have one DP, + which gets created at the same time as this object */ + NSSRWLock* lock; + CRLDPCache** dps; + PLHashTable* distributionpoints; + CERTCertificate* issuer; +#endif +}; + +/* CRL revocation cache object + This object tracks all the issuer caches +*/ + +struct CRLCacheStr { + PRLock* lock; + /* hash table of issuer to CRLIssuerCacheStr, + indexed by issuer DER subject */ + PLHashTable* issuers; +}; + +SECStatus InitCRLCache(void); +SECStatus ShutdownCRLCache(void); + +/* Returns a pointer to an environment-like string, a series of +** null-terminated strings, terminated by a zero-length string. +** This function is intended to be internal to NSS. +*/ +extern char * cert_GetCertificateEmailAddresses(CERTCertificate *cert); + +/* + * These functions are used to map subjectKeyID extension values to certs. + */ +SECStatus +cert_CreateSubjectKeyIDHashTable(void); + +SECStatus +cert_AddSubjectKeyIDMapping(SECItem *subjKeyID, CERTCertificate *cert); + +/* + * Call this function to remove an entry from the mapping table. + */ +SECStatus +cert_RemoveSubjectKeyIDMapping(SECItem *subjKeyID); + +SECStatus +cert_DestroySubjectKeyIDHashTable(void); + +SECItem* +cert_FindDERCertBySubjectKeyID(SECItem *subjKeyID); + +#endif /* _CERTI_H_ */ + diff --git a/security/nss/lib/certdb/certt.h b/security/nss/lib/certdb/certt.h new file mode 100644 index 000000000..f6b8f74bc --- /dev/null +++ b/security/nss/lib/certdb/certt.h @@ -0,0 +1,853 @@ +/* + * 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. + */ +/* + * certt.h - public data structures for the certificate library + * + * $Id$ + */ +#ifndef _CERTT_H_ +#define _CERTT_H_ + +#include "prclist.h" +#include "pkcs11t.h" +#include "seccomon.h" +#include "secmodt.h" +#include "secoidt.h" +#include "plarena.h" +#include "prcvar.h" +#include "nssilock.h" +#include "prio.h" +#include "prmon.h" + +/* Stan data types */ +struct NSSCertificateStr; +struct NSSTrustDomainStr; + +/* Non-opaque objects */ +typedef struct CERTAVAStr CERTAVA; +typedef struct CERTAttributeStr CERTAttribute; +typedef struct CERTAuthInfoAccessStr CERTAuthInfoAccess; +typedef struct CERTAuthKeyIDStr CERTAuthKeyID; +typedef struct CERTBasicConstraintsStr CERTBasicConstraints; +#ifdef NSS_CLASSIC +typedef struct CERTCertDBHandleStr CERTCertDBHandle; +#else +typedef struct NSSTrustDomainStr CERTCertDBHandle; +#endif +typedef struct CERTCertExtensionStr CERTCertExtension; +typedef struct CERTCertKeyStr CERTCertKey; +typedef struct CERTCertListStr CERTCertList; +typedef struct CERTCertListNodeStr CERTCertListNode; +typedef struct CERTCertNicknamesStr CERTCertNicknames; +typedef struct CERTCertTrustStr CERTCertTrust; +typedef struct CERTCertificateStr CERTCertificate; +typedef struct CERTCertificateListStr CERTCertificateList; +typedef struct CERTCertificateRequestStr CERTCertificateRequest; +typedef struct CERTCrlStr CERTCrl; +typedef struct CERTCrlDistributionPointsStr CERTCrlDistributionPoints; +typedef struct CERTCrlEntryStr CERTCrlEntry; +typedef struct CERTCrlHeadNodeStr CERTCrlHeadNode; +typedef struct CERTCrlKeyStr CERTCrlKey; +typedef struct CERTCrlNodeStr CERTCrlNode; +typedef struct CERTDERCertsStr CERTDERCerts; +typedef struct CERTDistNamesStr CERTDistNames; +typedef struct CERTGeneralNameStr CERTGeneralName; +typedef struct CERTGeneralNameListStr CERTGeneralNameList; +typedef struct CERTIssuerAndSNStr CERTIssuerAndSN; +typedef struct CERTNameStr CERTName; +typedef struct CERTNameConstraintStr CERTNameConstraint; +typedef struct CERTNameConstraintsStr CERTNameConstraints; +typedef struct CERTOKDomainNameStr CERTOKDomainName; +typedef struct CERTPublicKeyAndChallengeStr CERTPublicKeyAndChallenge; +typedef struct CERTRDNStr CERTRDN; +typedef struct CERTSignedCrlStr CERTSignedCrl; +typedef struct CERTSignedDataStr CERTSignedData; +typedef struct CERTStatusConfigStr CERTStatusConfig; +typedef struct CERTSubjectListStr CERTSubjectList; +typedef struct CERTSubjectNodeStr CERTSubjectNode; +typedef struct CERTSubjectPublicKeyInfoStr CERTSubjectPublicKeyInfo; +typedef struct CERTValidityStr CERTValidity; +typedef struct CERTVerifyLogStr CERTVerifyLog; +typedef struct CERTVerifyLogNodeStr CERTVerifyLogNode; +typedef struct CRLDistributionPointStr CRLDistributionPoint; + +/* CRL extensions type */ +typedef unsigned long CERTCrlNumber; + +/* +** An X.500 AVA object +*/ +struct CERTAVAStr { + SECItem type; + SECItem value; +}; + +/* +** An X.500 RDN object +*/ +struct CERTRDNStr { + CERTAVA **avas; +}; + +/* +** An X.500 name object +*/ +struct CERTNameStr { + PRArenaPool *arena; + CERTRDN **rdns; +}; + +/* +** An X.509 validity object +*/ +struct CERTValidityStr { + PRArenaPool *arena; + SECItem notBefore; + SECItem notAfter; +}; + +/* + * A serial number and issuer name, which is used as a database key + */ +struct CERTCertKeyStr { + SECItem serialNumber; + SECItem derIssuer; +}; + +/* +** A signed data object. Used to implement the "signed" macro used +** in the X.500 specs. +*/ +struct CERTSignedDataStr { + SECItem data; + SECAlgorithmID signatureAlgorithm; + SECItem signature; +}; + +/* +** An X.509 subject-public-key-info object +*/ +struct CERTSubjectPublicKeyInfoStr { + PRArenaPool *arena; + SECAlgorithmID algorithm; + SECItem subjectPublicKey; +}; + +struct CERTPublicKeyAndChallengeStr { + SECItem spki; + SECItem challenge; +}; + +struct CERTCertTrustStr { + unsigned int sslFlags; + unsigned int emailFlags; + unsigned int objectSigningFlags; +}; + +/* + * defined the types of trust that exist + */ +typedef enum SECTrustTypeEnum { + trustSSL = 0, + trustEmail = 1, + trustObjectSigning = 2, + trustTypeNone = 3 +} SECTrustType; + +#define SEC_GET_TRUST_FLAGS(trust,type) \ + (((type)==trustSSL)?((trust)->sslFlags): \ + (((type)==trustEmail)?((trust)->emailFlags): \ + (((type)==trustObjectSigning)?((trust)->objectSigningFlags):0))) + +/* +** An X.509.3 certificate extension +*/ +struct CERTCertExtensionStr { + SECItem id; + SECItem critical; + SECItem value; +}; + +struct CERTSubjectNodeStr { + struct CERTSubjectNodeStr *next; + struct CERTSubjectNodeStr *prev; + SECItem certKey; + SECItem keyID; +}; + +struct CERTSubjectListStr { + PRArenaPool *arena; + int ncerts; + char *emailAddr; + CERTSubjectNode *head; + CERTSubjectNode *tail; /* do we need tail? */ + void *entry; +}; + +/* +** An X.509 certificate object (the unsigned form) +*/ +struct CERTCertificateStr { + /* the arena is used to allocate any data structures that have the same + * lifetime as the cert. This is all stuff that hangs off of the cert + * structure, and is all freed at the same time. I is used when the + * cert is decoded, destroyed, and at some times when it changes + * state + */ + PRArenaPool *arena; + + /* The following fields are static after the cert has been decoded */ + char *subjectName; + char *issuerName; + CERTSignedData signatureWrap; /* XXX */ + SECItem derCert; /* original DER for the cert */ + SECItem derIssuer; /* DER for issuer name */ + SECItem derSubject; /* DER for subject name */ + SECItem derPublicKey; /* DER for the public key */ + SECItem certKey; /* database key for this cert */ + SECItem version; + SECItem serialNumber; + SECAlgorithmID signature; + CERTName issuer; + CERTValidity validity; + CERTName subject; + CERTSubjectPublicKeyInfo subjectPublicKeyInfo; + SECItem issuerID; + SECItem subjectID; + CERTCertExtension **extensions; + char *emailAddr; + CERTCertDBHandle *dbhandle; + SECItem subjectKeyID; /* x509v3 subject key identifier */ + PRBool keyIDGenerated; /* was the keyid generated? */ + unsigned int keyUsage; /* what uses are allowed for this cert */ + unsigned int rawKeyUsage; /* value of the key usage extension */ + PRBool keyUsagePresent; /* was the key usage extension present */ + unsigned int nsCertType; /* value of the ns cert type extension */ + + /* these values can be set by the application to bypass certain checks + * or to keep the cert in memory for an entire session. + * XXX - need an api to set these + */ + PRBool keepSession; /* keep this cert for entire session*/ + PRBool timeOK; /* is the bad validity time ok? */ + CERTOKDomainName *domainOK; /* these domain names are ok */ + + /* + * these values can change when the cert changes state. These state + * changes include transitions from temp to perm or vice-versa, and + * changes of trust flags + */ + PRBool isperm; + PRBool istemp; + char *nickname; + char *dbnickname; + struct NSSCertificateStr *nssCertificate; /* This is Stan stuff. */ + CERTCertTrust *trust; + + /* the reference count is modified whenever someone looks up, dups + * or destroys a certificate + */ + int referenceCount; + + /* The subject list is a list of all certs with the same subject name. + * It can be modified any time a cert is added or deleted from either + * the in-memory(temporary) or on-disk(permanent) database. + */ + CERTSubjectList *subjectList; + + /* these belong in the static section, but are here to maintain + * the structure's integrity + */ + CERTAuthKeyID * authKeyID; /* x509v3 authority key identifier */ + PRBool isRoot; /* cert is the end of a chain */ + + /* these fields are used by client GUI code to keep track of ssl sockets + * that are blocked waiting on GUI feedback related to this cert. + * XXX - these should be moved into some sort of application specific + * data structure. They are only used by the browser right now. + */ + struct SECSocketNode *authsocketlist; + int series; /* was int authsocketcount; record the series of the pkcs11ID */ + + /* This is PKCS #11 stuff. */ + PK11SlotInfo *slot; /*if this cert came of a token, which is it*/ + CK_OBJECT_HANDLE pkcs11ID; /*and which object on that token is it */ + PRBool ownSlot; /*true if the cert owns the slot reference */ +}; +#define SEC_CERTIFICATE_VERSION_1 0 /* default created */ +#define SEC_CERTIFICATE_VERSION_2 1 /* v2 */ +#define SEC_CERTIFICATE_VERSION_3 2 /* v3 extensions */ + +#define SEC_CRL_VERSION_1 0 /* default */ +#define SEC_CRL_VERSION_2 1 /* v2 extensions */ + +/* + * used to identify class of cert in mime stream code + */ +#define SEC_CERT_CLASS_CA 1 +#define SEC_CERT_CLASS_SERVER 2 +#define SEC_CERT_CLASS_USER 3 +#define SEC_CERT_CLASS_EMAIL 4 + +struct CERTDERCertsStr { + PRArenaPool *arena; + int numcerts; + SECItem *rawCerts; +}; + +/* +** A PKCS ? Attribute +** XXX this is duplicated through out the code, it *should* be moved +** to a central location. Where would be appropriate? +*/ +struct CERTAttributeStr { + SECItem attrType; + SECItem **attrValue; +}; + +/* +** A PKCS#10 certificate-request object (the unsigned form) +*/ +struct CERTCertificateRequestStr { + PRArenaPool *arena; + SECItem version; + CERTName subject; + CERTSubjectPublicKeyInfo subjectPublicKeyInfo; + SECItem **attributes; +}; +#define SEC_CERTIFICATE_REQUEST_VERSION 0 /* what we *create* */ + + +/* +** A certificate list object. +*/ +struct CERTCertificateListStr { + SECItem *certs; + int len; /* number of certs */ + PRArenaPool *arena; +}; + +struct CERTCertListNodeStr { + PRCList links; + CERTCertificate *cert; + void *appData; +}; + +struct CERTCertListStr { + PRCList list; + PRArenaPool *arena; +}; + +#define CERT_LIST_HEAD(l) ((CERTCertListNode *)PR_LIST_HEAD(&l->list)) +#define CERT_LIST_NEXT(n) ((CERTCertListNode *)n->links.next) +#define CERT_LIST_END(n,l) (((void *)n) == ((void *)&l->list)) +#define CERT_LIST_EMPTY(l) CERT_LIST_END(CERT_LIST_HEAD(l), l) + +struct CERTCrlEntryStr { + SECItem serialNumber; + SECItem revocationDate; + CERTCertExtension **extensions; +}; + +struct CERTCrlStr { + PRArenaPool *arena; + SECItem version; + SECAlgorithmID signatureAlg; + SECItem derName; + CERTName name; + SECItem lastUpdate; + SECItem nextUpdate; /* optional for x.509 CRL */ + CERTCrlEntry **entries; + CERTCertExtension **extensions; + /* can't add anything there for binary backwards compatibility reasons */ +}; + +struct CERTCrlKeyStr { + SECItem derName; + SECItem dummy; /* The decoder can not skip a primitive, + this serves as a place holder for the + decoder to finish its task only + */ +}; + +struct CERTSignedCrlStr { + PRArenaPool *arena; + CERTCrl crl; + void *reserved1; + PRBool reserved2; + PRBool isperm; + PRBool istemp; + int referenceCount; + CERTCertDBHandle *dbhandle; + CERTSignedData signatureWrap; /* XXX */ + char *url; + SECItem *derCrl; + PK11SlotInfo *slot; + CK_OBJECT_HANDLE pkcs11ID; + void* opaque; /* do not touch */ +}; + + +struct CERTCrlHeadNodeStr { + PRArenaPool *arena; + CERTCertDBHandle *dbhandle; + CERTCrlNode *first; + CERTCrlNode *last; +}; + + +struct CERTCrlNodeStr { + CERTCrlNode *next; + int type; + CERTSignedCrl *crl; +}; + + +/* + * Array of X.500 Distinguished Names + */ +struct CERTDistNamesStr { + PRArenaPool *arena; + int nnames; + SECItem *names; + void *head; /* private */ +}; + + +#define NS_CERT_TYPE_SSL_CLIENT (0x80) /* bit 0 */ +#define NS_CERT_TYPE_SSL_SERVER (0x40) /* bit 1 */ +#define NS_CERT_TYPE_EMAIL (0x20) /* bit 2 */ +#define NS_CERT_TYPE_OBJECT_SIGNING (0x10) /* bit 3 */ +#define NS_CERT_TYPE_RESERVED (0x08) /* bit 4 */ +#define NS_CERT_TYPE_SSL_CA (0x04) /* bit 5 */ +#define NS_CERT_TYPE_EMAIL_CA (0x02) /* bit 6 */ +#define NS_CERT_TYPE_OBJECT_SIGNING_CA (0x01) /* bit 7 */ + +#define EXT_KEY_USAGE_TIME_STAMP (0x8000) +#define EXT_KEY_USAGE_STATUS_RESPONDER (0x4000) + +#define NS_CERT_TYPE_APP ( NS_CERT_TYPE_SSL_CLIENT | \ + NS_CERT_TYPE_SSL_SERVER | \ + NS_CERT_TYPE_EMAIL | \ + NS_CERT_TYPE_OBJECT_SIGNING ) + +#define NS_CERT_TYPE_CA ( NS_CERT_TYPE_SSL_CA | \ + NS_CERT_TYPE_EMAIL_CA | \ + NS_CERT_TYPE_OBJECT_SIGNING_CA | \ + EXT_KEY_USAGE_STATUS_RESPONDER ) +typedef enum SECCertUsageEnum { + certUsageSSLClient = 0, + certUsageSSLServer = 1, + certUsageSSLServerWithStepUp = 2, + certUsageSSLCA = 3, + certUsageEmailSigner = 4, + certUsageEmailRecipient = 5, + certUsageObjectSigner = 6, + certUsageUserCertImport = 7, + certUsageVerifyCA = 8, + certUsageProtectedObjectSigner = 9, + certUsageStatusResponder = 10, + certUsageAnyCA = 11 +} SECCertUsage; + +typedef PRInt64 SECCertificateUsage; + +#define certificateUsageSSLClient (0x0001) +#define certificateUsageSSLServer (0x0002) +#define certificateUsageSSLServerWithStepUp (0x0004) +#define certificateUsageSSLCA (0x0008) +#define certificateUsageEmailSigner (0x0010) +#define certificateUsageEmailRecipient (0x0020) +#define certificateUsageObjectSigner (0x0040) +#define certificateUsageUserCertImport (0x0080) +#define certificateUsageVerifyCA (0x0100) +#define certificateUsageProtectedObjectSigner (0x0200) +#define certificateUsageStatusResponder (0x0400) +#define certificateUsageAnyCA (0x0800) + +#define certificateUsageHighest certificateUsageAnyCA + +/* + * Does the cert belong to the user, a peer, or a CA. + */ +typedef enum CERTCertOwnerEnum { + certOwnerUser = 0, + certOwnerPeer = 1, + certOwnerCA = 2 +} CERTCertOwner; + +/* + * This enum represents the state of validity times of a certificate + */ +typedef enum SECCertTimeValidityEnum { + secCertTimeValid = 0, + secCertTimeExpired = 1, + secCertTimeNotValidYet = 2 +} SECCertTimeValidity; + +/* + * Interface for getting certificate nickname strings out of the database + */ + +/* these are values for the what argument below */ +#define SEC_CERT_NICKNAMES_ALL 1 +#define SEC_CERT_NICKNAMES_USER 2 +#define SEC_CERT_NICKNAMES_SERVER 3 +#define SEC_CERT_NICKNAMES_CA 4 + +struct CERTCertNicknamesStr { + PRArenaPool *arena; + void *head; + int numnicknames; + char **nicknames; + int what; + int totallen; +}; + +struct CERTIssuerAndSNStr { + SECItem derIssuer; + CERTName issuer; + SECItem serialNumber; +}; + + +/* X.509 v3 Key Usage Extension flags */ +#define KU_DIGITAL_SIGNATURE (0x80) /* bit 0 */ +#define KU_NON_REPUDIATION (0x40) /* bit 1 */ +#define KU_KEY_ENCIPHERMENT (0x20) /* bit 2 */ +#define KU_DATA_ENCIPHERMENT (0x10) /* bit 3 */ +#define KU_KEY_AGREEMENT (0x08) /* bit 4 */ +#define KU_KEY_CERT_SIGN (0x04) /* bit 5 */ +#define KU_CRL_SIGN (0x02) /* bit 6 */ +#define KU_ALL (KU_DIGITAL_SIGNATURE | \ + KU_NON_REPUDIATION | \ + KU_KEY_ENCIPHERMENT | \ + KU_DATA_ENCIPHERMENT | \ + KU_KEY_AGREEMENT | \ + KU_KEY_CERT_SIGN | \ + KU_CRL_SIGN) + +/* This value will not occur in certs. It is used internally for the case + * when the key type is not know ahead of time and either key agreement or + * key encipherment are the correct value based on key type + */ +#define KU_KEY_AGREEMENT_OR_ENCIPHERMENT (0x4000) + +/* internal bits that do not match bits in the x509v3 spec, but are used + * for similar purposes + */ +#define KU_NS_GOVT_APPROVED (0x8000) /*don't make part of KU_ALL!*/ +/* + * x.509 v3 Basic Constraints Extension + * If isCA is false, the pathLenConstraint is ignored. + * Otherwise, the following pathLenConstraint values will apply: + * < 0 - there is no limit to the certificate path + * 0 - CA can issues end-entity certificates only + * > 0 - the number of certificates in the certificate path is + * limited to this number + */ +#define CERT_UNLIMITED_PATH_CONSTRAINT -2 + +struct CERTBasicConstraintsStr { + PRBool isCA; /* on if is CA */ + int pathLenConstraint; /* maximum number of certificates that can be + in the cert path. Only applies to a CA + certificate; otherwise, it's ignored. + */ +}; + +/* Maximum length of a certificate chain */ +#define CERT_MAX_CERT_CHAIN 20 + +/* x.509 v3 Reason Falgs, used in CRLDistributionPoint Extension */ +#define RF_UNUSED (0x80) /* bit 0 */ +#define RF_KEY_COMPROMISE (0x40) /* bit 1 */ +#define RF_CA_COMPROMISE (0x20) /* bit 2 */ +#define RF_AFFILIATION_CHANGED (0x10) /* bit 3 */ +#define RF_SUPERSEDED (0x08) /* bit 4 */ +#define RF_CESSATION_OF_OPERATION (0x04) /* bit 5 */ +#define RF_CERTIFICATE_HOLD (0x02) /* bit 6 */ + +/* If we needed to extract the general name field, use this */ +/* General Name types */ +typedef enum CERTGeneralNameTypeEnum { + certOtherName = 1, + certRFC822Name = 2, + certDNSName = 3, + certX400Address = 4, + certDirectoryName = 5, + certEDIPartyName = 6, + certURI = 7, + certIPAddress = 8, + certRegisterID = 9 +} CERTGeneralNameType; + + +typedef struct OtherNameStr { + SECItem name; + SECItem oid; +}OtherName; + + + +struct CERTGeneralNameStr { + CERTGeneralNameType type; /* name type */ + union { + CERTName directoryName; /* distinguish name */ + OtherName OthName; /* Other Name */ + SECItem other; /* the rest of the name forms */ + }name; + SECItem derDirectoryName; /* this is saved to simplify directory name + comparison */ + PRCList l; +}; + +struct CERTGeneralNameListStr { + PRArenaPool *arena; + CERTGeneralName *name; + int refCount; + int len; + PZLock *lock; +}; + +struct CERTNameConstraintStr { + CERTGeneralName name; + SECItem DERName; + SECItem min; + SECItem max; + PRCList l; +}; + + +struct CERTNameConstraintsStr { + CERTNameConstraint *permited; + CERTNameConstraint *excluded; + SECItem **DERPermited; + SECItem **DERExcluded; +}; + + +/* X.509 v3 Authority Key Identifier extension. For the authority certificate + issuer field, we only support URI now. + */ +struct CERTAuthKeyIDStr { + SECItem keyID; /* unique key identifier */ + CERTGeneralName *authCertIssuer; /* CA's issuer name. End with a NULL */ + SECItem authCertSerialNumber; /* CA's certificate serial number */ + SECItem **DERAuthCertIssuer; /* This holds the DER encoded format of + the authCertIssuer field. It is used + by the encoding engine. It should be + used as a read only field by the caller. + */ +}; + +/* x.509 v3 CRL Distributeion Point */ + +/* + * defined the types of CRL Distribution points + */ +typedef enum DistributionPointTypesEnum { + generalName = 1, /* only support this for now */ + relativeDistinguishedName = 2 +} DistributionPointTypes; + +struct CRLDistributionPointStr { + DistributionPointTypes distPointType; + union { + CERTGeneralName *fullName; + CERTRDN relativeName; + } distPoint; + SECItem reasons; + CERTGeneralName *crlIssuer; + + /* Reserved for internal use only*/ + SECItem derDistPoint; + SECItem derRelativeName; + SECItem **derCrlIssuer; + SECItem **derFullName; + SECItem bitsmap; +}; + +struct CERTCrlDistributionPointsStr { + CRLDistributionPoint **distPoints; +}; + +/* + * This structure is used to keep a log of errors when verifying + * a cert chain. This allows multiple errors to be reported all at + * once. + */ +struct CERTVerifyLogNodeStr { + CERTCertificate *cert; /* what cert had the error */ + long error; /* what error was it? */ + unsigned int depth; /* how far up the chain are we */ + void *arg; /* error specific argument */ + struct CERTVerifyLogNodeStr *next; /* next in the list */ + struct CERTVerifyLogNodeStr *prev; /* next in the list */ +}; + + +struct CERTVerifyLogStr { + PRArenaPool *arena; + unsigned int count; + struct CERTVerifyLogNodeStr *head; + struct CERTVerifyLogNodeStr *tail; +}; + + +struct CERTOKDomainNameStr { + CERTOKDomainName *next; + char name[1]; /* actual length may be longer. */ +}; + + +typedef SECStatus (PR_CALLBACK *CERTStatusChecker) (CERTCertDBHandle *handle, + CERTCertificate *cert, + int64 time, + void *pwArg); + +typedef SECStatus (PR_CALLBACK *CERTStatusDestroy) (CERTStatusConfig *handle); + +struct CERTStatusConfigStr { + CERTStatusChecker statusChecker; /* NULL means no checking enabled */ + CERTStatusDestroy statusDestroy; /* enabled or no, will clean up */ + void *statusContext; /* cx specific to checking protocol */ +}; + +struct CERTAuthInfoAccessStr { + SECItem method; + SECItem derLocation; + CERTGeneralName *location; /* decoded location */ +}; + + +/* This is the typedef for the callback passed to CERT_OpenCertDB() */ +/* callback to return database name based on version number */ +typedef char * (*CERTDBNameFunc)(void *arg, int dbVersion); + +/* + * types of cert packages that we can decode + */ +typedef enum CERTPackageTypeEnum { + certPackageNone = 0, + certPackageCert = 1, + certPackagePKCS7 = 2, + certPackageNSCertSeq = 3, + certPackageNSCertWrap = 4 +} CERTPackageType; + +/* + * these types are for the PKIX Certificate Policies extension + */ +typedef struct { + SECOidTag oid; + SECItem qualifierID; + SECItem qualifierValue; +} CERTPolicyQualifier; + +typedef struct { + SECOidTag oid; + SECItem policyID; + CERTPolicyQualifier **policyQualifiers; +} CERTPolicyInfo; + +typedef struct { + PRArenaPool *arena; + CERTPolicyInfo **policyInfos; +} CERTCertificatePolicies; + +typedef struct { + SECItem organization; + SECItem **noticeNumbers; +} CERTNoticeReference; + +typedef struct { + PRArenaPool *arena; + CERTNoticeReference noticeReference; + SECItem derNoticeReference; + SECItem displayText; +} CERTUserNotice; + +typedef struct { + PRArenaPool *arena; + SECItem **oids; +} CERTOidSequence; + + +/* XXX Lisa thinks the template declarations belong in cert.h, not here? */ + +#include "secasn1t.h" /* way down here because I expect template stuff to + * move out of here anyway */ + +SEC_BEGIN_PROTOS + +extern const SEC_ASN1Template CERT_CertificateRequestTemplate[]; +extern const SEC_ASN1Template CERT_CertificateTemplate[]; +extern const SEC_ASN1Template SEC_SignedCertificateTemplate[]; +extern const SEC_ASN1Template CERT_CertExtensionTemplate[]; +extern const SEC_ASN1Template CERT_SequenceOfCertExtensionTemplate[]; +extern const SEC_ASN1Template SECKEY_PublicKeyTemplate[]; +extern const SEC_ASN1Template CERT_SubjectPublicKeyInfoTemplate[]; +extern const SEC_ASN1Template CERT_ValidityTemplate[]; +extern const SEC_ASN1Template CERT_PublicKeyAndChallengeTemplate[]; +extern const SEC_ASN1Template SEC_CertSequenceTemplate[]; + +extern const SEC_ASN1Template CERT_IssuerAndSNTemplate[]; +extern const SEC_ASN1Template CERT_NameTemplate[]; +extern const SEC_ASN1Template CERT_SetOfSignedCrlTemplate[]; +extern const SEC_ASN1Template CERT_RDNTemplate[]; +extern const SEC_ASN1Template CERT_SignedDataTemplate[]; +extern const SEC_ASN1Template CERT_CrlTemplate[]; + +/* +** XXX should the attribute stuff be centralized for all of ns/security? +*/ +extern const SEC_ASN1Template CERT_AttributeTemplate[]; +extern const SEC_ASN1Template CERT_SetOfAttributeTemplate[]; + +/* These functions simply return the address of the above-declared templates. +** This is necessary for Windows DLLs. Sigh. +*/ +SEC_ASN1_CHOOSER_DECLARE(CERT_CertificateRequestTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_CertificateTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_CrlTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_NameTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_SetOfSignedCrlTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_SignedDataTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_SubjectPublicKeyInfoTemplate) +SEC_ASN1_CHOOSER_DECLARE(SEC_SignedCertificateTemplate) + +SEC_END_PROTOS + +#endif /* _CERTT_H_ */ diff --git a/security/nss/lib/certdb/certv3.c b/security/nss/lib/certdb/certv3.c new file mode 100644 index 000000000..e50c66279 --- /dev/null +++ b/security/nss/lib/certdb/certv3.c @@ -0,0 +1,406 @@ +/* + * 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. + */ + +/* + * Code for dealing with X509.V3 extensions. + * + * $Id$ + */ + +#include "cert.h" +#include "secitem.h" +#include "secoid.h" +#include "secder.h" +#include "secasn1.h" +#include "certxutl.h" +#include "secerr.h" + +SECStatus +CERT_FindCertExtensionByOID(CERTCertificate *cert, SECItem *oid, + SECItem *value) +{ + return (cert_FindExtensionByOID (cert->extensions, oid, value)); +} + + +SECStatus +CERT_FindCertExtension(CERTCertificate *cert, int tag, SECItem *value) +{ + return (cert_FindExtension (cert->extensions, tag, value)); +} + +static void +SetExts(void *object, CERTCertExtension **exts) +{ + CERTCertificate *cert = (CERTCertificate *)object; + + cert->extensions = exts; + DER_SetUInteger (cert->arena, &(cert->version), SEC_CERTIFICATE_VERSION_3); +} + +void * +CERT_StartCertExtensions(CERTCertificate *cert) +{ + return (cert_StartExtensions ((void *)cert, cert->arena, SetExts)); +} + +/* find the given extension in the certificate of the Issuer of 'cert' */ +SECStatus +CERT_FindIssuerCertExtension(CERTCertificate *cert, int tag, SECItem *value) +{ + CERTCertificate *issuercert; + SECStatus rv; + + issuercert = CERT_FindCertByName(cert->dbhandle, &cert->derIssuer); + if ( issuercert ) { + rv = cert_FindExtension(issuercert->extensions, tag, value); + CERT_DestroyCertificate(issuercert); + } else { + rv = SECFailure; + } + + return(rv); +} + +/* find a URL extension in the cert or its CA + * apply the base URL string if it exists + */ +char * +CERT_FindCertURLExtension(CERTCertificate *cert, int tag, int catag) +{ + SECStatus rv; + SECItem urlitem; + SECItem baseitem; + SECItem urlstringitem = {siBuffer,0}; + SECItem basestringitem = {siBuffer,0}; + PRArenaPool *arena = NULL; + PRBool hasbase; + char *urlstring; + char *str; + int len; + unsigned int i; + + urlstring = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( ! arena ) { + goto loser; + } + + hasbase = PR_FALSE; + urlitem.data = NULL; + baseitem.data = NULL; + + rv = cert_FindExtension(cert->extensions, tag, &urlitem); + if ( rv == SECSuccess ) { + rv = cert_FindExtension(cert->extensions, SEC_OID_NS_CERT_EXT_BASE_URL, + &baseitem); + if ( rv == SECSuccess ) { + hasbase = PR_TRUE; + } + + } else if ( catag ) { + /* if the cert doesn't have the extensions, see if the issuer does */ + rv = CERT_FindIssuerCertExtension(cert, catag, &urlitem); + if ( rv != SECSuccess ) { + goto loser; + } + rv = CERT_FindIssuerCertExtension(cert, SEC_OID_NS_CERT_EXT_BASE_URL, + &baseitem); + if ( rv == SECSuccess ) { + hasbase = PR_TRUE; + } + } else { + goto loser; + } + + rv = SEC_QuickDERDecodeItem(arena, &urlstringitem, SEC_IA5StringTemplate, + &urlitem); + + if ( rv != SECSuccess ) { + goto loser; + } + if ( hasbase ) { + rv = SEC_QuickDERDecodeItem(arena, &basestringitem, SEC_IA5StringTemplate, + &baseitem); + + if ( rv != SECSuccess ) { + goto loser; + } + } + + len = urlstringitem.len + ( hasbase ? basestringitem.len : 0 ) + 1; + + str = urlstring = (char *)PORT_Alloc(len); + if ( urlstring == NULL ) { + goto loser; + } + + /* copy the URL base first */ + if ( hasbase ) { + + /* if the urlstring has a : in it, then we assume it is an absolute + * URL, and will not get the base string pre-pended + */ + for ( i = 0; i < urlstringitem.len; i++ ) { + if ( urlstringitem.data[i] == ':' ) { + goto nobase; + } + } + + PORT_Memcpy(str, basestringitem.data, basestringitem.len); + str += basestringitem.len; + + } + +nobase: + /* copy the rest (or all) of the URL */ + PORT_Memcpy(str, urlstringitem.data, urlstringitem.len); + str += urlstringitem.len; + + *str = '\0'; + goto done; + +loser: + if ( urlstring ) { + PORT_Free(urlstring); + } + + urlstring = NULL; +done: + if ( arena ) { + PORT_FreeArena(arena, PR_FALSE); + } + if ( baseitem.data ) { + PORT_Free(baseitem.data); + } + if ( urlitem.data ) { + PORT_Free(urlitem.data); + } + + return(urlstring); +} + +/* + * get the value of the Netscape Certificate Type Extension + */ +SECStatus +CERT_FindNSCertTypeExtension(CERTCertificate *cert, SECItem *retItem) +{ + + return (CERT_FindBitStringExtension + (cert->extensions, SEC_OID_NS_CERT_EXT_CERT_TYPE, retItem)); +} + + +/* + * get the value of a string type extension + */ +char * +CERT_FindNSStringExtension(CERTCertificate *cert, int oidtag) +{ + SECItem wrapperItem, tmpItem = {siBuffer,0}; + SECStatus rv; + PRArenaPool *arena = NULL; + char *retstring = NULL; + + wrapperItem.data = NULL; + tmpItem.data = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if ( ! arena ) { + goto loser; + } + + rv = cert_FindExtension(cert->extensions, oidtag, + &wrapperItem); + if ( rv != SECSuccess ) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem(arena, &tmpItem, SEC_IA5StringTemplate, + &wrapperItem); + + if ( rv != SECSuccess ) { + goto loser; + } + + retstring = (char *)PORT_Alloc(tmpItem.len + 1 ); + if ( retstring == NULL ) { + goto loser; + } + + PORT_Memcpy(retstring, tmpItem.data, tmpItem.len); + retstring[tmpItem.len] = '\0'; + +loser: + if ( arena ) { + PORT_FreeArena(arena, PR_FALSE); + } + + if ( wrapperItem.data ) { + PORT_Free(wrapperItem.data); + } + + return(retstring); +} + +/* + * get the value of the X.509 v3 Key Usage Extension + */ +SECStatus +CERT_FindKeyUsageExtension(CERTCertificate *cert, SECItem *retItem) +{ + + return (CERT_FindBitStringExtension(cert->extensions, + SEC_OID_X509_KEY_USAGE, retItem)); +} + +/* + * get the value of the X.509 v3 Key Usage Extension + */ +SECStatus +CERT_FindSubjectKeyIDExtension(CERTCertificate *cert, SECItem *retItem) +{ + + SECItem encodedValue; + SECStatus rv; + + encodedValue.data = NULL; + rv = cert_FindExtension + (cert->extensions, SEC_OID_X509_SUBJECT_KEY_ID, &encodedValue); + if (rv != SECSuccess) + return (rv); + rv = SEC_ASN1DecodeItem (NULL, retItem, SEC_OctetStringTemplate, + &encodedValue); + PORT_Free (encodedValue.data); + + return (rv); +} + +SECStatus +CERT_FindBasicConstraintExten(CERTCertificate *cert, + CERTBasicConstraints *value) +{ + SECItem encodedExtenValue; + SECStatus rv; + + encodedExtenValue.data = NULL; + encodedExtenValue.len = 0; + + rv = cert_FindExtension(cert->extensions, SEC_OID_X509_BASIC_CONSTRAINTS, + &encodedExtenValue); + if ( rv != SECSuccess ) { + return (rv); + } + + rv = CERT_DecodeBasicConstraintValue (value, &encodedExtenValue); + + /* free the raw extension data */ + PORT_Free(encodedExtenValue.data); + encodedExtenValue.data = NULL; + + return(rv); +} + +CERTAuthKeyID * +CERT_FindAuthKeyIDExten (PRArenaPool *arena, CERTCertificate *cert) +{ + SECItem encodedExtenValue; + SECStatus rv; + CERTAuthKeyID *ret; + + encodedExtenValue.data = NULL; + encodedExtenValue.len = 0; + + rv = cert_FindExtension(cert->extensions, SEC_OID_X509_AUTH_KEY_ID, + &encodedExtenValue); + if ( rv != SECSuccess ) { + return (NULL); + } + + ret = CERT_DecodeAuthKeyID (arena, &encodedExtenValue); + + PORT_Free(encodedExtenValue.data); + encodedExtenValue.data = NULL; + + return(ret); +} + +SECStatus +CERT_CheckCertUsage(CERTCertificate *cert, unsigned char usage) +{ + PRBool critical; + SECItem keyUsage; + SECStatus rv; + + /* There is no extension, v1 or v2 certificate */ + if (cert->extensions == NULL) { + return (SECSuccess); + } + + keyUsage.data = NULL; + + do { + /* if the keyUsage extension exists and is critical, make sure that the + CA certificate is used for certificate signing purpose only. If the + extension does not exist, we will assum that it can be used for + certificate signing purpose. + */ + rv = CERT_GetExtenCriticality(cert->extensions, + SEC_OID_X509_KEY_USAGE, + &critical); + if (rv == SECFailure) { + rv = (PORT_GetError () == SEC_ERROR_EXTENSION_NOT_FOUND) ? + SECSuccess : SECFailure; + break; + } + + if (critical == PR_FALSE) { + rv = SECSuccess; + break; + } + + rv = CERT_FindKeyUsageExtension(cert, &keyUsage); + if (rv != SECSuccess) { + break; + } + if (!(keyUsage.data[0] & usage)) { + PORT_SetError (SEC_ERROR_CERT_USAGES_INVALID); + rv = SECFailure; + } + }while (0); + PORT_Free (keyUsage.data); + return (rv); +} diff --git a/security/nss/lib/certdb/certxutl.c b/security/nss/lib/certdb/certxutl.c new file mode 100644 index 000000000..9e9fa9b97 --- /dev/null +++ b/security/nss/lib/certdb/certxutl.c @@ -0,0 +1,482 @@ +/* + * 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. + */ + +/* + * Certificate Extensions handling code + * + */ + +#include "cert.h" +#include "secitem.h" +#include "secoid.h" +#include "secder.h" +#include "secasn1.h" +#include "certxutl.h" +#include "secerr.h" + +#ifdef OLD +#include "ocspti.h" /* XXX a better extensions interface would not + * require knowledge of data structures of callers */ +#endif + +static CERTCertExtension * +GetExtension (CERTCertExtension **extensions, SECItem *oid) +{ + CERTCertExtension **exts; + CERTCertExtension *ext = NULL; + SECComparison comp; + + exts = extensions; + + if (exts) { + while ( *exts ) { + ext = *exts; + comp = SECITEM_CompareItem(oid, &ext->id); + if ( comp == SECEqual ) + break; + + exts++; + } + return (*exts ? ext : NULL); + } + return (NULL); +} + +SECStatus +cert_FindExtensionByOID (CERTCertExtension **extensions, SECItem *oid, SECItem *value) +{ + CERTCertExtension *ext; + SECStatus rv = SECSuccess; + + ext = GetExtension (extensions, oid); + if (ext == NULL) { + PORT_SetError (SEC_ERROR_EXTENSION_NOT_FOUND); + return (SECFailure); + } + if (value) + rv = SECITEM_CopyItem(NULL, value, &ext->value); + return (rv); +} + + +SECStatus +CERT_GetExtenCriticality (CERTCertExtension **extensions, int tag, PRBool *isCritical) +{ + CERTCertExtension *ext; + SECOidData *oid; + + if (!isCritical) + return (SECSuccess); + + /* find the extension in the extensions list */ + oid = SECOID_FindOIDByTag((SECOidTag)tag); + if ( !oid ) { + return(SECFailure); + } + ext = GetExtension (extensions, &oid->oid); + if (ext == NULL) { + PORT_SetError (SEC_ERROR_EXTENSION_NOT_FOUND); + return (SECFailure); + } + + /* If the criticality is omitted, then it is false by default. + ex->critical.data is NULL */ + if (ext->critical.data == NULL) + *isCritical = PR_FALSE; + else + *isCritical = (ext->critical.data[0] == 0xff) ? PR_TRUE : PR_FALSE; + return (SECSuccess); +} + +SECStatus +cert_FindExtension(CERTCertExtension **extensions, int tag, SECItem *value) +{ + SECOidData *oid; + + oid = SECOID_FindOIDByTag((SECOidTag)tag); + if ( !oid ) { + return(SECFailure); + } + + return(cert_FindExtensionByOID(extensions, &oid->oid, value)); +} + + +typedef struct _extNode { + struct _extNode *next; + CERTCertExtension *ext; +} extNode; + +typedef struct { + void (*setExts)(void *object, CERTCertExtension **exts); + void *object; + PRArenaPool *ownerArena; + PRArenaPool *arena; + extNode *head; + int count; +}extRec; + +/* + * cert_StartExtensions + * + * NOTE: This interface changed significantly to remove knowledge + * about callers data structures (owner objects) + */ +void * +cert_StartExtensions(void *owner, PRArenaPool *ownerArena, + void (*setExts)(void *object, CERTCertExtension **exts)) +{ + PRArenaPool *arena; + extRec *handle; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( !arena ) { + return(0); + } + + handle = (extRec *)PORT_ArenaAlloc(arena, sizeof(extRec)); + if ( !handle ) { + PORT_FreeArena(arena, PR_FALSE); + return(0); + } + + handle->object = owner; + handle->ownerArena = ownerArena; + handle->setExts = setExts; + + handle->arena = arena; + handle->head = 0; + handle->count = 0; + + return(handle); +} + +static unsigned char hextrue = 0xff; + +/* + * Note - assumes that data pointed to by oid->data will not move + */ +SECStatus +CERT_AddExtensionByOID (void *exthandle, SECItem *oid, SECItem *value, + PRBool critical, PRBool copyData) +{ + CERTCertExtension *ext; + SECStatus rv; + extNode *node; + extRec *handle; + + handle = (extRec *)exthandle; + + /* allocate space for extension and list node */ + ext = (CERTCertExtension*)PORT_ArenaZAlloc(handle->ownerArena, + sizeof(CERTCertExtension)); + if ( !ext ) { + return(SECFailure); + } + + node = (extNode*)PORT_ArenaAlloc(handle->arena, sizeof(extNode)); + if ( !node ) { + return(SECFailure); + } + + /* add to list */ + node->next = handle->head; + handle->head = node; + + /* point to ext struct */ + node->ext = ext; + + /* the object ID of the extension */ + ext->id = *oid; + + /* set critical field */ + if ( critical ) { + ext->critical.data = (unsigned char*)&hextrue; + ext->critical.len = 1; + } + + /* set the value */ + if ( copyData ) { + rv = SECITEM_CopyItem(handle->ownerArena, &ext->value, value); + if ( rv ) { + return(SECFailure); + } + } else { + ext->value = *value; + } + + handle->count++; + + return(SECSuccess); + +} + +SECStatus +CERT_AddExtension(void *exthandle, int idtag, SECItem *value, + PRBool critical, PRBool copyData) +{ + SECOidData *oid; + + oid = SECOID_FindOIDByTag((SECOidTag)idtag); + if ( !oid ) { + return(SECFailure); + } + + return(CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical, copyData)); +} + +SECStatus +CERT_EncodeAndAddExtension(void *exthandle, int idtag, void *value, + PRBool critical, const SEC_ASN1Template *atemplate) +{ + extRec *handle; + SECItem *encitem; + + handle = (extRec *)exthandle; + + encitem = SEC_ASN1EncodeItem(handle->ownerArena, NULL, value, atemplate); + if ( encitem == NULL ) { + return(SECFailure); + } + + return CERT_AddExtension(exthandle, idtag, encitem, critical, PR_FALSE); +} + +void +PrepareBitStringForEncoding (SECItem *bitsmap, SECItem *value) +{ + unsigned char onebyte; + unsigned int i, len = 0; + + /* to prevent warning on some platform at compile time */ + onebyte = '\0'; + /* Get the position of the right-most turn-on bit */ + for (i = 0; i < (value->len ) * 8; ++i) { + if (i % 8 == 0) + onebyte = value->data[i/8]; + if (onebyte & 0x80) + len = i; + onebyte <<= 1; + + } + bitsmap->data = value->data; + /* Add one here since we work with base 1 */ + bitsmap->len = len + 1; +} + +SECStatus +CERT_EncodeAndAddBitStrExtension (void *exthandle, int idtag, + SECItem *value, PRBool critical) +{ + SECItem bitsmap; + + PrepareBitStringForEncoding (&bitsmap, value); + return (CERT_EncodeAndAddExtension + (exthandle, idtag, &bitsmap, critical, SEC_BitStringTemplate)); +} + +SECStatus +CERT_FinishExtensions(void *exthandle) +{ + extRec *handle; + extNode *node; + CERTCertExtension **exts; + SECStatus rv = SECFailure; + + handle = (extRec *)exthandle; + + /* allocate space for extensions array */ + exts = PORT_ArenaNewArray(handle->ownerArena, CERTCertExtension *, + handle->count + 1); + if (exts == NULL) { + goto loser; + } + + /* put extensions in owner object and update its version number */ + +#ifdef OLD + switch (handle->type) { + case CertificateExtensions: + handle->owner.cert->extensions = exts; + DER_SetUInteger (ownerArena, &(handle->owner.cert->version), + SEC_CERTIFICATE_VERSION_3); + break; + case CrlExtensions: + handle->owner.crl->extensions = exts; + DER_SetUInteger (ownerArena, &(handle->owner.crl->version), + SEC_CRL_VERSION_2); + break; + case OCSPRequestExtensions: + handle->owner.request->tbsRequest->requestExtensions = exts; + break; + case OCSPSingleRequestExtensions: + handle->owner.singleRequest->singleRequestExtensions = exts; + break; + case OCSPResponseSingleExtensions: + handle->owner.singleResponse->singleExtensions = exts; + break; + } +#endif + + handle->setExts(handle->object, exts); + + /* update the version number */ + + /* copy each extension pointer */ + node = handle->head; + while ( node ) { + *exts = node->ext; + + node = node->next; + exts++; + } + + /* terminate the array of extensions */ + *exts = 0; + + rv = SECSuccess; + +loser: + /* free working arena */ + PORT_FreeArena(handle->arena, PR_FALSE); + return rv; +} + +/* + * get the value of the Netscape Certificate Type Extension + */ +SECStatus +CERT_FindBitStringExtension (CERTCertExtension **extensions, int tag, + SECItem *retItem) +{ + SECItem wrapperItem, tmpItem = {siBuffer,0}; + SECStatus rv; + PRArenaPool *arena = NULL; + + wrapperItem.data = NULL; + tmpItem.data = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if ( ! arena ) { + return(SECFailure); + } + + rv = cert_FindExtension(extensions, tag, &wrapperItem); + if ( rv != SECSuccess ) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem(arena, &tmpItem, SEC_BitStringTemplate, + &wrapperItem); + + if ( rv != SECSuccess ) { + goto loser; + } + + retItem->data = (unsigned char *)PORT_Alloc( ( tmpItem.len + 7 ) >> 3 ); + if ( retItem->data == NULL ) { + goto loser; + } + + PORT_Memcpy(retItem->data, tmpItem.data, ( tmpItem.len + 7 ) >> 3); + retItem->len = tmpItem.len; + + rv = SECSuccess; + goto done; + +loser: + rv = SECFailure; + +done: + if ( arena ) { + PORT_FreeArena(arena, PR_FALSE); + } + + if ( wrapperItem.data ) { + PORT_Free(wrapperItem.data); + } + + return(rv); +} + +PRBool +cert_HasCriticalExtension (CERTCertExtension **extensions) +{ + CERTCertExtension **exts; + CERTCertExtension *ext = NULL; + PRBool hasCriticalExten = PR_FALSE; + + exts = extensions; + + if (exts) { + while ( *exts ) { + ext = *exts; + /* If the criticality is omitted, it's non-critical */ + if (ext->critical.data && ext->critical.data[0] == 0xff) { + hasCriticalExten = PR_TRUE; + break; + } + exts++; + } + } + return (hasCriticalExten); +} + +PRBool +cert_HasUnknownCriticalExten (CERTCertExtension **extensions) +{ + CERTCertExtension **exts; + CERTCertExtension *ext = NULL; + PRBool hasUnknownCriticalExten = PR_FALSE; + + exts = extensions; + + if (exts) { + while ( *exts ) { + ext = *exts; + /* If the criticality is omitted, it's non-critical. + If an extension is critical, make sure that we know + how to process the extension. + */ + if (ext->critical.data && ext->critical.data[0] == 0xff) { + if (SECOID_KnownCertExtenOID (&ext->id) == PR_FALSE) { + hasUnknownCriticalExten = PR_TRUE; + break; + } + } + exts++; + } + } + return (hasUnknownCriticalExten); +} diff --git a/security/nss/lib/certdb/certxutl.h b/security/nss/lib/certdb/certxutl.h new file mode 100644 index 000000000..381226a43 --- /dev/null +++ b/security/nss/lib/certdb/certxutl.h @@ -0,0 +1,79 @@ +/* + * 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. + */ + +/* + * x.509 v3 certificate extension helper routines + * + */ + + +#ifndef _CERTXUTL_H_ +#define _CERTXUTL_H_ + +#include "nspr.h" + +#ifdef OLD +typedef enum { + CertificateExtensions, + CrlExtensions, + OCSPRequestExtensions, + OCSPSingleRequestExtensions, + OCSPResponseSingleExtensions +} ExtensionsType; +#endif + +extern PRBool +cert_HasCriticalExtension (CERTCertExtension **extensions); + +extern SECStatus +CERT_FindBitStringExtension (CERTCertExtension **extensions, + int tag, SECItem *retItem); +extern void * +cert_StartExtensions (void *owner, PLArenaPool *arena, + void (*setExts)(void *object, CERTCertExtension **exts)); + +extern SECStatus +cert_FindExtension (CERTCertExtension **extensions, int tag, SECItem *value); + +extern SECStatus +cert_FindExtensionByOID (CERTCertExtension **extensions, + SECItem *oid, SECItem *value); + +extern SECStatus +cert_GetExtenCriticality (CERTCertExtension **extensions, + int tag, PRBool *isCritical); + +extern PRBool +cert_HasUnknownCriticalExten (CERTCertExtension **extensions); + +#endif diff --git a/security/nss/lib/certdb/config.mk b/security/nss/lib/certdb/config.mk new file mode 100644 index 000000000..0a00dc61e --- /dev/null +++ b/security/nss/lib/certdb/config.mk @@ -0,0 +1,43 @@ +# +# 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. +# + +# +# Override TARGETS variable so that only static libraries +# are specifed as dependencies within rules.mk. +# + +TARGETS = $(LIBRARY) +SHARED_LIBRARY = +IMPORT_LIBRARY = +PROGRAM = + diff --git a/security/nss/lib/certdb/crl.c b/security/nss/lib/certdb/crl.c new file mode 100644 index 000000000..72e0aa94e --- /dev/null +++ b/security/nss/lib/certdb/crl.c @@ -0,0 +1,1950 @@ +/* + * 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. + */ + +/* + * Moved from secpkcs7.c + * + * $Id$ + */ + +#include "cert.h" +#include "certi.h" +#include "secder.h" +#include "secasn1.h" +#include "secoid.h" +#include "certdb.h" +#include "certxutl.h" +#include "prtime.h" +#include "secerr.h" +#include "pk11func.h" +#include "dev.h" +#include "dev3hack.h" +#include "nssbase.h" +#ifdef USE_RWLOCK +#include "nssrwlk.h" +#endif + +const SEC_ASN1Template SEC_CERTExtensionTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCertExtension) }, + { SEC_ASN1_OBJECT_ID, + offsetof(CERTCertExtension,id) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */ + offsetof(CERTCertExtension,critical), }, + { SEC_ASN1_OCTET_STRING, + offsetof(CERTCertExtension,value) }, + { 0, } +}; + +static const SEC_ASN1Template SEC_CERTExtensionsTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, 0, SEC_CERTExtensionTemplate} +}; + +/* + * XXX Also, these templates, especially the Krl/FORTEZZA ones, need to + * be tested; Lisa did the obvious translation but they still should be + * verified. + */ + +const SEC_ASN1Template CERT_IssuerAndSNTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTIssuerAndSN) }, + { SEC_ASN1_SAVE, + offsetof(CERTIssuerAndSN,derIssuer) }, + { SEC_ASN1_INLINE, + offsetof(CERTIssuerAndSN,issuer), + CERT_NameTemplate }, + { SEC_ASN1_INTEGER, + offsetof(CERTIssuerAndSN,serialNumber) }, + { 0 } +}; + +static const SEC_ASN1Template cert_KrlEntryTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCrlEntry) }, + { SEC_ASN1_OCTET_STRING, + offsetof(CERTCrlEntry,serialNumber) }, + { SEC_ASN1_UTC_TIME, + offsetof(CERTCrlEntry,revocationDate) }, + { 0 } +}; + +static const SEC_ASN1Template cert_KrlTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCrl) }, + { SEC_ASN1_INLINE, + offsetof(CERTCrl,signatureAlg), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_SAVE, + offsetof(CERTCrl,derName) }, + { SEC_ASN1_INLINE, + offsetof(CERTCrl,name), + CERT_NameTemplate }, + { SEC_ASN1_UTC_TIME, + offsetof(CERTCrl,lastUpdate) }, + { SEC_ASN1_UTC_TIME, + offsetof(CERTCrl,nextUpdate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF, + offsetof(CERTCrl,entries), + cert_KrlEntryTemplate }, + { 0 } +}; + +static const SEC_ASN1Template cert_SignedKrlTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTSignedCrl) }, + { SEC_ASN1_SAVE, + offsetof(CERTSignedCrl,signatureWrap.data) }, + { SEC_ASN1_INLINE, + offsetof(CERTSignedCrl,crl), + cert_KrlTemplate }, + { SEC_ASN1_INLINE, + offsetof(CERTSignedCrl,signatureWrap.signatureAlgorithm), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_BIT_STRING, + offsetof(CERTSignedCrl,signatureWrap.signature) }, + { 0 } +}; + +static const SEC_ASN1Template cert_CrlKeyTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCrlKey) }, + { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(CERTCrlKey,dummy) }, + { SEC_ASN1_SKIP }, + { SEC_ASN1_ANY, offsetof(CERTCrlKey,derName) }, + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +static const SEC_ASN1Template cert_CrlEntryTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCrlEntry) }, + { SEC_ASN1_INTEGER, + offsetof(CERTCrlEntry,serialNumber) }, + { SEC_ASN1_UTC_TIME, + offsetof(CERTCrlEntry,revocationDate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF, + offsetof(CERTCrlEntry, extensions), + SEC_CERTExtensionTemplate}, + { 0 } +}; + +const SEC_ASN1Template CERT_CrlTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCrl) }, + { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof (CERTCrl, version) }, + { SEC_ASN1_INLINE, + offsetof(CERTCrl,signatureAlg), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_SAVE, + offsetof(CERTCrl,derName) }, + { SEC_ASN1_INLINE, + offsetof(CERTCrl,name), + CERT_NameTemplate }, + { SEC_ASN1_UTC_TIME, + offsetof(CERTCrl,lastUpdate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_UTC_TIME, + offsetof(CERTCrl,nextUpdate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF, + offsetof(CERTCrl,entries), + cert_CrlEntryTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_EXPLICIT | 0, + offsetof(CERTCrl,extensions), + SEC_CERTExtensionsTemplate}, + { 0 } +}; + +const SEC_ASN1Template CERT_CrlTemplateNoEntries[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCrl) }, + { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof (CERTCrl, version) }, + { SEC_ASN1_INLINE, + offsetof(CERTCrl,signatureAlg), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_SAVE, + offsetof(CERTCrl,derName) }, + { SEC_ASN1_INLINE, + offsetof(CERTCrl,name), + CERT_NameTemplate }, + { SEC_ASN1_UTC_TIME, + offsetof(CERTCrl,lastUpdate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_UTC_TIME, + offsetof(CERTCrl,nextUpdate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF | + SEC_ASN1_SKIP }, /* skip entries */ + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_EXPLICIT | 0, + offsetof(CERTCrl,extensions), + SEC_CERTExtensionsTemplate }, + { 0 } +}; + +const SEC_ASN1Template CERT_CrlTemplateEntriesOnly[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCrl) }, + { SEC_ASN1_SKIP | SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL }, + { SEC_ASN1_SKIP }, + { SEC_ASN1_SKIP }, + { SEC_ASN1_SKIP | SEC_ASN1_UTC_TIME }, + { SEC_ASN1_SKIP | SEC_ASN1_OPTIONAL | SEC_ASN1_UTC_TIME }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF, + offsetof(CERTCrl,entries), + cert_CrlEntryTemplate }, /* decode entries */ + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +static const SEC_ASN1Template cert_SignedCrlTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTSignedCrl) }, + { SEC_ASN1_SAVE, + offsetof(CERTSignedCrl,signatureWrap.data) }, + { SEC_ASN1_INLINE, + offsetof(CERTSignedCrl,crl), + CERT_CrlTemplate }, + { SEC_ASN1_INLINE, + offsetof(CERTSignedCrl,signatureWrap.signatureAlgorithm), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_BIT_STRING, + offsetof(CERTSignedCrl,signatureWrap.signature) }, + { 0 } +}; + +static const SEC_ASN1Template cert_SignedCrlTemplateNoEntries[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTSignedCrl) }, + { SEC_ASN1_SAVE, + offsetof(CERTSignedCrl,signatureWrap.data) }, + { SEC_ASN1_INLINE, + offsetof(CERTSignedCrl,crl), + CERT_CrlTemplateNoEntries }, + { SEC_ASN1_INLINE, + offsetof(CERTSignedCrl,signatureWrap.signatureAlgorithm), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_BIT_STRING, + offsetof(CERTSignedCrl,signatureWrap.signature) }, + { 0 } +}; + +const SEC_ASN1Template CERT_SetOfSignedCrlTemplate[] = { + { SEC_ASN1_SET_OF, 0, cert_SignedCrlTemplate }, +}; + +/* Check the version of the CRL. If there is a critical extension in the crl + or crl entry, then the version must be v2. Otherwise, it should be v1. If + the crl contains critical extension(s), then we must recognized the extension's + OID. + */ +SECStatus cert_check_crl_version (CERTCrl *crl) +{ + CERTCrlEntry **entries; + CERTCrlEntry *entry; + PRBool hasCriticalExten = PR_FALSE; + SECStatus rv = SECSuccess; + int version; + + /* CRL version is defaulted to v1 */ + version = SEC_CRL_VERSION_1; + if (crl->version.data != 0) + version = (int)DER_GetUInteger (&crl->version); + + if (version > SEC_CRL_VERSION_2) { + PORT_SetError (SEC_ERROR_BAD_DER); + return (SECFailure); + } + + /* Check the crl extensions for a critial extension. If one is found, + and the version is not v2, then we are done. + */ + if (crl->extensions) { + hasCriticalExten = cert_HasCriticalExtension (crl->extensions); + if (hasCriticalExten) { + if (version != SEC_CRL_VERSION_2) + return (SECFailure); + /* make sure that there is no unknown critical extension */ + if (cert_HasUnknownCriticalExten (crl->extensions) == PR_TRUE) { + PORT_SetError (SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); + return (SECFailure); + } + } + } + + + if (crl->entries == NULL) { + return (SECSuccess); + } + /* Look in the crl entry extensions. If there is a critical extension, + then the crl version must be v2; otherwise, it should be v1. + */ + entries = crl->entries; + while (*entries) { + entry = *entries; + if (entry->extensions) { + /* If there is a critical extension in the entries, then the + CRL must be of version 2. If we already saw a critical extension, + there is no need to check the version again. + */ + if (hasCriticalExten == PR_FALSE) { + hasCriticalExten = cert_HasCriticalExtension (entry->extensions); + if (hasCriticalExten && version != SEC_CRL_VERSION_2) { + rv = SECFailure; + break; + } + } + + /* For each entry, make sure that it does not contain an unknown + critical extension. If it does, we must reject the CRL since + we don't know how to process the extension. + */ + if (cert_HasUnknownCriticalExten (entry->extensions) == PR_TRUE) { + PORT_SetError (SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); + rv = SECFailure; + break; + } + } + ++entries; + } + if (rv == SECFailure) + return (rv); + + return (SECSuccess); +} + +/* + * Generate a database key, based on the issuer name from a + * DER crl. + */ +SECStatus +CERT_KeyFromDERCrl(PRArenaPool *arena, SECItem *derCrl, SECItem *key) +{ + SECStatus rv; + CERTSignedData sd; + CERTCrlKey crlkey; + + PORT_Memset (&sd, 0, sizeof (sd)); + rv = SEC_ASN1DecodeItem (arena, &sd, CERT_SignedDataTemplate, derCrl); + if (rv != SECSuccess) { + return rv; + } + + PORT_Memset (&crlkey, 0, sizeof (crlkey)); + rv = SEC_ASN1DecodeItem(arena, &crlkey, cert_CrlKeyTemplate, &sd.data); + if (rv != SECSuccess) { + return rv; + } + + key->len = crlkey.derName.len; + key->data = crlkey.derName.data; + + return(SECSuccess); +} + +#define GetOpaqueCRLFields(x) ((OpaqueCRLFields*)x->opaque) + +/* +PRBool CERT_CRLIsInvalid(CERTSignedCrl* crl) +{ + OpaqueCRLFields* extended = NULL; + + if (crl && (extended = (OpaqueCRLFields*) crl->opaque)) { + return extended->bad; + } + return PR_TRUE; +} +*/ + +SECStatus CERT_CompleteCRLDecodeEntries(CERTSignedCrl* crl) +{ + SECStatus rv = SECSuccess; + SECItem* crldata = NULL; + OpaqueCRLFields* extended = NULL; + + if ( (!crl) || + (!(extended = (OpaqueCRLFields*) crl->opaque)) ) { + rv = SECFailure; + } else { + if (PR_FALSE == extended->partial) { + /* the CRL has already been fully decoded */ + return SECSuccess; + } + if (PR_TRUE == extended->badEntries) { + /* the entries decoding already failed */ + return SECFailure; + } + crldata = &crl->signatureWrap.data; + if (!crldata) { + rv = SECFailure; + } + } + + if (SECSuccess == rv) { + rv = SEC_QuickDERDecodeItem(crl->arena, + &crl->crl, + CERT_CrlTemplateEntriesOnly, + crldata); + if (SECSuccess == rv) { + extended->partial = PR_FALSE; /* successful decode, avoid + decoding again */ + } else { + extended->bad = PR_TRUE; + extended->badEntries = PR_TRUE; + /* cache the decoding failure. If it fails the first time, + it will fail again, which will grow the arena and leak + memory, so we want to avoid it */ + } + } + return rv; +} + +/* + * take a DER CRL or KRL and decode it into a CRL structure + * allow reusing the input DER without making a copy + */ +CERTSignedCrl * +CERT_DecodeDERCrlWithFlags(PRArenaPool *narena, SECItem *derSignedCrl, + int type, PRInt32 options) +{ + PRArenaPool *arena; + CERTSignedCrl *crl; + SECStatus rv; + OpaqueCRLFields* extended = NULL; + const SEC_ASN1Template* crlTemplate = cert_SignedCrlTemplate; + + /* make a new arena */ + if (narena == NULL) { + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( !arena ) { + return NULL; + } + } else { + arena = narena; + } + + /* allocate the CRL structure */ + crl = (CERTSignedCrl *)PORT_ArenaZAlloc(arena, sizeof(CERTSignedCrl)); + if ( !crl ) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + crl->arena = arena; + + /* allocate opaque fields */ + crl->opaque = (void*)PORT_ArenaZAlloc(arena, sizeof(OpaqueCRLFields)); + if ( !crl->opaque ) { + goto loser; + } + extended = (OpaqueCRLFields*) crl->opaque; + + if (options & CRL_DECODE_DONT_COPY_DER) { + crl->derCrl = derSignedCrl; /* DER is not copied . The application + must keep derSignedCrl until it + destroys the CRL */ + } else { + crl->derCrl = (SECItem *)PORT_ArenaZAlloc(arena,sizeof(SECItem)); + if (crl->derCrl == NULL) { + goto loser; + } + rv = SECITEM_CopyItem(arena, crl->derCrl, derSignedCrl); + if (rv != SECSuccess) { + goto loser; + } + } + + /* Save the arena in the inner crl for CRL extensions support */ + crl->crl.arena = arena; + if (options & CRL_DECODE_SKIP_ENTRIES) { + crlTemplate = cert_SignedCrlTemplateNoEntries; + extended->partial = PR_TRUE; + } + + /* decode the CRL info */ + switch (type) { + case SEC_CRL_TYPE: + rv = SEC_QuickDERDecodeItem(arena, crl, crlTemplate, crl->derCrl); + if (rv != SECSuccess) { + extended->badDER = PR_TRUE; + break; + } + /* check for critical extentions */ + rv = cert_check_crl_version (&crl->crl); + if (rv != SECSuccess) { + extended->badExtensions = PR_TRUE; + } + break; + + case SEC_KRL_TYPE: + rv = SEC_QuickDERDecodeItem + (arena, crl, cert_SignedKrlTemplate, derSignedCrl); + break; + default: + rv = SECFailure; + break; + } + + if (rv != SECSuccess) { + goto loser; + } + + crl->referenceCount = 1; + + return(crl); + +loser: + if (options & CRL_DECODE_KEEP_BAD_CRL) { + extended->bad = PR_TRUE; + crl->referenceCount = 1; + return(crl); + } + + if ((narena == NULL) && arena ) { + PORT_FreeArena(arena, PR_FALSE); + } + + return(0); +} + +/* + * take a DER CRL or KRL and decode it into a CRL structure + */ +CERTSignedCrl * +CERT_DecodeDERCrl(PRArenaPool *narena, SECItem *derSignedCrl, int type) +{ + return CERT_DecodeDERCrlWithFlags(narena, derSignedCrl, type, CRL_DECODE_DEFAULT_OPTIONS); +} + +/* + * Lookup a CRL in the databases. We mirror the same fast caching data base + * caching stuff used by certificates....? + * return values : + * + * SECSuccess means we got a valid DER CRL (passed in "decoded"), or no CRL at all + * + * SECFailure means we got a fatal error - most likely, we found a CRL, + * and it failed decoding, or there was an out of memory error. Do NOT ignore + * it and specifically do NOT treat it the same as having no CRL, as this + * can compromise security !!! Ideally, you should treat this case as if you + * received a "catch-all" CRL where all certs you were looking up are + * considered to be revoked + */ +static SECStatus +SEC_FindCrlByKeyOnSlot(PK11SlotInfo *slot, SECItem *crlKey, int type, + CERTSignedCrl** decoded, PRInt32 decodeoptions) +{ + SECStatus rv = SECSuccess; + CERTSignedCrl *crl = NULL; + SECItem *derCrl = NULL; + CK_OBJECT_HANDLE crlHandle = 0; + char *url = NULL; + int nsserror; + + PORT_Assert(decoded); + if (!decoded) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (slot) { + PK11_ReferenceSlot(slot); + } + + /* XXX it would be really useful to be able to fetch the CRL directly into an + arena. This would avoid a copy later on in the decode step */ + PORT_SetError(0); + derCrl = PK11_FindCrlByName(&slot, &crlHandle, crlKey, type, &url); + if (derCrl == NULL) { + /* if we had a problem other than the CRL just didn't exist, return + * a failure to the upper level */ + nsserror = PORT_GetError(); + if ((nsserror != 0) && (nsserror != SEC_ERROR_CRL_NOT_FOUND)) { + rv = SECFailure; + } + goto loser; + } + PORT_Assert(crlHandle != CK_INVALID_HANDLE); + + crl = CERT_DecodeDERCrlWithFlags(NULL, derCrl, type, decodeoptions); + if (crl) { + crl->slot = slot; + slot = NULL; /* adopt it */ + crl->pkcs11ID = crlHandle; + if (url) { + crl->url = PORT_ArenaStrdup(crl->arena,url); + } + } else { + rv = SECFailure; + } + + if (url) { + PORT_Free(url); + } + +loser: + if (slot) { + PK11_FreeSlot(slot); + } + + if (derCrl) { + /* destroy the DER, unless a decoded CRL was returned with DER + allocated on the heap. This is solely for cache purposes */ + if (crl && (decodeoptions & CRL_DECODE_DONT_COPY_DER)) { + /* mark the DER as having come from the heap instead of the + arena, so it can be destroyed */ + GetOpaqueCRLFields(crl)->heapDER = PR_TRUE; + } else { + SECITEM_FreeItem(derCrl, PR_TRUE); + } + } + + *decoded = crl; + + return rv; +} + +SECStatus SEC_DestroyCrl(CERTSignedCrl *crl); + +CERTSignedCrl * +crl_storeCRL (PK11SlotInfo *slot,char *url, + CERTSignedCrl *newCrl, SECItem *derCrl, int type) +{ + CERTSignedCrl *oldCrl = NULL, *crl = NULL; + PRBool deleteOldCrl = PR_FALSE; + CK_OBJECT_HANDLE crlHandle; + + PORT_Assert(newCrl); + PORT_Assert(derCrl); + + /* we can't use the cache here because we must look in the same + token */ + SEC_FindCrlByKeyOnSlot(slot, &newCrl->crl.derName, type, + &oldCrl, CRL_DECODE_SKIP_ENTRIES); + + /* if there is an old crl on the token, make sure the one we are + installing is newer. If not, exit out, otherwise delete the + old crl. + */ + if (oldCrl != NULL) { + /* if it's already there, quietly continue */ + if (SECITEM_CompareItem(newCrl->derCrl, oldCrl->derCrl) + == SECEqual) { + crl = newCrl; + crl->slot = PK11_ReferenceSlot(slot); + crl->pkcs11ID = oldCrl->pkcs11ID; + goto done; + } + if (!SEC_CrlIsNewer(&newCrl->crl,&oldCrl->crl)) { + + if (type == SEC_CRL_TYPE) { + PORT_SetError(SEC_ERROR_OLD_CRL); + } else { + PORT_SetError(SEC_ERROR_OLD_KRL); + } + + goto done; + } + + if ((SECITEM_CompareItem(&newCrl->crl.derName, + &oldCrl->crl.derName) != SECEqual) && + (type == SEC_KRL_TYPE) ) { + + PORT_SetError(SEC_ERROR_CKL_CONFLICT); + goto done; + } + + /* if we have a url in the database, use that one */ + if (oldCrl->url) { + url = oldCrl->url; + } + + /* really destroy this crl */ + /* first drum it out of the permanment Data base */ + deleteOldCrl = PR_TRUE; + } + + /* invalidate CRL cache for this issuer */ + CERT_CRLCacheRefreshIssuer(NULL, &newCrl->crl.derName); + /* Write the new entry into the data base */ + crlHandle = PK11_PutCrl(slot, derCrl, &newCrl->crl.derName, url, type); + if (crlHandle != CK_INVALID_HANDLE) { + crl = newCrl; + crl->slot = PK11_ReferenceSlot(slot); + crl->pkcs11ID = crlHandle; + if (url) { + crl->url = PORT_ArenaStrdup(crl->arena,url); + } + } + +done: + if (oldCrl) { + if (deleteOldCrl && crlHandle != CK_INVALID_HANDLE) { + SEC_DeletePermCRL(oldCrl); + } + SEC_DestroyCrl(oldCrl); + } + + return crl; +} + +/* + * + * create a new CRL from DER material. + * + * The signature on this CRL must be checked before you + * load it. ??? + */ +CERTSignedCrl * +SEC_NewCrl(CERTCertDBHandle *handle, char *url, SECItem *derCrl, int type) +{ + CERTSignedCrl* retCrl = NULL; + PK11SlotInfo* slot = PK11_GetInternalKeySlot(); + retCrl = PK11_ImportCRL(slot, derCrl, url, type, NULL, + CRL_IMPORT_BYPASS_CHECKS, NULL, CRL_DECODE_DEFAULT_OPTIONS); + PK11_FreeSlot(slot); + + return retCrl; +} + +CERTSignedCrl * +SEC_FindCrlByDERCert(CERTCertDBHandle *handle, SECItem *derCrl, int type) +{ + PRArenaPool *arena; + SECItem crlKey; + SECStatus rv; + CERTSignedCrl *crl = NULL; + + /* create a scratch arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( arena == NULL ) { + return(NULL); + } + + /* extract the database key from the cert */ + rv = CERT_KeyFromDERCrl(arena, derCrl, &crlKey); + if ( rv != SECSuccess ) { + goto loser; + } + + /* find the crl */ + crl = SEC_FindCrlByName(handle, &crlKey, type); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return(crl); +} + +CERTSignedCrl* SEC_DupCrl(CERTSignedCrl* acrl) +{ + if (acrl) + { + PR_AtomicIncrement(&acrl->referenceCount); + return acrl; + } + return NULL; +} + +SECStatus +SEC_DestroyCrl(CERTSignedCrl *crl) +{ + if (crl) { + if (PR_AtomicDecrement(&crl->referenceCount) < 1) { + if (crl->slot) { + PK11_FreeSlot(crl->slot); + } + if (PR_TRUE == GetOpaqueCRLFields(crl)->heapDER) { + SECITEM_FreeItem(crl->derCrl, PR_TRUE); + } + PORT_FreeArena(crl->arena, PR_FALSE); + } + } + return SECSuccess; +} + +SECStatus +SEC_LookupCrls(CERTCertDBHandle *handle, CERTCrlHeadNode **nodes, int type) +{ + CERTCrlHeadNode *head; + PRArenaPool *arena = NULL; + SECStatus rv; + + *nodes = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( arena == NULL ) { + return SECFailure; + } + + /* build a head structure */ + head = (CERTCrlHeadNode *)PORT_ArenaAlloc(arena, sizeof(CERTCrlHeadNode)); + head->arena = arena; + head->first = NULL; + head->last = NULL; + head->dbhandle = handle; + + /* Look up the proper crl types */ + *nodes = head; + + rv = PK11_LookupCrls(head, type, NULL); + + if (rv != SECSuccess) { + if ( arena ) { + PORT_FreeArena(arena, PR_FALSE); + *nodes = NULL; + } + } + + return rv; +} + +/* These functions simply return the address of the above-declared templates. +** This is necessary for Windows DLLs. Sigh. +*/ +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_IssuerAndSNTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CrlTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SetOfSignedCrlTemplate) + +/* +** Pre-allocator hash allocator ops. +*/ +static void * PR_CALLBACK +PreAllocTable(void *pool, PRSize size) +{ + PreAllocator* alloc = (PreAllocator*)pool; + PR_ASSERT(alloc); + if (!alloc) + { + /* no allocator, or buffer full */ + return NULL; + } + if (size > (alloc->len - alloc->used)) + { + alloc->extra += size; + return PORT_ArenaAlloc(alloc->arena, size); + } + alloc->used += size; + return (char*) alloc->data + alloc->used - size; +} + +static void PR_CALLBACK +PreFreeTable(void *pool, void *item) +{ +} + +static PLHashEntry * PR_CALLBACK +PreAllocEntry(void *pool, const void *key) +{ + return PreAllocTable(pool, sizeof(PLHashEntry)); +} + +static void PR_CALLBACK +PreFreeEntry(void *pool, PLHashEntry *he, PRUintn flag) +{ +} + +static PLHashAllocOps preAllocOps = { + PreAllocTable, PreFreeTable, + PreAllocEntry, PreFreeEntry +}; + +void PreAllocator_Destroy(PreAllocator* PreAllocator) +{ + if (!PreAllocator) + { + return; + } + if (PreAllocator->arena) + { + PORT_FreeArena(PreAllocator->arena, PR_TRUE); + } + if (PreAllocator->data) + { + PORT_Free(PreAllocator->data); + } + PORT_Free(PreAllocator); +} + +PreAllocator* PreAllocator_Create(PRSize size) +{ + PreAllocator prebuffer; + PreAllocator* prepointer = NULL; + memset(&prebuffer, 0, sizeof(PreAllocator)); + prebuffer.len = size; + prebuffer.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + PR_ASSERT(prebuffer.arena); + if (!prebuffer.arena) { + PreAllocator_Destroy(&prebuffer); + return NULL; + } + if (prebuffer.len) { + prebuffer.data = PR_Malloc(prebuffer.len); + if (!prebuffer.data) { + PreAllocator_Destroy(&prebuffer); + return NULL; + } + } else { + prebuffer.data = NULL; + } + prepointer = (PreAllocator*)PR_Malloc(sizeof(PreAllocator)); + if (!prepointer) { + PreAllocator_Destroy(&prebuffer); + return NULL; + } + *prepointer = prebuffer; + return prepointer; +} + +static CRLCache crlcache = { NULL, NULL }; + +static PRBool crlcache_initialized = PR_FALSE; + +/* this needs to be called at NSS initialization time */ + +SECStatus InitCRLCache(void) +{ + if (PR_FALSE == crlcache_initialized) + { + PR_ASSERT(NULL == crlcache.lock); + crlcache.lock = PR_NewLock(); + if (!crlcache.lock) + { + return SECFailure; + } + PR_ASSERT(NULL == crlcache.issuers); + crlcache.issuers = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare, + PL_CompareValues, NULL, NULL); + if (!crlcache.issuers) + { + PR_DestroyLock(crlcache.lock); + crlcache.lock = NULL; + return SECFailure; + } + crlcache_initialized = PR_TRUE; + return SECSuccess; + } + else + { + PR_ASSERT(crlcache.lock); + PR_ASSERT(crlcache.issuers); + if ( (NULL == crlcache.lock) || (NULL == crlcache.issuers) ) + { + return SECFailure; + } + else + { + return SECSuccess; + } + } +} + +SECStatus DPCache_Destroy(CRLDPCache* cache) +{ + PRUint32 i = 0; + PR_ASSERT(cache); + if (!cache) { + return SECFailure; + } + if (cache->lock) + { +#ifdef USE_RWLOCK + NSSRWLock_Destroy(cache->lock); +#else + PR_DestroyLock(cache->lock); +#endif + } + /* destroy all our CRL objects */ + for (i=0;i<cache->ncrls;i++) + { + SEC_DestroyCrl(cache->crls[i]); + } + /* free the array of CRLs */ + if (cache->crls) + { + PR_Free(cache->crls); + } + /* destroy the hash table */ + if (cache->entries) + { + PL_HashTableDestroy(cache->entries); + } + /* free the pre buffer */ + if (cache->prebuffer) + { + PreAllocator_Destroy(cache->prebuffer); + } + /* destroy the cert */ + if (cache->issuer) + { + CERT_DestroyCertificate(cache->issuer); + } + /* free the subject */ + if (cache->subject) + { + SECITEM_FreeItem(cache->subject, PR_TRUE); + } + /* free the distribution points */ + if (cache->distributionPoint) + { + SECITEM_FreeItem(cache->distributionPoint, PR_TRUE); + } + return SECSuccess; +} + +SECStatus IssuerCache_Destroy(CRLIssuerCache* cache) +{ + PORT_Assert(cache); + if (!cache) + { + return SECFailure; + } +#if 0 + /* XCRL */ + if (cache->lock) + { + NSSRWLock_Destroy(cache->lock); + } + if (cache->issuer) + { + CERT_DestroyCertificate(cache->issuer); + } +#endif + /* free the subject */ + if (cache->subject) + { + SECITEM_FreeItem(cache->subject, PR_TRUE); + } + DPCache_Destroy(&cache->dp); + PR_Free(cache); + return SECSuccess; +} + +PRIntn PR_CALLBACK FreeIssuer(PLHashEntry *he, PRIntn i, void *arg) +{ + CRLIssuerCache* issuer = NULL; + PR_ASSERT(he); + if (!he) { + return HT_ENUMERATE_NEXT; + } + issuer = (CRLIssuerCache*) he->value; + PR_ASSERT(issuer); + if (issuer) { + IssuerCache_Destroy(issuer); + } + return HT_ENUMERATE_NEXT; +} + +SECStatus ShutdownCRLCache(void) +{ + if (!crlcache.lock || !crlcache.issuers) + { + return SECFailure; + } + /* empty the cache */ + PL_HashTableEnumerateEntries(crlcache.issuers, &FreeIssuer, NULL); + PL_HashTableDestroy(crlcache.issuers); + crlcache.issuers = NULL; + PR_DestroyLock(crlcache.lock); + crlcache.lock = NULL; + crlcache_initialized = PR_FALSE; + return SECSuccess; +} + +SECStatus DPCache_AddCRL(CRLDPCache* cache, CERTSignedCrl* crl) +{ + CERTSignedCrl** newcrls = NULL; + PORT_Assert(cache); + PORT_Assert(crl); + if (!cache || !crl) { + return SECFailure; + } + + newcrls = (CERTSignedCrl**)PORT_Realloc(cache->crls, + (cache->ncrls+1)*sizeof(CERTSignedCrl*)); + if (!newcrls) { + return SECFailure; + } + cache->crls = newcrls; + cache->ncrls++; + cache->crls[cache->ncrls-1] = crl; + return SECSuccess; +} + +SECStatus DPCache_Cleanup(CRLDPCache* cache) +{ + /* remove deleted CRLs from memory */ + PRUint32 i = 0; + PORT_Assert(cache); + if (!cache) { + return SECFailure; + } + for (i=0;i<cache->ncrls;i++) { + CERTSignedCrl* acrl = cache->crls[i]; + if (acrl && (PR_TRUE == GetOpaqueCRLFields(acrl)->deleted)) { + cache->crls[i] = cache->crls[cache->ncrls-1]; + cache->crls[cache->ncrls-1] = NULL; + cache->ncrls--; + } + } + return SECSuccess; +} + +PRBool CRLStillExists(CERTSignedCrl* crl) +{ + NSSItem newsubject; + SECItem subject; + CK_ULONG crl_class; + PRStatus status; + PK11SlotInfo* slot = NULL; + nssCryptokiObject instance; + NSSArena* arena; + PRBool xstatus = PR_TRUE; + SECItem* oldSubject = NULL; + + PORT_Assert(crl); + if (!crl) { + return PR_FALSE; + } + slot = crl->slot; + PORT_Assert(slot); + if (!slot) { + return PR_FALSE; + } + oldSubject = &crl->crl.derName; + PR_ASSERT(oldSubject); + if (!oldSubject) { + return PR_FALSE; + } + + /* query subject and type attributes in order to determine if the + object has been deleted */ + + /* first, make an nssCryptokiObject */ + instance.handle = crl->pkcs11ID; + PORT_Assert(instance.handle); + if (!instance.handle) { + return PR_FALSE; + } + instance.token = PK11Slot_GetNSSToken(slot); + PORT_Assert(instance.token); + if (!instance.token) { + return PR_FALSE; + } + instance.isTokenObject = PR_TRUE; + instance.label = NULL; + + arena = NSSArena_Create(); + PORT_Assert(arena); + if (!arena) { + return PR_FALSE; + } + + status = nssCryptokiCRL_GetAttributes(&instance, + NULL, /* XXX sessionOpt */ + arena, + NULL, + &newsubject, /* subject */ + &crl_class, /* class */ + NULL, + NULL); + if (PR_SUCCESS == status) { + subject.data = newsubject.data; + subject.len = newsubject.size; + if (SECITEM_CompareItem(oldSubject, &subject) != SECEqual) { + xstatus = PR_FALSE; + } + if (CKO_NETSCAPE_CRL != crl_class) { + xstatus = PR_FALSE; + } + } else { + xstatus = PR_FALSE; + } + NSSArena_Destroy(arena); + return xstatus; +} + +SECStatus DPCache_Refresh(CRLDPCache* cache, CERTSignedCrl* crlobject, + void* wincx) +{ + SECStatus rv = SECSuccess; + /* Check if it is an invalid CRL + if we got a bad CRL, we want to cache it in order to avoid + subsequent fetches of this same identical bad CRL. We set + the cache to the invalid state to ensure that all certs + on this DP are considered revoked from now on. The cache + object will remain in this state until the bad CRL object + is removed from the token it was fetched from. If the cause + of the failure is that we didn't have the issuer cert to + verify the signature, this state can be cleared when + the issuer certificate becomes available if that causes the + signature to verify */ + + if (PR_TRUE == GetOpaqueCRLFields(crlobject)->bad) { + PORT_SetError(SEC_ERROR_BAD_DER); + cache->invalid |= CRL_CACHE_INVALID_CRLS; + return SECSuccess; + } else { + SECStatus signstatus = SECFailure; + if (cache->issuer) { + int64 issuingDate = 0; + signstatus = DER_UTCTimeToTime(&issuingDate, &crlobject->crl.lastUpdate); + if (SECSuccess == signstatus) { + signstatus = CERT_VerifySignedData(&crlobject->signatureWrap, + cache->issuer, issuingDate, wincx); + } + } + if (SECSuccess != signstatus) { + if (!cache->issuer) { + /* we tried to verify without an issuer cert . This is + because this CRL came through a call to SEC_FindCrlByName. + So we don't cache this verification failure. We'll try + to verify the CRL again when a certificate from that issuer + becomes available */ + GetOpaqueCRLFields(crlobject)->unverified = PR_TRUE; + } else { + GetOpaqueCRLFields(crlobject)->unverified = PR_FALSE; + } + PORT_SetError(SEC_ERROR_CRL_BAD_SIGNATURE); + cache->invalid |= CRL_CACHE_INVALID_CRLS; + return SECSuccess; + } else { + GetOpaqueCRLFields(crlobject)->unverified = PR_FALSE; + } + } + + /* complete the entry decoding */ + rv = CERT_CompleteCRLDecodeEntries(crlobject); + if (SECSuccess == rv) { + /* XCRL : if this is a delta, add it to the hash table */ + /* for now, always build the hash table from the full CRL */ + CERTCrlEntry** crlEntry = NULL; + PRUint32 numEntries = 0; + if (cache->entries) { + /* we already have a hash table, destroy it */ + PL_HashTableDestroy(cache->entries); + cache->entries = NULL; + } + /* also destroy the PreAllocator */ + if (cache->prebuffer) + { + PreAllocator_Destroy(cache->prebuffer); + cache->prebuffer = NULL; + } + /* count CRL entries so we can pre-allocate space for hash table entries */ + for (crlEntry = crlobject->crl.entries; crlEntry && *crlEntry; crlEntry++) { + numEntries++; + } + cache->prebuffer = PreAllocator_Create(numEntries*sizeof(PLHashEntry)); + PR_ASSERT(cache->prebuffer); + if (cache->prebuffer) { + /* create a new hash table */ + cache->entries = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare, + PL_CompareValues, &preAllocOps, cache->prebuffer); + } + PR_ASSERT(cache->entries); + if (!cache->entries) { + rv = SECFailure; + } + if (SECSuccess == rv) { + /* add all serial numbers to the hash table */ + for (crlEntry = crlobject->crl.entries; crlEntry && *crlEntry; crlEntry++) { + PL_HashTableAdd(cache->entries, &(*crlEntry)->serialNumber, *crlEntry); + } + cache->full = crlobject; + cache->invalid = 0; /* valid cache */ + } else { + cache->invalid |= CRL_CACHE_OUT_OF_MEMORY; + } + } else { + cache->invalid |= CRL_CACHE_INVALID_CRLS; + } + return rv; +} + +void DPCache_Empty(CRLDPCache* cache) +{ + PRUint32 i; + PR_ASSERT(cache); + if (!cache) + { + return; + } + cache->full = NULL; + + cache->invalid = 0; + + if (cache->entries) { + /* we already have a hash table, destroy it */ + PL_HashTableDestroy(cache->entries); + cache->entries = NULL; + } + /* also destroy the PreAllocator */ + if (cache->prebuffer) + { + PreAllocator_Destroy(cache->prebuffer); + cache->prebuffer = NULL; + } + + for (i=0;i<cache->ncrls;i++) + { + CERTSignedCrl* crl = cache->crls[i]; + if (crl) + { + GetOpaqueCRLFields(crl)->deleted = PR_TRUE; + } + } +} + +SECStatus DPCache_Fetch(CRLDPCache* cache, void* wincx) +{ + SECStatus rv = SECSuccess; + CERTSignedCrl* crlobject = NULL; + PRUint32 i=0; + /* XCRL For now, we can only get one full CRL. In the future, we'll be able to + find more than one object, because of multiple tokens and deltas */ + rv = SEC_FindCrlByKeyOnSlot(NULL, cache->subject, SEC_CRL_TYPE, + &crlobject, CRL_DECODE_DONT_COPY_DER | + CRL_DECODE_SKIP_ENTRIES | + CRL_DECODE_KEEP_BAD_CRL); + /* if this function fails, something very wrong happened, such as an out + of memory error during CRL decoding. We don't want to proceed and must + mark the cache object invalid */ + if (SECFailure == rv) { + cache->invalid |= CRL_CACHE_LAST_FETCH_FAILED; + } else { + cache->invalid &= (~CRL_CACHE_LAST_FETCH_FAILED); + } + + if ((SECSuccess == rv) && (!crlobject)) { + /* no CRL was found. This is OK */ + DPCache_Empty(cache); + return SECSuccess; + } + + /* now check if we already have a binary equivalent DER CRL */ + for (i=0;i<cache->ncrls;i++) { + CERTSignedCrl* existing = cache->crls[i]; + if (existing && (SECEqual == SECITEM_CompareItem(existing->derCrl, crlobject->derCrl))) { + /* yes. Has the matching CRL been marked deleted ? */ + if (PR_TRUE == GetOpaqueCRLFields(crlobject)->deleted) { + /* Yes. Just replace the CK object ID and slot in the existing object. + This avoids an unnecessary signature verification & entry decode */ + /* XCRL we'll need to lock the CRL here in the future for iCRLs that are + shared between multiple CAs */ + existing->pkcs11ID = crlobject->pkcs11ID; + PK11_FreeSlot(existing->slot); /* release reference to old + CRL slot */ + existing->slot = crlobject->slot; /* adopt new CRL slot */ + crlobject->slot = NULL; /* clear slot to avoid double-freeing it + during CRL destroy */ + rv = SEC_DestroyCrl(crlobject); + PORT_Assert(SECSuccess == rv); + return rv; + } else { + /* We got an identical CRL from a different token. + Throw it away. */ + return SEC_DestroyCrl(crlobject); + } + } + } + + /* add the CRL to our array */ + if (SECSuccess == rv) { + rv = DPCache_AddCRL(cache, crlobject); + } + + /* update the cache with this new CRL */ + if (SECSuccess == rv) { + rv = DPCache_Refresh(cache, crlobject, wincx); + } + return rv; +} + +SECStatus DPCache_Lookup(CRLDPCache* cache, SECItem* sn, CERTCrlEntry** returned) +{ + CERTSignedCrl* crl = NULL; + CERTCrlEntry* acrlEntry = NULL; + if (!cache || !sn) { + /* no cache or SN to look up, this is bad */ + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (0 != cache->invalid) { + /* the cache contains a bad CRL, consider all certs revoked + as a security measure */ + PORT_SetError(SEC_ERROR_CRL_INVALID); + return SECFailure; + } + if (!cache->full) { + /* no CRL means no entry to return, but this is OK */ + *returned = NULL; + return SECSuccess; + } + crl = cache->full; + PR_ASSERT(cache->entries); + if (!cache->entries) + { + return SECFailure; + } + acrlEntry = PL_HashTableLookup(cache->entries, (void*)sn); + if (acrlEntry) + { + *returned = acrlEntry; + } + return SECSuccess; +} + +#ifdef USE_RWLOCK + +#define DPCache_LockWrite() { \ + if (readlocked){ \ + NSSRWLock_UnlockRead(cache->lock); \ + } \ + NSSRWLock_LockWrite(cache->lock); \ +} + +#define DPCache_UnlockWrite() { \ + if (readlocked){ \ + NSSRWLock_LockRead(cache->lock); \ + } \ + NSSRWLock_UnlockWrite(cache->lock); \ +} + +#else + +#define DPCache_LockWrite() {} + +#define DPCache_UnlockWrite() {} + +#endif + +SECStatus DPCache_Update(CRLDPCache* cache, CERTCertificate* issuer, + void* wincx, PRBool readlocked) +{ + /* Update the CRLDPCache now. We don't cache token CRL lookup misses + yet, as we have no way of getting notified of new PKCS#11 object + creation that happens in a token */ + SECStatus rv = SECSuccess; + PRUint32 i = 0; + PRBool updated = PR_FALSE; + + if (!cache) { + return SECFailure; + } + + /* verify CRLs that couldn't be checked when inserted into the cache + because the issuer cert was unavailable. These are CRLs that were + inserted into the cache through SEC_FindCrlByName, rather than + through a certificate verification (CERT_CheckCRL) */ + if (issuer) { + /* if we didn't have a valid issuer cert yet, but we do now. add it */ + if (NULL == cache->issuer) { + /* save the issuer cert */ + cache->issuer = CERT_DupCertificate(issuer); + } + + /* re-process all unverified CRLs */ + if (cache->issuer) { + for (i = 0; i < cache->ncrls ; i++) { + CERTSignedCrl* acrl = cache->crls[i]; + if (PR_TRUE == GetOpaqueCRLFields(acrl)->unverified) { + DPCache_LockWrite(); + /* check that we are the first thread to update */ + if (PR_TRUE == GetOpaqueCRLFields(acrl)->unverified) { + DPCache_Refresh(cache, acrl, wincx); + /* also check all the other CRLs */ + for (i = i+1 ; i < cache->ncrls ; i++) { + acrl = cache->crls[i]; + if (acrl && (PR_TRUE == GetOpaqueCRLFields(acrl)->unverified)) { + DPCache_Refresh(cache, acrl, wincx); + } + } + } + DPCache_UnlockWrite(); + break; + } + } + } + } + + if (cache->ncrls) { + /* check if all CRLs still exist */ + for (i = 0; (i < cache->ncrls) && (PR_FALSE == updated); i++) + { + CERTSignedCrl* savcrl = cache->crls[i]; + if (savcrl && (PR_TRUE != CRLStillExists(savcrl))) { + + /* this CRL is gone */ + DPCache_LockWrite(); + /* first, we need to check if another thread updated + it before we did, and abort if it has been modified since + we acquired the lock */ + if ((savcrl == cache->crls[i]) && + PR_TRUE != CRLStillExists(savcrl)) { + /* the CRL is gone. And we are the one to do the update */ + /* Mark the CRL deleted */ + GetOpaqueCRLFields(savcrl)->deleted = PR_TRUE; + /* also check all the other CRLs */ + for (i = i+1 ; i < cache->ncrls ; i++) { + CERTSignedCrl* acrl = cache->crls[i]; + if (acrl && (PR_TRUE != CRLStillExists(acrl))) { + GetOpaqueCRLFields(acrl)->deleted = PR_TRUE; + } + } + /* and try to fetch a new one */ + rv = DPCache_Fetch(cache, wincx); + updated = PR_TRUE; + if (SECSuccess == rv) { + rv = DPCache_Cleanup(cache); /* clean up deleted CRLs + from the cache*/ + } + } + DPCache_UnlockWrite(); + } + } + } else { + /* we had zero CRL for this DP, try to get one from tokens */ + DPCache_LockWrite(); + /* check if another thread updated before us, and skip update if so */ + if (0 == cache->ncrls) + { + /* we are the first */ + rv = DPCache_Fetch(cache, wincx); + } + DPCache_UnlockWrite(); + } + + return rv; +} + +SECStatus DPCache_Initialize(CRLDPCache* cache, CERTCertificate* issuer, + SECItem* subject, SECItem* dp) +{ + PORT_Assert(cache); + if (!cache) { + return SECFailure; + } + memset(cache, 0, sizeof(CRLDPCache)); +#ifdef USE_RWLOCK + cache->lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL); +#else + cache->lock = PR_NewLock(); +#endif + if (!cache->lock) + { + return SECFailure; + } + if (issuer) + { + cache->issuer = CERT_DupCertificate(issuer); + } + cache->distributionPoint = SECITEM_DupItem(dp); + cache->subject = SECITEM_DupItem(subject); + return SECSuccess; +} + +SECStatus IssuerCache_Create(CRLIssuerCache** returned, + CERTCertificate* issuer, + SECItem* subject, SECItem* dp) +{ + SECStatus rv = SECSuccess; + CRLIssuerCache* cache = NULL; + PORT_Assert(returned); + PORT_Assert(subject); + if (!returned || !subject) + { + return SECFailure; + } + cache = (CRLIssuerCache*) PR_Malloc(sizeof(CRLIssuerCache)); + if (!cache) + { + return SECFailure; + } + memset(cache, 0, sizeof(CRLIssuerCache)); + cache->subject = SECITEM_DupItem(subject); +#if 0 + /* XCRL */ + cache->lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL); + if (!cache->lock) + { + rv = SECFailure; + } + if (SECSuccess == rv && issuer) + { + cache->issuer = CERT_DupCertificate(issuer); + if (!cache->issuer) + { + rv = SECFailure; + } + } +#endif + if (SECSuccess != rv) + { + return IssuerCache_Destroy(cache); + } + *returned = cache; + return SECSuccess; +} + +SECStatus IssuerCache_AddDP(CRLIssuerCache* cache, CERTCertificate* issuer, + SECItem* subject, SECItem* dp, CRLDPCache** newdpc) +{ + SECStatus rv = SECSuccess; + /* now create the required DP cache object */ + if (!dp) { + /* default distribution point */ + rv = DPCache_Initialize(&cache->dp, issuer, subject, NULL); + if (SECSuccess == rv) { + cache->dpp = &cache->dp; + if (newdpc) { + *newdpc = cache->dpp; + } + } + } else { + /* we should never hit this until we support multiple DPs */ + PORT_Assert(dp); + rv = SECFailure; + /* XCRL allocate a new distribution point cache object, initialize it, + and add it to the hash table of DPs */ + } + return rv; +} + +SECStatus CRLCache_AddIssuer(CRLIssuerCache* issuer) +{ + PORT_Assert(issuer); + PORT_Assert(crlcache.issuers); + if (!issuer || !crlcache.issuers) { + return SECFailure; + } + if (NULL == PL_HashTableAdd(crlcache.issuers, (void*) issuer->subject, + (void*) issuer)) { + return SECFailure; + } + return SECSuccess; +} + +SECStatus GetIssuerCache(CRLCache* cache, SECItem* subject, CRLIssuerCache** returned) +{ + /* we need to look up the issuer in the hash table */ + SECStatus rv = SECSuccess; + PORT_Assert(cache); + PORT_Assert(subject); + PORT_Assert(returned); + PORT_Assert(crlcache.issuers); + if (!cache || !subject || !returned || !crlcache.issuers) { + rv = SECFailure; + } + + if (SECSuccess == rv){ + *returned = (CRLIssuerCache*) PL_HashTableLookup(crlcache.issuers, + (void*) subject); + } + + return rv; +} + +CERTSignedCrl* GetBestCRL(CRLDPCache* cache) +{ + PRUint32 i = 0; + PR_ASSERT(cache); + if (!cache) { + return NULL; + } + if (0 == cache->ncrls) { + /* no CRLs in the cache */ + return NULL; + } + /* first, check if we have a valid full CRL, and use that */ + if (cache->full) { + return SEC_DupCrl(cache->full); + } + /* otherwise, check all the fetched CRLs for one with valid DER */ + for (i = 0; i < cache->ncrls ; i++) { + CERTSignedCrl* acrl = cache->crls[i]; + if (PR_FALSE == GetOpaqueCRLFields(acrl)->bad) { + SECStatus rv = CERT_CompleteCRLDecodeEntries(acrl); + if (SECSuccess == rv) { + return SEC_DupCrl(acrl); + } + } + } + return NULL; +} + +CRLDPCache* GetDPCache(CRLIssuerCache* cache, SECItem* dp) +{ + CRLDPCache* dpp = NULL; + PORT_Assert(cache); + /* XCRL for now we only support the "default" DP, ie. the + full CRL. So we can return the global one without locking. In + the future we will have a lock */ + PORT_Assert(NULL == dp); + if (!cache || dp) { + return NULL; + } +#if 0 + /* XCRL */ + NSSRWLock_LockRead(cache->lock); +#endif + dpp = cache->dpp; +#if 0 + /* XCRL */ + NSSRWLock_UnlockRead(cache->lock); +#endif + return dpp; +} + +SECStatus AcquireDPCache(CERTCertificate* issuer, SECItem* subject, SECItem* dp, + int64 t, void* wincx, CRLDPCache** dpcache, + PRBool* writeLocked) +{ + SECStatus rv = SECSuccess; + CRLIssuerCache* issuercache = NULL; + + PORT_Assert(crlcache.lock); + if (!crlcache.lock) { + /* CRL cache is not initialized */ + return SECFailure; + } + PR_Lock(crlcache.lock); + rv = GetIssuerCache(&crlcache, subject, &issuercache); + if (SECSuccess != rv) { + PR_Unlock(crlcache.lock); + return SECFailure; + } + if (!issuercache) { + /* there is no cache for this issuer yet. This means this is the + first time we look up a cert from that issuer, and we need to + create the cache. Do it within the global cache lock to ensure + no two threads will simultaneously try to create the same issuer + cache. XXX this could be optimized with a r/w lock at this level + too. But the code would have to check if it already exists when + adding to the hash table */ + + rv = IssuerCache_Create(&issuercache, issuer, subject, dp); + if (SECSuccess == rv && !issuercache) { + PORT_Assert(issuercache); + rv = SECFailure; + } + + if (SECSuccess == rv) { + /* This is the first time we look up a cert of this issuer. + Create the DPCache for this DP . */ + rv = IssuerCache_AddDP(issuercache, issuer, subject, dp, dpcache); + } + + if (SECSuccess == rv) { + /* lock the DPCache for write to ensure the update happens in this thread */ + *writeLocked = PR_TRUE; +#ifdef USE_RWLOCK + NSSRWLock_LockWrite((*dpcache)->lock); +#else + PR_Lock((*dpcache)->lock); +#endif + } + + if (SECSuccess == rv) { + /* now add the new issuer cache to the global hash table of issuers */ + rv = CRLCache_AddIssuer(issuercache); + if (SECSuccess != rv) { + /* failure */ + rv = SECFailure; + } + } + + /* now unlock the global cache. We only want to lock the hash table + addition. Holding it longer would hurt scalability */ + PR_Unlock(crlcache.lock); + + if (SECSuccess != rv && issuercache) { + if (PR_TRUE == *writeLocked) { +#ifdef USE_RWLOCK + NSSRWLock_UnlockWrite((*dpcache)->lock); +#else + PR_Unlock((*dpcache)->lock); +#endif + } + IssuerCache_Destroy(issuercache); + issuercache = NULL; + } + + if (SECSuccess != rv) { + return SECFailure; + } + } else { + PR_Unlock(crlcache.lock); + *dpcache = GetDPCache(issuercache, dp); + } + /* we now have a DPCache that we can use for lookups */ + /* lock it for read, unless we already locked for write */ + if (PR_FALSE == *writeLocked) + { +#ifdef USE_RWLOCK + NSSRWLock_LockRead((*dpcache)->lock); +#else + PR_Lock((*dpcache)->lock); +#endif + } + + if (SECSuccess == rv) { + /* currently there is always one and only one DPCache */ + PORT_Assert(*dpcache); + if (*dpcache) + { + /* make sure the DP cache is up to date before using it */ + rv = DPCache_Update(*dpcache, issuer, wincx, PR_FALSE == *writeLocked); + } + else + { + rv = SECFailure; + } + } + return rv; +} + +void ReleaseDPCache(CRLDPCache* dpcache, PRBool writeLocked) +{ + if (!dpcache) { + return; + } + if (PR_TRUE == writeLocked) { +#ifdef USE_RWLOCK + NSSRWLock_UnlockWrite(dpcache->lock); +#else + PR_Unlock(dpcache->lock); +#endif + } else { +#ifdef USE_RWLOCK + NSSRWLock_UnlockRead(dpcache->lock); +#else + PR_Unlock(dpcache->lock); +#endif + } +} + +SECStatus +CERT_CheckCRL(CERTCertificate* cert, CERTCertificate* issuer, SECItem* dp, + int64 t, void* wincx) +{ + PRBool lockedwrite = PR_FALSE; + SECStatus rv = SECSuccess; + CRLDPCache* dpcache = NULL; + if (!cert || !issuer) { + return SECFailure; + } + + rv = AcquireDPCache(issuer, &issuer->derSubject, dp, t, wincx, &dpcache, &lockedwrite); + + if (SECSuccess == rv) { + /* now look up the certificate SN in the DP cache's CRL */ + CERTCrlEntry* entry = NULL; + rv = DPCache_Lookup(dpcache, &cert->serialNumber, &entry); + if (SECSuccess == rv && entry) { + /* check the time if we have one */ + if (entry->revocationDate.data && entry->revocationDate.len) { + int64 revocationDate = 0; + if (SECSuccess == DER_UTCTimeToTime(&revocationDate, + &entry->revocationDate)) { + /* we got a good revocation date, only consider the + certificate revoked if the time we are inquiring about + is past the revocation date */ + if (t>=revocationDate) { + rv = SECFailure; + } + } else { + /* invalid revocation date, consider the certificate + permanently revoked */ + rv = SECFailure; + } + } else { + /* no revocation date, certificate is permanently revoked */ + rv = SECFailure; + } + if (SECFailure == rv) { + PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); + } + } + } + + ReleaseDPCache(dpcache, lockedwrite); + return rv; +} + +CERTSignedCrl * +SEC_FindCrlByName(CERTCertDBHandle *handle, SECItem *crlKey, int type) +{ + CERTSignedCrl* acrl = NULL; + CRLDPCache* dpcache = NULL; + SECStatus rv = SECSuccess; + PRBool writeLocked = PR_FALSE; + + rv = AcquireDPCache(NULL, crlKey, NULL, 0, NULL, &dpcache, &writeLocked); + if (SECSuccess == rv) + { + acrl = GetBestCRL(dpcache); + ReleaseDPCache(dpcache, writeLocked); + } + return acrl; +} + +void CERT_CRLCacheRefreshIssuer(CERTCertDBHandle* dbhandle, SECItem* crlKey) +{ + CERTSignedCrl* acrl = NULL; + CRLDPCache* cache = NULL; + SECStatus rv = SECSuccess; + PRBool writeLocked = PR_FALSE; + + (void) dbhandle; /* silence compiler warnings */ + + rv = AcquireDPCache(NULL, crlKey, NULL, 0, NULL, &cache, &writeLocked); + if (SECSuccess != rv) + { + return; + } + if (PR_TRUE == writeLocked) + { + /* the DPCache is write-locked. This means that the issuer was just + added to the CRL cache. There is no need to do anything */ + } + else + { + PRBool readlocked = PR_TRUE; + /* we need to invalidate the DPCache here */ + DPCache_LockWrite(); + DPCache_Empty(cache); + DPCache_Cleanup(cache); + DPCache_UnlockWrite(); + } + ReleaseDPCache(cache, writeLocked); + return; +} + diff --git a/security/nss/lib/certdb/genname.c b/security/nss/lib/certdb/genname.c new file mode 100644 index 000000000..a8dcf6dff --- /dev/null +++ b/security/nss/lib/certdb/genname.c @@ -0,0 +1,1610 @@ +/* + * 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. + */ + +#include "plarena.h" +#include "seccomon.h" +#include "secitem.h" +#include "secoidt.h" +#include "mcom_db.h" +#include "secasn1.h" +#include "secder.h" +#include "certt.h" +#include "cert.h" +#include "xconst.h" +#include "secerr.h" +#include "secoid.h" +#include "prprf.h" +#include "genname.h" + + + +static const SEC_ASN1Template CERTNameConstraintTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNameConstraint) }, + { SEC_ASN1_ANY, offsetof(CERTNameConstraint, DERName) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(CERTNameConstraint, min), SEC_IntegerTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(CERTNameConstraint, max), SEC_IntegerTemplate }, + { 0, } +}; + +const SEC_ASN1Template CERT_NameConstraintSubtreeSubTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, 0, SEC_AnyTemplate } +}; + + +const SEC_ASN1Template CERT_NameConstraintSubtreePermitedTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | 0, 0, CERT_NameConstraintSubtreeSubTemplate } +}; + +const SEC_ASN1Template CERT_NameConstraintSubtreeExcludedTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | 1, 0, CERT_NameConstraintSubtreeSubTemplate } +}; + + +static const SEC_ASN1Template CERTNameConstraintsTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNameConstraints) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(CERTNameConstraints, DERPermited), CERT_NameConstraintSubtreeSubTemplate}, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(CERTNameConstraints, DERExcluded), CERT_NameConstraintSubtreeSubTemplate}, + { 0, } +}; + + +static const SEC_ASN1Template CERTOthNameTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OtherName) }, + { SEC_ASN1_OBJECT_ID, + offsetof(OtherName, oid) }, + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | 0, + offsetof(OtherName, name), SEC_AnyTemplate }, + { 0, } +}; + +static const SEC_ASN1Template CERTOtherNameTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | 0 , + offsetof(CERTGeneralName, name.OthName), CERTOthNameTemplate, + sizeof(CERTGeneralName) } +}; + +static const SEC_ASN1Template CERTOtherName2Template[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_CONTEXT_SPECIFIC | 0 , + 0, NULL, sizeof(CERTGeneralName) }, + { SEC_ASN1_OBJECT_ID, + offsetof(CERTGeneralName, name.OthName) + offsetof(OtherName, oid) }, + { SEC_ASN1_ANY, + offsetof(CERTGeneralName, name.OthName) + offsetof(OtherName, name) }, + { 0, } +}; + +static const SEC_ASN1Template CERT_RFC822NameTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | 1 , + offsetof(CERTGeneralName, name.other), SEC_IA5StringTemplate, + sizeof (CERTGeneralName)} +}; + +static const SEC_ASN1Template CERT_DNSNameTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | 2 , + offsetof(CERTGeneralName, name.other), SEC_IA5StringTemplate, + sizeof (CERTGeneralName)} +}; + +static const SEC_ASN1Template CERT_X400AddressTemplate[] = { + { SEC_ASN1_ANY | SEC_ASN1_CONTEXT_SPECIFIC | 3, + offsetof(CERTGeneralName, name.other), SEC_AnyTemplate, + sizeof (CERTGeneralName)} +}; + +static const SEC_ASN1Template CERT_DirectoryNameTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | 4, + offsetof(CERTGeneralName, derDirectoryName), SEC_AnyTemplate, + sizeof (CERTGeneralName)} +}; + + +static const SEC_ASN1Template CERT_EDIPartyNameTemplate[] = { + { SEC_ASN1_ANY | SEC_ASN1_CONTEXT_SPECIFIC | 5, + offsetof(CERTGeneralName, name.other), SEC_AnyTemplate, + sizeof (CERTGeneralName)} +}; + +static const SEC_ASN1Template CERT_URITemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | 6 , + offsetof(CERTGeneralName, name.other), SEC_IA5StringTemplate, + sizeof (CERTGeneralName)} +}; + +static const SEC_ASN1Template CERT_IPAddressTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | 7 , + offsetof(CERTGeneralName, name.other), SEC_OctetStringTemplate, + sizeof (CERTGeneralName)} +}; + +static const SEC_ASN1Template CERT_RegisteredIDTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | 8 , + offsetof(CERTGeneralName, name.other), SEC_ObjectIDTemplate, + sizeof (CERTGeneralName)} +}; + + +const SEC_ASN1Template CERT_GeneralNamesTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, 0, SEC_AnyTemplate } +}; + + + +void +CERT_DestroyGeneralNameList(CERTGeneralNameList *list) +{ + PZLock *lock; + + if (list != NULL) { + lock = list->lock; + PZ_Lock(lock); + if (--list->refCount <= 0 && list->arena != NULL) { + PORT_FreeArena(list->arena, PR_FALSE); + PZ_Unlock(lock); + PZ_DestroyLock(lock); + } else { + PZ_Unlock(lock); + } + } + return; +} + +CERTGeneralNameList * +CERT_CreateGeneralNameList(CERTGeneralName *name) { + PRArenaPool *arena; + CERTGeneralNameList *list = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto done; + } + list = (CERTGeneralNameList *) + PORT_ArenaZAlloc(arena, sizeof(CERTGeneralNameList)); + if (name != NULL) { + list->name = (CERTGeneralName *) + PORT_ArenaZAlloc(arena, sizeof(CERTGeneralName)); + list->name->l.next = list->name->l.prev = &list->name->l; + CERT_CopyGeneralName(arena, list->name, name); + } + list->lock = PZ_NewLock(nssILockList); + list->arena = arena; + list->refCount = 1; +done: + return list; +} + +CERTGeneralName * +cert_get_next_general_name(CERTGeneralName *current) +{ + PRCList *next; + + next = current->l.next; + return (CERTGeneralName *) (((char *) next) - offsetof(CERTGeneralName, l)); +} + +CERTGeneralName * +cert_get_prev_general_name(CERTGeneralName *current) +{ + PRCList *prev; + prev = current->l.prev; + return (CERTGeneralName *) (((char *) prev) - offsetof(CERTGeneralName, l)); +} + +CERTNameConstraint * +cert_get_next_name_constraint(CERTNameConstraint *current) +{ + PRCList *next; + + next = current->l.next; + return (CERTNameConstraint *) (((char *) next) - offsetof(CERTNameConstraint, l)); +} + +CERTNameConstraint * +cert_get_prev_name_constraint(CERTNameConstraint *current) +{ + PRCList *prev; + prev = current->l.prev; + return (CERTNameConstraint *) (((char *) prev) - offsetof(CERTNameConstraint, l)); +} + +SECItem * +CERT_EncodeGeneralName(CERTGeneralName *genName, SECItem *dest, PRArenaPool *arena) +{ + + + PORT_Assert(arena); + if (arena == NULL) { + goto loser; + } + if (dest == NULL) { + dest = (SECItem *) PORT_ArenaZAlloc(arena, sizeof(SECItem)); + } + switch (genName->type) { + case certURI: + dest = SEC_ASN1EncodeItem(arena, dest, genName, + CERT_URITemplate); + break; + case certRFC822Name: + dest = SEC_ASN1EncodeItem(arena, dest, genName, + CERT_RFC822NameTemplate); + break; + case certDNSName: + dest = SEC_ASN1EncodeItem(arena, dest, genName, + CERT_DNSNameTemplate); + break; + case certIPAddress: + dest = SEC_ASN1EncodeItem(arena, dest, genName, + CERT_IPAddressTemplate); + break; + case certOtherName: + dest = SEC_ASN1EncodeItem(arena, dest, genName, + CERTOtherNameTemplate); + break; + case certRegisterID: + dest = SEC_ASN1EncodeItem(arena, dest, genName, + CERT_RegisteredIDTemplate); + break; + case certEDIPartyName: + /* for this type, we expect the value is already encoded */ + dest = SEC_ASN1EncodeItem (arena, dest, genName, + CERT_EDIPartyNameTemplate); + break; + case certX400Address: + /* for this type, we expect the value is already encoded */ + dest = SEC_ASN1EncodeItem (arena, dest, genName, + CERT_X400AddressTemplate); + break; + case certDirectoryName: + if (genName->derDirectoryName.data == NULL) { + /* The field hasn't been encoded yet. */ + SEC_ASN1EncodeItem (arena, &(genName->derDirectoryName), + &(genName->name.directoryName), + CERT_NameTemplate); + } + if (genName->derDirectoryName.data == NULL) { + goto loser; + } + dest = SEC_ASN1EncodeItem(arena, dest, genName, + CERT_DirectoryNameTemplate); + break; + } + if (!dest) { + goto loser; + } + return dest; +loser: + return NULL; +} + + + +SECItem ** +cert_EncodeGeneralNames(PRArenaPool *arena, CERTGeneralName *names) +{ + CERTGeneralName *current_name; + SECItem **items = NULL; + int count = 0; + int i; + PRCList *head; + + PORT_Assert(arena); + current_name = names; + if (names != NULL) { + count = 1; + } + head = &(names->l); + while (current_name->l.next != head) { + current_name = cert_get_next_general_name(current_name); + ++count; + } + current_name = cert_get_next_general_name(current_name); + items = (SECItem **) PORT_ArenaAlloc(arena, sizeof(SECItem *) * (count + 1)); + + if (items == NULL) { + goto loser; + } + for (i = 0; i < count; i++) { + items[i] = CERT_EncodeGeneralName(current_name, (SECItem *) NULL, arena); + if (items[i] == NULL) { + goto loser; + } + current_name = cert_get_next_general_name(current_name); + } + items[i] = NULL; + return items; +loser: + return NULL; +} + +CERTGeneralName * +CERT_DecodeGeneralName(PRArenaPool *arena, + SECItem *encodedName, + CERTGeneralName *genName) +{ + CERTGeneralNameType genNameType; + SECStatus rv = SECSuccess; + + PORT_Assert(arena); + if (genName == NULL) { + genName = (CERTGeneralName *) PORT_ArenaZAlloc(arena, sizeof(CERTGeneralName)); + } + genNameType = (CERTGeneralNameType)((*(encodedName->data) & 0x0f) + 1); + switch (genNameType) { + case certURI: + rv = SEC_ASN1DecodeItem(arena, genName, CERT_URITemplate, encodedName); + break; + case certRFC822Name: + rv = SEC_ASN1DecodeItem(arena, genName, CERT_RFC822NameTemplate, encodedName); + break; + case certDNSName: + rv = SEC_ASN1DecodeItem(arena, genName, CERT_DNSNameTemplate, encodedName); + break; + case certIPAddress: + rv = SEC_ASN1DecodeItem(arena, genName, CERT_IPAddressTemplate, encodedName); + break; + case certOtherName: + rv = SEC_ASN1DecodeItem(arena, genName, CERTOtherNameTemplate, encodedName); + break; + case certRegisterID: + rv = SEC_ASN1DecodeItem(arena, genName, CERT_RegisteredIDTemplate, encodedName); + break; + case certEDIPartyName: + rv = SEC_ASN1DecodeItem(arena, genName, CERT_EDIPartyNameTemplate, encodedName); + break; + case certX400Address: + rv = SEC_ASN1DecodeItem(arena, genName, CERT_X400AddressTemplate, encodedName); + break; + case certDirectoryName: + rv = SEC_ASN1DecodeItem + (arena, genName, CERT_DirectoryNameTemplate, encodedName); + if (rv != SECSuccess) { + goto loser; + } + rv = SEC_ASN1DecodeItem + (arena, &(genName->name.directoryName), CERT_NameTemplate, + &(genName->derDirectoryName)); + break; + } + + if (rv != SECSuccess) { + goto loser; + } + genName->type = genNameType; + genName->l.next = (PRCList *) ((char *) genName) + offsetof(CERTGeneralName, l); + genName->l.prev = genName->l.next; + return genName; +loser: + return NULL; +} + +CERTGeneralName * +cert_DecodeGeneralNames (PRArenaPool *arena, + SECItem **encodedGenName) +{ + PRCList *head = NULL; + PRCList *tail = NULL; + CERTGeneralName *currentName = NULL; + + PORT_Assert(arena); + if (!encodedGenName) { + goto loser; + } + while (*encodedGenName != NULL) { + currentName = CERT_DecodeGeneralName(arena, *encodedGenName, NULL); + if (currentName == NULL) { + goto loser; + } + if (head == NULL) { + head = &(currentName->l); + tail = head; + } + currentName->l.next = head; + currentName->l.prev = tail; + tail = &(currentName->l); + (cert_get_prev_general_name(currentName))->l.next = tail; + encodedGenName++; + } + (cert_get_next_general_name(currentName))->l.prev = tail; + return cert_get_next_general_name(currentName); +loser: + return NULL; +} + +void +CERT_DestroyGeneralName(CERTGeneralName *name) +{ + cert_DestroyGeneralNames(name); +} + +SECStatus +cert_DestroyGeneralNames(CERTGeneralName *name) +{ + CERTGeneralName *first; + CERTGeneralName *next = NULL; + + + first = name; + do { + next = cert_get_next_general_name(name); + PORT_Free(name); + name = next; + } while (name != first); + return SECSuccess; +} + +SECItem * +cert_EncodeNameConstraint(CERTNameConstraint *constraint, + SECItem *dest, + PRArenaPool *arena) +{ + PORT_Assert(arena); + if (dest == NULL) { + dest = (SECItem *) PORT_ArenaZAlloc(arena, sizeof(SECItem)); + if (dest == NULL) { + return NULL; + } + } + CERT_EncodeGeneralName(&(constraint->name), &(constraint->DERName), + arena); + + dest = SEC_ASN1EncodeItem (arena, dest, constraint, + CERTNameConstraintTemplate); + return dest; +} + +SECStatus +cert_EncodeNameConstraintSubTree(CERTNameConstraint *constraints, + PRArenaPool *arena, + SECItem ***dest, + PRBool permited) +{ + CERTNameConstraint *current_constraint = constraints; + SECItem **items = NULL; + int count = 0; + int i; + PRCList *head; + + PORT_Assert(arena); + if (constraints != NULL) { + count = 1; + } + head = (PRCList *) (((char *) constraints) + offsetof(CERTNameConstraint, l)); + while (current_constraint->l.next != head) { + current_constraint = cert_get_next_name_constraint(current_constraint); + ++count; + } + current_constraint = cert_get_next_name_constraint(current_constraint); + items = (SECItem **) PORT_ArenaZAlloc(arena, sizeof(SECItem *) * (count + 1)); + + if (items == NULL) { + goto loser; + } + for (i = 0; i < count; i++) { + items[i] = cert_EncodeNameConstraint(current_constraint, + (SECItem *) NULL, arena); + if (items[i] == NULL) { + goto loser; + } + current_constraint = cert_get_next_name_constraint(current_constraint); + } + *dest = items; + if (*dest == NULL) { + goto loser; + } + return SECSuccess; +loser: + return SECFailure; +} + +SECStatus +cert_EncodeNameConstraints(CERTNameConstraints *constraints, + PRArenaPool *arena, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(arena); + if (constraints->permited != NULL) { + rv = cert_EncodeNameConstraintSubTree(constraints->permited, arena, + &constraints->DERPermited, PR_TRUE); + if (rv == SECFailure) { + goto loser; + } + } + if (constraints->excluded != NULL) { + rv = cert_EncodeNameConstraintSubTree(constraints->excluded, arena, + &constraints->DERExcluded, PR_FALSE); + if (rv == SECFailure) { + goto loser; + } + } + dest = SEC_ASN1EncodeItem(arena, dest, constraints, + CERTNameConstraintsTemplate); + if (dest == NULL) { + goto loser; + } + return SECSuccess; +loser: + return SECFailure; +} + + +CERTNameConstraint * +cert_DecodeNameConstraint(PRArenaPool *arena, + SECItem *encodedConstraint) +{ + CERTNameConstraint *constraint; + SECStatus rv = SECSuccess; + CERTGeneralName *temp; + + + + PORT_Assert(arena); + constraint = (CERTNameConstraint *) PORT_ArenaZAlloc(arena, sizeof(CERTNameConstraint)); + rv = SEC_ASN1DecodeItem(arena, constraint, CERTNameConstraintTemplate, encodedConstraint); + if (rv != SECSuccess) { + goto loser; + } + temp = CERT_DecodeGeneralName(arena, &(constraint->DERName), &(constraint->name)); + if (temp != &(constraint->name)) { + goto loser; + } + + /* ### sjlee: since the name constraint contains only one + * CERTGeneralName, the list within CERTGeneralName shouldn't + * point anywhere else. Otherwise, bad things will happen. + */ + constraint->name.l.prev = constraint->name.l.next = &(constraint->name.l); + return constraint; +loser: + return NULL; +} + + +CERTNameConstraint * +cert_DecodeNameConstraintSubTree(PRArenaPool *arena, + SECItem **subTree, + PRBool permited) +{ + CERTNameConstraint *current = NULL; + CERTNameConstraint *first = NULL; + CERTNameConstraint *last = NULL; + CERTNameConstraint *next = NULL; + int i = 0; + + while (subTree[i] != NULL) { + current = cert_DecodeNameConstraint(arena, subTree[i]); + if (current == NULL) { + goto loser; + } + if (last == NULL) { + first = last = current; + } + current->l.prev = &(last->l); + current->l.next = last->l.next; + last->l.next = &(current->l); + i++; + } + first->l.prev = &(current->l); + return first; +loser: + if (first) { + current = first; + do { + next = cert_get_next_name_constraint(current); + PORT_Free(current); + current = next; + }while (current != first); + } + return NULL; +} + +CERTNameConstraints * +cert_DecodeNameConstraints(PRArenaPool *arena, + SECItem *encodedConstraints) +{ + CERTNameConstraints *constraints; + SECStatus rv; + + PORT_Assert(arena); + PORT_Assert(encodedConstraints); + constraints = (CERTNameConstraints *) PORT_ArenaZAlloc(arena, + sizeof(CERTNameConstraints)); + if (constraints == NULL) { + goto loser; + } + rv = SEC_ASN1DecodeItem(arena, constraints, CERTNameConstraintsTemplate, + encodedConstraints); + if (rv != SECSuccess) { + goto loser; + } + if (constraints->DERPermited != NULL && constraints->DERPermited[0] != NULL) { + constraints->permited = cert_DecodeNameConstraintSubTree(arena, + constraints->DERPermited, + PR_TRUE); + if (constraints->permited == NULL) { + goto loser; + } + } + if (constraints->DERExcluded != NULL && constraints->DERExcluded[0] != NULL) { + constraints->excluded = cert_DecodeNameConstraintSubTree(arena, + constraints->DERExcluded, + PR_FALSE); + if (constraints->excluded == NULL) { + goto loser; + } + } + return constraints; +loser: + return NULL; +} + + +SECStatus +CERT_CopyGeneralName(PRArenaPool *arena, + CERTGeneralName *dest, + CERTGeneralName *src) +{ + SECStatus rv; + CERTGeneralName *destHead = dest; + CERTGeneralName *srcHead = src; + CERTGeneralName *temp; + + PORT_Assert(dest != NULL); + dest->type = src->type; + do { + switch (src->type) { + case certDirectoryName: { + rv = SECITEM_CopyItem(arena, &dest->derDirectoryName, &src->derDirectoryName); + if (rv != SECSuccess) { + return rv; + } + rv = CERT_CopyName(arena, &dest->name.directoryName, &src->name.directoryName); + break; + } + case certOtherName: { + rv = SECITEM_CopyItem(arena, &dest->name.OthName.name, &src->name.OthName.name); + if (rv != SECSuccess) { + return rv; + } + rv = SECITEM_CopyItem(arena, &dest->name.OthName.oid, &src->name.OthName.oid); + break; + } + default: { + rv = SECITEM_CopyItem(arena, &dest->name.other, &src->name.other); + } + } + src = cert_get_next_general_name(src); + /* if there is only one general name, we shouldn't do this */ + if (src != srcHead) { + if (dest->l.next == &destHead->l) { + if (arena) { + temp = (CERTGeneralName *) + PORT_ArenaZAlloc(arena, sizeof(CERTGeneralName)); + } else { + temp = (CERTGeneralName *) + PORT_ZAlloc(sizeof(CERTGeneralName)); + } + temp->l.next = &destHead->l; + temp->l.prev = &dest->l; + destHead->l.prev = &temp->l; + dest->l.next = &temp->l; + dest = temp; + } else { + dest = cert_get_next_general_name(dest); + } + } + } while (src != srcHead && rv == SECSuccess); + return rv; +} + + +CERTGeneralNameList * +CERT_DupGeneralNameList(CERTGeneralNameList *list) +{ + if (list != NULL) { + PZ_Lock(list->lock); + list->refCount++; + PZ_Unlock(list->lock); + } + return list; +} + +CERTNameConstraint * +CERT_CopyNameConstraint(PRArenaPool *arena, + CERTNameConstraint *dest, + CERTNameConstraint *src) +{ + SECStatus rv; + + if (dest == NULL) { + dest = (CERTNameConstraint *) PORT_ArenaZAlloc(arena, sizeof(CERTNameConstraint)); + /* mark that it is not linked */ + dest->name.l.prev = dest->name.l.next = &(dest->name.l); + } + rv = CERT_CopyGeneralName(arena, &dest->name, &src->name); + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_CopyItem(arena, &dest->DERName, &src->DERName); + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_CopyItem(arena, &dest->min, &src->min); + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_CopyItem(arena, &dest->max, &src->max); + if (rv != SECSuccess) { + goto loser; + } + dest->l.prev = dest->l.next = &dest->l; + return dest; +loser: + return NULL; +} + + +CERTGeneralName * +cert_CombineNamesLists(CERTGeneralName *list1, CERTGeneralName *list2) +{ + PRCList *begin1; + PRCList *begin2; + PRCList *end1; + PRCList *end2; + + if (list1 == NULL){ + return list2; + } else if (list2 == NULL) { + return list1; + } else { + begin1 = &list1->l; + begin2 = &list2->l; + end1 = list1->l.prev; + end2 = list2->l.prev; + end1->next = begin2; + end2->next = begin1; + begin1->prev = end2; + begin2->prev = end1; + return list1; + } +} + + +CERTNameConstraint * +cert_CombineConstraintsLists(CERTNameConstraint *list1, CERTNameConstraint *list2) +{ + PRCList *begin1; + PRCList *begin2; + PRCList *end1; + PRCList *end2; + + if (list1 == NULL){ + return list2; + } else if (list2 == NULL) { + return list1; + } else { + begin1 = &list1->l; + begin2 = &list2->l; + end1 = list1->l.prev; + end2 = list2->l.prev; + end1->next = begin2; + end2->next = begin1; + begin1->prev = end2; + begin2->prev = end1; + return list1; + } +} + + +CERTNameConstraint * +CERT_AddNameConstraint(CERTNameConstraint *list, + CERTNameConstraint *constraint) +{ + PORT_Assert(constraint != NULL); + constraint->l.next = constraint->l.prev = &constraint->l; + list = cert_CombineConstraintsLists(list, constraint); + return list; +} + + +SECStatus +CERT_GetNameConstriantByType (CERTNameConstraint *constraints, + CERTGeneralNameType type, + CERTNameConstraint **returnList, + PRArenaPool *arena) +{ + CERTNameConstraint *current; + CERTNameConstraint *temp; + + *returnList = NULL; + if (!constraints) + return SECSuccess; + current = constraints; + + do { + if (current->name.type == type || + (type == certDirectoryName && current->name.type == certRFC822Name)) { + temp = NULL; + temp = CERT_CopyNameConstraint(arena, temp, current); + if (temp == NULL) { + goto loser; + } + *returnList = CERT_AddNameConstraint(*returnList, temp); + } + current = cert_get_next_name_constraint(current); + } while (current != constraints); + return SECSuccess; +loser: + return SECFailure; +} + + +void * +CERT_GetGeneralNameByType (CERTGeneralName *genNames, + CERTGeneralNameType type, PRBool derFormat) +{ + CERTGeneralName *current; + + if (!genNames) + return (NULL); + current = genNames; + + do { + if (current->type == type) { + switch (type) { + case certDNSName: + case certEDIPartyName: + case certIPAddress: + case certRegisterID: + case certRFC822Name: + case certX400Address: + case certURI: { + return &(current->name.other); + } + case certOtherName: { + return &(current->name.OthName); + break; + } + case certDirectoryName: { + if (derFormat) { + return &(current->derDirectoryName); + } else{ + return &(current->name.directoryName); + } + break; + } + } + } + current = cert_get_next_general_name(current); + } while (current != genNames); + return (NULL); +} + +int +CERT_GetNamesLength(CERTGeneralName *names) +{ + int length = 0; + CERTGeneralName *first; + + first = names; + if (names != NULL) { + do { + length++; + names = cert_get_next_general_name(names); + } while (names != first); + } + return length; +} + +CERTGeneralName * +CERT_GetCertificateNames(CERTCertificate *cert, PRArenaPool *arena) +{ + CERTGeneralName *DN; + CERTGeneralName *altName; + SECItem altNameExtension; + SECStatus rv; + + + DN = (CERTGeneralName *) PORT_ArenaZAlloc(arena, sizeof(CERTGeneralName)); + if (DN == NULL) { + goto loser; + } + rv = CERT_CopyName(arena, &DN->name.directoryName, &cert->subject); + DN->type = certDirectoryName; + DN->l.next = DN->l.prev = &DN->l; + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_CopyItem(arena, &DN->derDirectoryName, &cert->derSubject); + if (rv != SECSuccess) { + goto loser; + } + rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, + &altNameExtension); + if (rv != SECSuccess) { + return DN; + } + altName = CERT_DecodeAltNameExtension(arena, &altNameExtension); + PORT_Free(altNameExtension.data); + DN = cert_CombineNamesLists(DN, altName); + return DN; +loser: + + return NULL; +} + +static SECStatus +compareNameToConstraint(char *name, char *constraint, PRBool substring) +{ + SECStatus rv; + + if (*constraint == '\0' && *name == '\0') { + return SECSuccess; + } + if (*constraint == '*') { + return compareNameToConstraint(name, constraint + 1, PR_TRUE); + } + if (substring) { + if (*constraint == '\0') { + return SECSuccess; + } + while (*name != *constraint) { + if (*name == '\0') { + return SECFailure; + } + name++; + } + rv = compareNameToConstraint(name + 1, constraint + 1, PR_FALSE); + if (rv == SECSuccess) { + return rv; + } + name++; + } else { + if (*name == *constraint) { + name++; + constraint++; + } else { + return SECFailure; + } + } + return compareNameToConstraint(name, constraint, substring); +} + +SECStatus +cert_CompareNameWithConstraints(CERTGeneralName *name, + CERTNameConstraint *constraints, + PRBool excluded) +{ + SECStatus rv = SECSuccess; + char *nameString = NULL; + char *constraintString = NULL; + int start; + int end; + int tag; + CERTRDN **nameRDNS, *nameRDN; + CERTRDN **constraintRDNS, *constraintRDN; + CERTAVA **nameAVAS, *nameAVA; + CERTAVA **constraintAVAS, *constraintAVA; + CERTNameConstraint *current; + SECItem *avaValue; + CERTName constraintName; + CERTName certName; + SECComparison status = SECEqual; + PRArenaPool *certNameArena; + PRArenaPool *constraintNameArena; + + certName.arena = NULL; + certName.rdns = NULL; + constraintName.arena = NULL; + constraintName.rdns = NULL; + if (constraints != NULL) { + current = constraints; + if (name->type == certDirectoryName) { + certNameArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + CERT_CopyName(certNameArena, &certName, &name->name.directoryName); + nameRDNS = certName.rdns; + for (;;) { + nameRDN = *nameRDNS++; + nameAVAS = nameRDN->avas; + for(;;) { + nameAVA = *nameAVAS++; + tag = CERT_GetAVATag(nameAVA); + if ( tag == SEC_OID_PKCS9_EMAIL_ADDRESS || + tag == SEC_OID_RFC1274_MAIL) { + avaValue = CERT_DecodeAVAValue(&nameAVA->value); + nameString = (char*)PORT_ZAlloc(avaValue->len + 1); + nameString = PORT_Strncpy(nameString, (char *) avaValue->data, avaValue->len); + start = 0; + while(nameString[start] != '@' && nameString[start + 1] != '\0') { + start++; + } + start++; + do{ + if (current->name.type == certRFC822Name) { + constraintString = (char*)PORT_ZAlloc(current->name.name.other.len + 1); + constraintString = PORT_Strncpy(constraintString, + (char *) current->name.name.other.data, + current->name.name.other.len); + rv = compareNameToConstraint(nameString + start, constraintString, + PR_FALSE); + + if (constraintString != NULL) { + PORT_Free(constraintString); + constraintString = NULL; + } + if (nameString != NULL) { + PORT_Free(nameString); + nameString = NULL; + } + if (rv == SECSuccess && excluded == PR_TRUE) { + goto found; + } + if (rv == SECSuccess && excluded == PR_FALSE) { + break; + } + } + current = cert_get_next_name_constraint(current); + } while (current != constraints); + } + if (rv != SECSuccess && excluded == PR_FALSE) { + goto loser; + } + if (*nameAVAS == NULL) { + break; + } + } + if (*nameRDNS == NULL) { + break; + } + } + } + current = constraints; + do { + switch (name->type) { + case certDNSName: + nameString = (char*)PORT_ZAlloc(name->name.other.len + 1); + nameString = PORT_Strncpy(nameString, (char *) name->name.other.data, + name->name.other.len); + constraintString = (char*)PORT_ZAlloc(current->name.name.other.len + 1); + constraintString = PORT_Strncpy(constraintString, + (char *) current->name.name.other.data, + current->name.name.other.len); + rv = compareNameToConstraint(nameString, constraintString, PR_FALSE); + if (nameString != NULL) { + PORT_Free(nameString); + } + if (constraintString != NULL) { + PORT_Free(constraintString); + } + break; + case certRFC822Name: + nameString = (char*)PORT_ZAlloc(name->name.other.len + 1); + nameString = PORT_Strncpy(nameString, (char *) name->name.other.data, + name->name.other.len); + start = 0; + while(nameString[start] != '@' && nameString[start + 1] != '\0') { + start++; + } + start++; + constraintString = (char*)PORT_ZAlloc(current->name.name.other.len + 1); + constraintString = PORT_Strncpy(constraintString, + (char *) current->name.name.other.data, + current->name.name.other.len); + rv = compareNameToConstraint(nameString + start, constraintString, PR_FALSE); + if (nameString != NULL) { + PORT_Free(nameString); + } + if (constraintString != NULL) { + PORT_Free(constraintString); + } + break; + case certURI: + nameString = (char*)PORT_ZAlloc(name->name.other.len + 1); + nameString = PORT_Strncpy(nameString, (char *) name->name.other.data, + name->name.other.len); + start = 0; + while(PORT_Strncmp(nameString + start, "://", 3) != 0 && + nameString[start + 3] != '\0') { + start++; + } + start +=3; + end = 0; + while(nameString[start + end] != '/' && + nameString[start + end] != '\0') { + end++; + } + nameString[start + end] = '\0'; + constraintString = (char*)PORT_ZAlloc(current->name.name.other.len + 1); + constraintString = PORT_Strncpy(constraintString, + (char *) current->name.name.other.data, + current->name.name.other.len); + rv = compareNameToConstraint(nameString + start, constraintString, PR_FALSE); + if (nameString != NULL) { + PORT_Free(nameString); + } + if (constraintString != NULL) { + PORT_Free(constraintString); + } + break; + case certDirectoryName: + if (current->name.type == certDirectoryName) { + constraintNameArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + CERT_CopyName(constraintNameArena, &constraintName, ¤t->name.name.directoryName); + constraintRDNS = constraintName.rdns; + for (;;) { + constraintRDN = *constraintRDNS++; + constraintAVAS = constraintRDN->avas; + for(;;) { + constraintAVA = *constraintAVAS++; + certNameArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + CERT_CopyName(certNameArena, &certName, &name->name.directoryName); + nameRDNS = certName.rdns; + for (;;) { + nameRDN = *nameRDNS++; + nameAVAS = nameRDN->avas++; + for(;;) { + nameAVA = *nameAVAS++; + status = CERT_CompareAVA(constraintAVA, nameAVA); + if (status == SECEqual || *nameAVAS == NULL) { + break; + } + } + if (status == SECEqual || *nameRDNS == NULL) { + break; + } + } + if (status != SECEqual || *constraintAVAS == NULL) { + break; + } + } + if (status != SECEqual || *constraintRDNS == NULL) { + break; + } + } + if (status == SECEqual) { + if (excluded == PR_FALSE) { + goto found; + } else { + goto loser; + } + } + break; + } else if (current->name.type == certRFC822Name) { + current = cert_get_next_name_constraint(current); + continue; + } + default: + /* other types are not supported */ + if (excluded) { + goto found; + } else { + goto loser; + } + } + if (rv == SECSuccess && status == SECEqual) { + goto found; + } + current = cert_get_next_name_constraint(current); + } while (current !=constraints); + } else { + goto found; + } +loser: + if (certName.arena) { + CERT_DestroyName(&certName); + } + if (constraintName.arena) { + CERT_DestroyName(&constraintName); + } + return SECFailure; +found: + if (certName.arena) { + CERT_DestroyName(&certName); + } + if (constraintName.arena) { + CERT_DestroyName(&constraintName); + } + return SECSuccess; +} + + +CERTCertificate * +CERT_CompareNameSpace(CERTCertificate *cert, + CERTGeneralName *namesList, + SECItem *namesListIndex, + PRArenaPool *arena, + CERTCertDBHandle *handle) +{ + SECStatus rv; + SECItem constraintsExtension; + CERTNameConstraints *constraints; + CERTGeneralName *currentName; + int count = 0; + CERTNameConstraint *matchingConstraints; + CERTCertificate *badCert = NULL; + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_NAME_CONSTRAINTS, &constraintsExtension); + if (rv != SECSuccess) { + return NULL; + } + constraints = cert_DecodeNameConstraints(arena, &constraintsExtension); + currentName = namesList; + if (constraints == NULL) { + goto loser; + } + do { + if (constraints->excluded != NULL) { + rv = CERT_GetNameConstriantByType(constraints->excluded, currentName->type, + &matchingConstraints, arena); + if (rv != SECSuccess) { + goto loser; + } + if (matchingConstraints != NULL) { + rv = cert_CompareNameWithConstraints(currentName, matchingConstraints, + PR_TRUE); + if (rv != SECFailure) { + goto loser; + } + } + } + if (constraints->permited != NULL) { + rv = CERT_GetNameConstriantByType(constraints->permited, currentName->type, + &matchingConstraints, arena); + if (rv != SECSuccess) { + goto loser; + } + if (matchingConstraints != NULL) { + rv = cert_CompareNameWithConstraints(currentName, matchingConstraints, + PR_FALSE); + if (rv != SECSuccess) { + goto loser; + } + } else { + goto loser; + } + } + currentName = cert_get_next_general_name(currentName); + count ++; + } while (currentName != namesList); + return NULL; +loser: + badCert = CERT_FindCertByName (handle, &namesListIndex[count]); + return badCert; +} + + + +/* Search the cert for an X509_SUBJECT_ALT_NAME extension. +** ASN1 Decode it into a list of alternate names. +** Search the list of alternate names for one with the NETSCAPE_NICKNAME OID. +** ASN1 Decode that name. Turn the result into a zString. +** Look for duplicate nickname already in the certdb. +** If one is found, create a nickname string that is not a duplicate. +*/ +char * +CERT_GetNickName(CERTCertificate *cert, + CERTCertDBHandle *handle, + PRArenaPool *nicknameArena) +{ + CERTGeneralName *current; + CERTGeneralName *names; + char *nickname = NULL; + char *returnName = NULL; + char *basename = NULL; + PRArenaPool *arena = NULL; + CERTCertificate *tmpcert; + SECStatus rv; + int count; + int found = 0; + SECItem altNameExtension; + SECItem nick; + + if (handle == NULL) { + handle = CERT_GetDefaultCertDB(); + } + altNameExtension.data = NULL; + rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, + &altNameExtension); + if (rv != SECSuccess) { + goto loser; + } + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto loser; + } + names = CERT_DecodeAltNameExtension(arena, &altNameExtension); + if (names == NULL) { + goto loser; + } + current = names; + do { + if (current->type == certOtherName && + SECOID_FindOIDTag(¤t->name.OthName.oid) == + SEC_OID_NETSCAPE_NICKNAME) { + found = 1; + break; + } + current = cert_get_next_general_name(current); + } while (current != names); + if (!found) + goto loser; + + rv = SEC_ASN1DecodeItem(arena, &nick, SEC_IA5StringTemplate, + ¤t->name.OthName.name); + if (rv != SECSuccess) { + goto loser; + } + + /* make a null terminated string out of nick, with room enough at + ** the end to add on a number of up to 21 digits in length, (a signed + ** 64-bit number in decimal) plus a space and a "#". + */ + nickname = (char*)PORT_ZAlloc(nick.len + 24); + if (!nickname) + goto loser; + PORT_Strncpy(nickname, (char *)nick.data, nick.len); + + /* Don't let this cert's nickname duplicate one already in the DB. + ** If it does, create a variant of the nickname that doesn't. + */ + count = 0; + while ((tmpcert = CERT_FindCertByNickname(handle, nickname)) != NULL) { + CERT_DestroyCertificate(tmpcert); + if (!basename) { + basename = PORT_Strdup(nickname); + if (!basename) + goto loser; + } + count++; + sprintf(nickname, "%s #%d", basename, count); + } + + /* success */ + if (nicknameArena) { + returnName = PORT_ArenaStrdup(nicknameArena, nickname); + } else { + returnName = nickname; + nickname = NULL; + } +loser: + if (arena != NULL) + PORT_FreeArena(arena, PR_FALSE); + if (nickname) + PORT_Free(nickname); + if (basename) + PORT_Free(basename); + if (altNameExtension.data) + PORT_Free(altNameExtension.data); + return returnName; +} + + +SECStatus +CERT_CompareGeneralName(CERTGeneralName *a, CERTGeneralName *b) +{ + CERTGeneralName *currentA; + CERTGeneralName *currentB; + PRBool found; + + currentA = a; + currentB = b; + if (a != NULL) { + do { + if (currentB == NULL) { + return SECFailure; + } + currentB = cert_get_next_general_name(currentB); + currentA = cert_get_next_general_name(currentA); + } while (currentA != a); + } + if (currentB != b) { + return SECFailure; + } + currentA = a; + do { + currentB = b; + found = PR_FALSE; + do { + if (currentB->type == currentA->type) { + switch (currentB->type) { + case certDNSName: + case certEDIPartyName: + case certIPAddress: + case certRegisterID: + case certRFC822Name: + case certX400Address: + case certURI: + if (SECITEM_CompareItem(¤tA->name.other, + ¤tB->name.other) + == SECEqual) { + found = PR_TRUE; + } + break; + case certOtherName: + if (SECITEM_CompareItem(¤tA->name.OthName.oid, + ¤tB->name.OthName.oid) + == SECEqual && + SECITEM_CompareItem(¤tA->name.OthName.name, + ¤tB->name.OthName.name) + == SECEqual) { + found = PR_TRUE; + } + break; + case certDirectoryName: + if (CERT_CompareName(¤tA->name.directoryName, + ¤tB->name.directoryName) + == SECEqual) { + found = PR_TRUE; + } + } + + } + currentB = cert_get_next_general_name(currentB); + } while (currentB != b && found != PR_TRUE); + if (found != PR_TRUE) { + return SECFailure; + } + currentA = cert_get_next_general_name(currentA); + } while (currentA != a); + return SECSuccess; +} + +SECStatus +CERT_CompareGeneralNameLists(CERTGeneralNameList *a, CERTGeneralNameList *b) +{ + SECStatus rv; + + if (a == b) { + return SECSuccess; + } + if (a != NULL && b != NULL) { + PZ_Lock(a->lock); + PZ_Lock(b->lock); + rv = CERT_CompareGeneralName(a->name, b->name); + PZ_Unlock(a->lock); + PZ_Unlock(b->lock); + } else { + rv = SECFailure; + } + return rv; +} + +void * +CERT_GetGeneralNameFromListByType(CERTGeneralNameList *list, + CERTGeneralNameType type, + PRArenaPool *arena) +{ + CERTName *name = NULL; + SECItem *item = NULL; + OtherName *other = NULL; + OtherName *tmpOther = NULL; + void *data; + + PZ_Lock(list->lock); + data = CERT_GetGeneralNameByType(list->name, type, PR_FALSE); + if (data != NULL) { + switch (type) { + case certDNSName: + case certEDIPartyName: + case certIPAddress: + case certRegisterID: + case certRFC822Name: + case certX400Address: + case certURI: + if (arena != NULL) { + item = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem)); + if (item != NULL) { + SECITEM_CopyItem(arena, item, (SECItem *) data); + } + } else { + item = SECITEM_DupItem((SECItem *) data); + } + PZ_Unlock(list->lock); + return item; + case certOtherName: + other = (OtherName *) data; + if (arena != NULL) { + tmpOther = (OtherName *)PORT_ArenaAlloc(arena, + sizeof(OtherName)); + } else { + tmpOther = (OtherName *) PORT_Alloc(sizeof(OtherName)); + } + if (tmpOther != NULL) { + SECITEM_CopyItem(arena, &tmpOther->oid, &other->oid); + SECITEM_CopyItem(arena, &tmpOther->name, &other->name); + } + PZ_Unlock(list->lock); + return tmpOther; + case certDirectoryName: + if (arena == NULL) { + PZ_Unlock(list->lock); + return NULL; + } else { + name = (CERTName *) PORT_ArenaZAlloc(list->arena, + sizeof(CERTName)); + if (name != NULL) { + CERT_CopyName(arena, name, (CERTName *) data); + } + PZ_Unlock(list->lock); + return name; + } + } + } + PZ_Unlock(list->lock); + return NULL; +} + +void +CERT_AddGeneralNameToList(CERTGeneralNameList *list, + CERTGeneralNameType type, + void *data, SECItem *oid) +{ + CERTGeneralName *name; + + if (list != NULL && data != NULL) { + PZ_Lock(list->lock); + name = (CERTGeneralName *) + PORT_ArenaZAlloc(list->arena, sizeof(CERTGeneralName)); + name->type = type; + switch (type) { + case certDNSName: + case certEDIPartyName: + case certIPAddress: + case certRegisterID: + case certRFC822Name: + case certX400Address: + case certURI: + SECITEM_CopyItem(list->arena, &name->name.other, (SECItem *)data); + break; + case certOtherName: + SECITEM_CopyItem(list->arena, &name->name.OthName.name, + (SECItem *) data); + SECITEM_CopyItem(list->arena, &name->name.OthName.oid, + oid); + break; + case certDirectoryName: + CERT_CopyName(list->arena, &name->name.directoryName, + (CERTName *) data); + break; + } + name->l.prev = name->l.next = &name->l; + list->name = cert_CombineNamesLists(list->name, name); + list->len++; + PZ_Unlock(list->lock); + } + return; +} diff --git a/security/nss/lib/certdb/genname.h b/security/nss/lib/certdb/genname.h new file mode 100644 index 000000000..8e3a58619 --- /dev/null +++ b/security/nss/lib/certdb/genname.h @@ -0,0 +1,125 @@ +/* + * 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. + */ + +#ifndef _GENAME_H_ +#define _GENAME_H_ + +#include "plarena.h" +#include "seccomon.h" +#include "secoidt.h" +#include "secasn1.h" +#include "secder.h" +#include "certt.h" + +/************************************************************************/ +SEC_BEGIN_PROTOS + +extern const SEC_ASN1Template CERT_GeneralNamesTemplate[]; + +extern CERTGeneralName * +cert_get_next_general_name(CERTGeneralName *current); + +extern CERTGeneralName * +cert_get_prev_general_name(CERTGeneralName *current); + +extern SECItem * +CERT_EncodeGeneralName(CERTGeneralName *genName, SECItem *dest, + PRArenaPool *arena); + +extern SECItem ** +cert_EncodeGeneralNames(PRArenaPool *arena, CERTGeneralName *names); + +extern CERTGeneralName * +CERT_DecodeGeneralName(PRArenaPool *arena, SECItem *encodedName, + CERTGeneralName *genName); + +extern CERTGeneralName * +cert_DecodeGeneralNames(PRArenaPool *arena, SECItem **encodedGenName); + +extern SECStatus +cert_DestroyGeneralNames(CERTGeneralName *name); + +extern SECStatus +cert_EncodeNameConstraints(CERTNameConstraints *constraints, PRArenaPool *arena, + SECItem *dest); + +extern CERTNameConstraints * +cert_DecodeNameConstraints(PRArenaPool *arena, SECItem *encodedConstraints); + +extern CERTGeneralName * +cert_CombineNamesLists(CERTGeneralName *list1, CERTGeneralName *list2); + +extern CERTNameConstraint * +cert_CombineConstraintsLists(CERTNameConstraint *list1, CERTNameConstraint *list2); + +SECStatus +CERT_CompareGeneralName(CERTGeneralName *a, CERTGeneralName *b); + +SECStatus +CERT_CopyGeneralName(PRArenaPool *arena, + CERTGeneralName *dest, + CERTGeneralName *src); + +/* General Name Lists are a thread safe, reference counting layer to + * general names */ + +void +CERT_DestroyGeneralNameList(CERTGeneralNameList *list); + +CERTGeneralNameList * +CERT_CreateGeneralNameList(CERTGeneralName *name); + +SECStatus +CERT_CompareGeneralNameLists(CERTGeneralNameList *a, CERTGeneralNameList *b); + +void * +CERT_GetGeneralNameFromListByType(CERTGeneralNameList *list, + CERTGeneralNameType type, + PRArenaPool *arena); + +void +CERT_AddGeneralNameToList(CERTGeneralNameList *list, + CERTGeneralNameType type, + void *data, SECItem *oid); + + +CERTGeneralNameList * +CERT_DupGeneralNameList(CERTGeneralNameList *list); + + +int +CERT_GetNamesLength(CERTGeneralName *names); +/************************************************************************/ +SEC_END_PROTOS + +#endif diff --git a/security/nss/lib/certdb/manifest.mn b/security/nss/lib/certdb/manifest.mn new file mode 100644 index 000000000..944d38a55 --- /dev/null +++ b/security/nss/lib/certdb/manifest.mn @@ -0,0 +1,68 @@ +# +# 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. +# +CORE_DEPTH = ../../.. + +EXPORTS = \ + cert.h \ + certt.h \ + certdb.h \ + $(NULL) + +PRIVATE_EXPORTS = \ + genname.h \ + xconst.h \ + certxutl.h \ + certi.h \ + $(NULL) + +MODULE = nss + +CSRCS = \ + alg1485.c \ + certdb.c \ + certv3.c \ + certxutl.c \ + crl.c \ + genname.c \ + stanpcertdb.c \ + polcyxtn.c \ + secname.c \ + xauthkid.c \ + xbsconst.c \ + xconst.c \ + $(NULL) + +REQUIRES = dbm + +LIBRARY_NAME = certdb + diff --git a/security/nss/lib/certdb/polcyxtn.c b/security/nss/lib/certdb/polcyxtn.c new file mode 100644 index 000000000..41302f039 --- /dev/null +++ b/security/nss/lib/certdb/polcyxtn.c @@ -0,0 +1,565 @@ +/* + * 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. + */ + +/* + * Support for various policy related extensions + * + * $Id$ + */ + +#include "seccomon.h" +#include "secport.h" +#include "secder.h" +#include "cert.h" +#include "secoid.h" +#include "secasn1.h" +#include "secerr.h" +#include "nspr.h" + +const SEC_ASN1Template CERT_NoticeReferenceTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTNoticeReference) }, +/* NOTE: this should be a choice */ + { SEC_ASN1_IA5_STRING, + offsetof(CERTNoticeReference, organization) }, + { SEC_ASN1_SEQUENCE_OF, + offsetof(CERTNoticeReference, noticeNumbers), + SEC_IntegerTemplate }, + { 0 } +}; + +/* this template can not be encoded because of the option inline */ +const SEC_ASN1Template CERT_UserNoticeTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTUserNotice) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED, + offsetof(CERTUserNotice, derNoticeReference) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, + offsetof(CERTUserNotice, displayText) }, + { 0 } +}; + +const SEC_ASN1Template CERT_PolicyQualifierTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTPolicyQualifier) }, + { SEC_ASN1_OBJECT_ID, + offsetof(CERTPolicyQualifier, qualifierID) }, + { SEC_ASN1_ANY, + offsetof(CERTPolicyQualifier, qualifierValue) }, + { 0 } +}; + +const SEC_ASN1Template CERT_PolicyInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTPolicyInfo) }, + { SEC_ASN1_OBJECT_ID, + offsetof(CERTPolicyInfo, policyID) }, + { SEC_ASN1_SEQUENCE_OF, + offsetof(CERTPolicyInfo, policyQualifiers), + CERT_PolicyQualifierTemplate }, + { 0 } +}; + +const SEC_ASN1Template CERT_CertificatePoliciesTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, + offsetof(CERTCertificatePolicies, policyInfos), + CERT_PolicyInfoTemplate, sizeof(CERTCertificatePolicies) } +}; + +static void +breakLines(char *string) +{ + char *tmpstr; + char *lastspace = NULL; + int curlen = 0; + int c; + + tmpstr = string; + + while ( ( c = *tmpstr ) != '\0' ) { + switch ( c ) { + case ' ': + lastspace = tmpstr; + break; + case '\n': + lastspace = NULL; + curlen = 0; + break; + } + + if ( ( curlen >= 55 ) && ( lastspace != NULL ) ) { + *lastspace = '\n'; + curlen = ( tmpstr - lastspace ); + lastspace = NULL; + } + + curlen++; + tmpstr++; + } + + return; +} + +CERTCertificatePolicies * +CERT_DecodeCertificatePoliciesExtension(SECItem *extnValue) +{ + PRArenaPool *arena = NULL; + SECStatus rv; + CERTCertificatePolicies *policies; + CERTPolicyInfo **policyInfos, *policyInfo; + CERTPolicyQualifier **policyQualifiers, *policyQualifier; + SECItem newExtnValue; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if ( !arena ) { + goto loser; + } + + /* allocate the certifiate policies structure */ + policies = (CERTCertificatePolicies *) + PORT_ArenaZAlloc(arena, sizeof(CERTCertificatePolicies)); + + if ( policies == NULL ) { + goto loser; + } + + policies->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newExtnValue, extnValue); + if ( rv != SECSuccess ) { + goto loser; + } + + /* decode the policy info */ + rv = SEC_QuickDERDecodeItem(arena, policies, CERT_CertificatePoliciesTemplate, + &newExtnValue); + + if ( rv != SECSuccess ) { + goto loser; + } + + /* initialize the oid tags */ + policyInfos = policies->policyInfos; + while (*policyInfos != NULL ) { + policyInfo = *policyInfos; + policyInfo->oid = SECOID_FindOIDTag(&policyInfo->policyID); + policyQualifiers = policyInfo->policyQualifiers; + while ( *policyQualifiers != NULL ) { + policyQualifier = *policyQualifiers; + policyQualifier->oid = + SECOID_FindOIDTag(&policyQualifier->qualifierID); + policyQualifiers++; + } + policyInfos++; + } + + return(policies); + +loser: + if ( arena != NULL ) { + PORT_FreeArena(arena, PR_FALSE); + } + + return(NULL); +} + +void +CERT_DestroyCertificatePoliciesExtension(CERTCertificatePolicies *policies) +{ + if ( policies != NULL ) { + PORT_FreeArena(policies->arena, PR_FALSE); + } + return; +} + + +CERTUserNotice * +CERT_DecodeUserNotice(SECItem *noticeItem) +{ + PRArenaPool *arena = NULL; + SECStatus rv; + CERTUserNotice *userNotice; + SECItem newNoticeItem; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if ( !arena ) { + goto loser; + } + + /* allocate the userNotice structure */ + userNotice = (CERTUserNotice *)PORT_ArenaZAlloc(arena, + sizeof(CERTUserNotice)); + + if ( userNotice == NULL ) { + goto loser; + } + + userNotice->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newNoticeItem, noticeItem); + if ( rv != SECSuccess ) { + goto loser; + } + + /* decode the user notice */ + rv = SEC_QuickDERDecodeItem(arena, userNotice, CERT_UserNoticeTemplate, + &newNoticeItem); + + if ( rv != SECSuccess ) { + goto loser; + } + + if (userNotice->derNoticeReference.data != NULL) { + /* sigh, the asn1 parser stripped the sequence encoding, re add it + * before we decode. + */ + SECItem tmpbuf; + int newBytes; + + newBytes = SEC_ASN1LengthLength(userNotice->derNoticeReference.len)+1; + tmpbuf.len = newBytes + userNotice->derNoticeReference.len; + tmpbuf.data = PORT_ArenaZAlloc(arena, tmpbuf.len); + if (tmpbuf.data == NULL) { + goto loser; + } + tmpbuf.data[0] = SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED; + SEC_ASN1EncodeLength(&tmpbuf.data[1],userNotice->derNoticeReference.len); + PORT_Memcpy(&tmpbuf.data[newBytes],userNotice->derNoticeReference.data, + userNotice->derNoticeReference.len); + + /* OK, no decode it */ + rv = SEC_QuickDERDecodeItem(arena, &userNotice->noticeReference, + CERT_NoticeReferenceTemplate, &tmpbuf); + + PORT_Free(tmpbuf.data); tmpbuf.data = NULL; + if ( rv != SECSuccess ) { + goto loser; + } + } + + return(userNotice); + +loser: + if ( arena != NULL ) { + PORT_FreeArena(arena, PR_FALSE); + } + + return(NULL); +} + +void +CERT_DestroyUserNotice(CERTUserNotice *userNotice) +{ + if ( userNotice != NULL ) { + PORT_FreeArena(userNotice->arena, PR_FALSE); + } + return; +} + +static CERTPolicyStringCallback policyStringCB = NULL; +static void *policyStringCBArg = NULL; + +void +CERT_SetCAPolicyStringCallback(CERTPolicyStringCallback cb, void *cbarg) +{ + policyStringCB = cb; + policyStringCBArg = cbarg; + return; +} + +char * +stringFromUserNotice(SECItem *noticeItem) +{ + SECItem *org; + unsigned int len, headerlen; + char *stringbuf; + CERTUserNotice *userNotice; + char *policystr; + char *retstr = NULL; + SECItem *displayText; + SECItem **noticeNumbers; + unsigned int strnum; + + /* decode the user notice */ + userNotice = CERT_DecodeUserNotice(noticeItem); + if ( userNotice == NULL ) { + return(NULL); + } + + org = &userNotice->noticeReference.organization; + if ( (org->len != 0 ) && ( policyStringCB != NULL ) ) { + /* has a noticeReference */ + + /* extract the org string */ + len = org->len; + stringbuf = (char*)PORT_Alloc(len + 1); + if ( stringbuf != NULL ) { + PORT_Memcpy(stringbuf, org->data, len); + stringbuf[len] = '\0'; + + noticeNumbers = userNotice->noticeReference.noticeNumbers; + while ( *noticeNumbers != NULL ) { + /* XXX - only one byte integers right now*/ + strnum = (*noticeNumbers)->data[0]; + policystr = (* policyStringCB)(stringbuf, + strnum, + policyStringCBArg); + if ( policystr != NULL ) { + if ( retstr != NULL ) { + retstr = PR_sprintf_append(retstr, "\n%s", policystr); + } else { + retstr = PR_sprintf_append(retstr, "%s", policystr); + } + + PORT_Free(policystr); + } + + noticeNumbers++; + } + + PORT_Free(stringbuf); + } + } + + if ( retstr == NULL ) { + if ( userNotice->displayText.len != 0 ) { + displayText = &userNotice->displayText; + + if ( displayText->len > 2 ) { + if ( displayText->data[0] == SEC_ASN1_VISIBLE_STRING ) { + headerlen = 2; + if ( displayText->data[1] & 0x80 ) { + /* multibyte length */ + headerlen += ( displayText->data[1] & 0x7f ); + } + + len = displayText->len - headerlen; + retstr = (char*)PORT_Alloc(len + 1); + if ( retstr != NULL ) { + PORT_Memcpy(retstr, &displayText->data[headerlen],len); + retstr[len] = '\0'; + } + } + } + } + } + + CERT_DestroyUserNotice(userNotice); + + return(retstr); +} + +char * +CERT_GetCertCommentString(CERTCertificate *cert) +{ + char *retstring = NULL; + SECStatus rv; + SECItem policyItem; + CERTCertificatePolicies *policies = NULL; + CERTPolicyInfo **policyInfos; + CERTPolicyQualifier **policyQualifiers, *qualifier; + + policyItem.data = NULL; + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_CERTIFICATE_POLICIES, + &policyItem); + if ( rv != SECSuccess ) { + goto nopolicy; + } + + policies = CERT_DecodeCertificatePoliciesExtension(&policyItem); + if ( policies == NULL ) { + goto nopolicy; + } + + policyInfos = policies->policyInfos; + /* search through policyInfos looking for the verisign policy */ + while (*policyInfos != NULL ) { + if ( (*policyInfos)->oid == SEC_OID_VERISIGN_USER_NOTICES ) { + policyQualifiers = (*policyInfos)->policyQualifiers; + /* search through the policy qualifiers looking for user notice */ + while ( *policyQualifiers != NULL ) { + qualifier = *policyQualifiers; + if ( qualifier->oid == SEC_OID_PKIX_USER_NOTICE_QUALIFIER ) { + retstring = + stringFromUserNotice(&qualifier->qualifierValue); + break; + } + + policyQualifiers++; + } + break; + } + policyInfos++; + } + +nopolicy: + if ( policyItem.data != NULL ) { + PORT_Free(policyItem.data); + } + + if ( policies != NULL ) { + CERT_DestroyCertificatePoliciesExtension(policies); + } + + if ( retstring == NULL ) { + retstring = CERT_FindNSStringExtension(cert, + SEC_OID_NS_CERT_EXT_COMMENT); + } + + if ( retstring != NULL ) { + breakLines(retstring); + } + + return(retstring); +} + + +const SEC_ASN1Template CERT_OidSeqTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, + offsetof(CERTOidSequence, oids), + SEC_ObjectIDTemplate } +}; + +CERTOidSequence * +CERT_DecodeOidSequence(SECItem *seqItem) +{ + PRArenaPool *arena = NULL; + SECStatus rv; + CERTOidSequence *oidSeq; + SECItem newSeqItem; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if ( !arena ) { + goto loser; + } + + /* allocate the userNotice structure */ + oidSeq = (CERTOidSequence *)PORT_ArenaZAlloc(arena, + sizeof(CERTOidSequence)); + + if ( oidSeq == NULL ) { + goto loser; + } + + oidSeq->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newSeqItem, seqItem); + if ( rv != SECSuccess ) { + goto loser; + } + + /* decode the user notice */ + rv = SEC_QuickDERDecodeItem(arena, oidSeq, CERT_OidSeqTemplate, &newSeqItem); + + if ( rv != SECSuccess ) { + goto loser; + } + + return(oidSeq); + +loser: + return(NULL); +} + + +void +CERT_DestroyOidSequence(CERTOidSequence *oidSeq) +{ + if ( oidSeq != NULL ) { + PORT_FreeArena(oidSeq->arena, PR_FALSE); + } + return; +} + +PRBool +CERT_GovtApprovedBitSet(CERTCertificate *cert) +{ + SECStatus rv; + SECItem extItem; + CERTOidSequence *oidSeq = NULL; + PRBool ret; + SECItem **oids; + SECItem *oid; + SECOidTag oidTag; + + extItem.data = NULL; + rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE, &extItem); + if ( rv != SECSuccess ) { + goto loser; + } + + oidSeq = CERT_DecodeOidSequence(&extItem); + if ( oidSeq == NULL ) { + goto loser; + } + + oids = oidSeq->oids; + while ( oids != NULL && *oids != NULL ) { + oid = *oids; + + oidTag = SECOID_FindOIDTag(oid); + + if ( oidTag == SEC_OID_NS_KEY_USAGE_GOVT_APPROVED ) { + goto success; + } + + oids++; + } + +loser: + ret = PR_FALSE; + goto done; +success: + ret = PR_TRUE; +done: + if ( oidSeq != NULL ) { + CERT_DestroyOidSequence(oidSeq); + } + if (extItem.data != NULL) { + PORT_Free(extItem.data); + } + return(ret); +} diff --git a/security/nss/lib/certdb/secname.c b/security/nss/lib/certdb/secname.c new file mode 100644 index 000000000..a508dcb44 --- /dev/null +++ b/security/nss/lib/certdb/secname.c @@ -0,0 +1,653 @@ +/* + * 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. + */ + +#include "cert.h" +#include "secoid.h" +#include "secder.h" /* XXX remove this when remove the DERTemplates */ +#include "secasn1.h" +#include "secitem.h" +#include <stdarg.h> +#include "secerr.h" + +static const SEC_ASN1Template cert_AVATemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTAVA) }, + { SEC_ASN1_OBJECT_ID, + offsetof(CERTAVA,type), }, + { SEC_ASN1_ANY, + offsetof(CERTAVA,value), }, + { 0, } +}; + +const SEC_ASN1Template CERT_RDNTemplate[] = { + { SEC_ASN1_SET_OF, + offsetof(CERTRDN,avas), cert_AVATemplate, sizeof(CERTRDN) } +}; + + +static int +CountArray(void **array) +{ + int count = 0; + if (array) { + while (*array++) { + count++; + } + } + return count; +} + +static void +**AddToArray(PRArenaPool *arena, void **array, void *element) +{ + unsigned count; + void **ap; + + /* Count up number of slots already in use in the array */ + count = 0; + ap = array; + if (ap) { + while (*ap++) { + count++; + } + } + + if (array) { + array = (void**) PORT_ArenaGrow(arena, array, + (count + 1) * sizeof(void *), + (count + 2) * sizeof(void *)); + } else { + array = (void**) PORT_ArenaAlloc(arena, (count + 2) * sizeof(void *)); + } + if (array) { + array[count] = element; + array[count+1] = 0; + } + return array; +} + +#if 0 +static void +**RemoveFromArray(void **array, void *element) +{ + unsigned count; + void **ap; + int slot; + + /* Look for element */ + ap = array; + if (ap) { + count = 1; /* count the null at the end */ + slot = -1; + for (; *ap; ap++, count++) { + if (*ap == element) { + /* Found it */ + slot = ap - array; + } + } + if (slot >= 0) { + /* Found it. Squish array down */ + PORT_Memmove((void*) (array + slot), (void*) (array + slot + 1), + (count - slot - 1) * sizeof(void*)); + /* Don't bother reallocing the memory */ + } + } + return array; +} +#endif /* 0 */ + +SECOidTag +CERT_GetAVATag(CERTAVA *ava) +{ + SECOidData *oid; + if (!ava->type.data) return (SECOidTag)-1; + + oid = SECOID_FindOID(&ava->type); + + if ( oid ) { + return(oid->offset); + } + return (SECOidTag)-1; +} + +static SECStatus +SetupAVAType(PRArenaPool *arena, SECOidTag type, SECItem *it, unsigned *maxLenp) +{ + unsigned char *oid; + unsigned oidLen; + unsigned char *cp; + unsigned maxLen; + SECOidData *oidrec; + + oidrec = SECOID_FindOIDByTag(type); + if (oidrec == NULL) + return SECFailure; + + oid = oidrec->oid.data; + oidLen = oidrec->oid.len; + + switch (type) { + case SEC_OID_AVA_COUNTRY_NAME: + maxLen = 2; + break; + case SEC_OID_AVA_ORGANIZATION_NAME: + maxLen = 64; + break; + case SEC_OID_AVA_COMMON_NAME: + maxLen = 64; + break; + case SEC_OID_AVA_LOCALITY: + maxLen = 128; + break; + case SEC_OID_AVA_STATE_OR_PROVINCE: + maxLen = 128; + break; + case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME: + maxLen = 64; + break; + case SEC_OID_AVA_DC: + maxLen = 128; + break; + case SEC_OID_AVA_DN_QUALIFIER: + maxLen = 0x7fff; + break; + case SEC_OID_PKCS9_EMAIL_ADDRESS: + maxLen = 128; + break; + case SEC_OID_RFC1274_UID: + maxLen = 256; /* RFC 1274 specifies 256 */ + break; + case SEC_OID_RFC1274_MAIL: + maxLen = 256; /* RFC 1274 specifies 256 */ + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + it->data = cp = (unsigned char*) PORT_ArenaAlloc(arena, oidLen); + if (cp == NULL) { + return SECFailure; + } + it->len = oidLen; + PORT_Memcpy(cp, oid, oidLen); + *maxLenp = maxLen; + return SECSuccess; +} + +static SECStatus +SetupAVAValue(PRArenaPool *arena, int valueType, char *value, SECItem *it, + unsigned maxLen) +{ + unsigned valueLen, valueLenLen, total; + unsigned ucs4Len = 0, ucs4MaxLen; + unsigned char *cp, *ucs4Val; + + switch (valueType) { + case SEC_ASN1_PRINTABLE_STRING: + case SEC_ASN1_IA5_STRING: + case SEC_ASN1_T61_STRING: + valueLen = PORT_Strlen(value); + break; + case SEC_ASN1_UNIVERSAL_STRING: + valueLen = PORT_Strlen(value); + ucs4Val = (unsigned char *)PORT_ArenaZAlloc(arena, + PORT_Strlen(value) * 6); + ucs4MaxLen = PORT_Strlen(value) * 6; + if(!ucs4Val || !PORT_UCS4_UTF8Conversion(PR_TRUE, (unsigned char *)value, valueLen, + ucs4Val, ucs4MaxLen, &ucs4Len)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + value = (char *)ucs4Val; + valueLen = ucs4Len; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (((valueType != SEC_ASN1_UNIVERSAL_STRING) && (valueLen > maxLen)) || + ((valueType == SEC_ASN1_UNIVERSAL_STRING) && (valueLen > (maxLen * 4)))) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + valueLenLen = DER_LengthLength(valueLen); + total = 1 + valueLenLen + valueLen; + it->data = cp = (unsigned char*) PORT_ArenaAlloc(arena, total); + if (!cp) { + return SECFailure; + } + it->len = total; + cp = (unsigned char*) DER_StoreHeader(cp, valueType, valueLen); + PORT_Memcpy(cp, value, valueLen); + return SECSuccess; +} + +CERTAVA * +CERT_CreateAVA(PRArenaPool *arena, SECOidTag kind, int valueType, char *value) +{ + CERTAVA *ava; + int rv; + unsigned maxLen; + + ava = (CERTAVA*) PORT_ArenaZAlloc(arena, sizeof(CERTAVA)); + if (ava) { + rv = SetupAVAType(arena, kind, &ava->type, &maxLen); + if (rv) { + /* Illegal AVA type */ + return 0; + } + rv = SetupAVAValue(arena, valueType, value, &ava->value, maxLen); + if (rv) { + /* Illegal value type */ + return 0; + } + } + return ava; +} + +CERTAVA * +CERT_CopyAVA(PRArenaPool *arena, CERTAVA *from) +{ + CERTAVA *ava; + int rv; + + ava = (CERTAVA*) PORT_ArenaZAlloc(arena, sizeof(CERTAVA)); + if (ava) { + rv = SECITEM_CopyItem(arena, &ava->type, &from->type); + if (rv) goto loser; + rv = SECITEM_CopyItem(arena, &ava->value, &from->value); + if (rv) goto loser; + } + return ava; + + loser: + return 0; +} + +/************************************************************************/ +/* XXX This template needs to go away in favor of the new SEC_ASN1 version. */ +static const SEC_ASN1Template cert_RDNTemplate[] = { + { SEC_ASN1_SET_OF, + offsetof(CERTRDN,avas), cert_AVATemplate, sizeof(CERTRDN) } +}; + + +CERTRDN * +CERT_CreateRDN(PRArenaPool *arena, CERTAVA *ava0, ...) +{ + CERTAVA *ava; + CERTRDN *rdn; + va_list ap; + unsigned count; + CERTAVA **avap; + + rdn = (CERTRDN*) PORT_ArenaAlloc(arena, sizeof(CERTRDN)); + if (rdn) { + /* Count number of avas going into the rdn */ + count = 0; + if (ava0) { + count++; + va_start(ap, ava0); + while ((ava = va_arg(ap, CERTAVA*)) != 0) { + count++; + } + va_end(ap); + } + + /* Now fill in the pointers */ + rdn->avas = avap = + (CERTAVA**) PORT_ArenaAlloc( arena, (count + 1)*sizeof(CERTAVA*)); + if (!avap) { + return 0; + } + if (ava0) { + *avap++ = ava0; + va_start(ap, ava0); + while ((ava = va_arg(ap, CERTAVA*)) != 0) { + *avap++ = ava; + } + va_end(ap); + } + *avap++ = 0; + } + return rdn; +} + +SECStatus +CERT_AddAVA(PRArenaPool *arena, CERTRDN *rdn, CERTAVA *ava) +{ + rdn->avas = (CERTAVA**) AddToArray(arena, (void**) rdn->avas, ava); + return rdn->avas ? SECSuccess : SECFailure; +} + +SECStatus +CERT_CopyRDN(PRArenaPool *arena, CERTRDN *to, CERTRDN *from) +{ + CERTAVA **avas, *fava, *tava; + SECStatus rv; + + /* Copy each ava from from */ + avas = from->avas; + while ((fava = *avas++) != 0) { + tava = CERT_CopyAVA(arena, fava); + if (!tava) return SECFailure; + rv = CERT_AddAVA(arena, to, tava); + if (rv) return rv; + } + return SECSuccess; +} + +/************************************************************************/ + +const SEC_ASN1Template CERT_NameTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, + offsetof(CERTName,rdns), CERT_RDNTemplate, sizeof(CERTName) } +}; + +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_NameTemplate) + +CERTName * +CERT_CreateName(CERTRDN *rdn0, ...) +{ + CERTRDN *rdn; + CERTName *name; + va_list ap; + unsigned count; + CERTRDN **rdnp; + PRArenaPool *arena; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( !arena ) { + return(0); + } + + name = (CERTName*) PORT_ArenaAlloc(arena, sizeof(CERTName)); + if (name) { + name->arena = arena; + + /* Count number of RDNs going into the Name */ + if (!rdn0) { + count = 0; + } else { + count = 1; + va_start(ap, rdn0); + while ((rdn = va_arg(ap, CERTRDN*)) != 0) { + count++; + } + va_end(ap); + } + + /* Allocate space (including space for terminal null ptr) */ + name->rdns = rdnp = + (CERTRDN**) PORT_ArenaAlloc(arena, (count + 1) * sizeof(CERTRDN*)); + if (!name->rdns) { + goto loser; + } + + /* Now fill in the pointers */ + if (count > 0) { + *rdnp++ = rdn0; + va_start(ap, rdn0); + while ((rdn = va_arg(ap, CERTRDN*)) != 0) { + *rdnp++ = rdn; + } + va_end(ap); + } + + /* null terminate the list */ + *rdnp++ = 0; + } + return name; + +loser: + PORT_FreeArena(arena, PR_FALSE); + return(0); +} + +void +CERT_DestroyName(CERTName *name) +{ + if (name) + { + PRArenaPool *arena = name->arena; + name->rdns = NULL; + name->arena = NULL; + if (arena) PORT_FreeArena(arena, PR_FALSE); + } +} + +SECStatus +CERT_AddRDN(CERTName *name, CERTRDN *rdn) +{ + name->rdns = (CERTRDN**) AddToArray(name->arena, (void**) name->rdns, rdn); + return name->rdns ? SECSuccess : SECFailure; +} + +SECStatus +CERT_CopyName(PRArenaPool *arena, CERTName *to, CERTName *from) +{ + CERTRDN **rdns, *frdn, *trdn; + SECStatus rv; + + if (!to || !from) + return SECFailure; + + CERT_DestroyName(to); + to->arena = arena; + + /* Copy each rdn from from */ + rdns = from->rdns; + while ((frdn = *rdns++) != 0) { + trdn = CERT_CreateRDN(arena, 0); + if ( trdn == NULL ) { + return(SECFailure); + } + rv = CERT_CopyRDN(arena, trdn, frdn); + if (rv) return rv; + rv = CERT_AddRDN(to, trdn); + if (rv) return rv; + } + return SECSuccess; +} + +/************************************************************************/ + +SECComparison +CERT_CompareAVA(CERTAVA *a, CERTAVA *b) +{ + SECComparison rv; + + rv = SECITEM_CompareItem(&a->type, &b->type); + if (rv) { + /* + ** XXX for now we are going to just assume that a bitwise + ** comparison of the value codes will do the trick. + */ + } + rv = SECITEM_CompareItem(&a->value, &b->value); + return rv; +} + +SECComparison +CERT_CompareRDN(CERTRDN *a, CERTRDN *b) +{ + CERTAVA **aavas, *aava; + CERTAVA **bavas, *bava; + int ac, bc; + SECComparison rv = SECEqual; + + aavas = a->avas; + bavas = b->avas; + + /* + ** Make sure array of ava's are the same length. If not, then we are + ** not equal + */ + ac = CountArray((void**) aavas); + bc = CountArray((void**) bavas); + if (ac < bc) return SECLessThan; + if (ac > bc) return SECGreaterThan; + + for (;;) { + aava = *aavas++; + bava = *bavas++; + if (!aava) { + break; + } + rv = CERT_CompareAVA(aava, bava); + if (rv) return rv; + } + return rv; +} + +SECComparison +CERT_CompareName(CERTName *a, CERTName *b) +{ + CERTRDN **ardns, *ardn; + CERTRDN **brdns, *brdn; + int ac, bc; + SECComparison rv = SECEqual; + + ardns = a->rdns; + brdns = b->rdns; + + /* + ** Make sure array of rdn's are the same length. If not, then we are + ** not equal + */ + ac = CountArray((void**) ardns); + bc = CountArray((void**) brdns); + if (ac < bc) return SECLessThan; + if (ac > bc) return SECGreaterThan; + + for (;;) { + ardn = *ardns++; + brdn = *brdns++; + if (!ardn) { + break; + } + rv = CERT_CompareRDN(ardn, brdn); + if (rv) return rv; + } + return rv; +} + +/* Moved from certhtml.c */ +SECItem * +CERT_DecodeAVAValue(SECItem *derAVAValue) +{ + SECItem *retItem; + const SEC_ASN1Template *theTemplate = NULL; + PRBool convertUCS4toUTF8 = PR_FALSE; + PRBool convertUCS2toUTF8 = PR_FALSE; + SECItem avaValue = {siBuffer, 0}; + PLArenaPool *newarena = NULL; + + if(!derAVAValue) { + return NULL; + } + + switch(derAVAValue->data[0]) { + case SEC_ASN1_UNIVERSAL_STRING: + convertUCS4toUTF8 = PR_TRUE; + theTemplate = SEC_UniversalStringTemplate; + break; + case SEC_ASN1_IA5_STRING: + theTemplate = SEC_IA5StringTemplate; + break; + case SEC_ASN1_PRINTABLE_STRING: + theTemplate = SEC_PrintableStringTemplate; + break; + case SEC_ASN1_T61_STRING: + theTemplate = SEC_T61StringTemplate; + break; + case SEC_ASN1_BMP_STRING: + convertUCS2toUTF8 = PR_TRUE; + theTemplate = SEC_BMPStringTemplate; + break; + case SEC_ASN1_UTF8_STRING: + /* No conversion needed ! */ + theTemplate = SEC_UTF8StringTemplate; + break; + default: + return NULL; + } + + PORT_Memset(&avaValue, 0, sizeof(SECItem)); + newarena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!newarena) { + return NULL; + } + if(SEC_QuickDERDecodeItem(newarena, &avaValue, theTemplate, derAVAValue) + != SECSuccess) { + PORT_FreeArena(newarena, PR_FALSE); + return NULL; + } + + if (convertUCS4toUTF8) { + unsigned int utf8ValLen = avaValue.len * 3; + unsigned char *utf8Val = (unsigned char*) + PORT_ArenaZAlloc(newarena, utf8ValLen); + + if(!PORT_UCS4_UTF8Conversion(PR_FALSE, avaValue.data, avaValue.len, + utf8Val, utf8ValLen, &utf8ValLen)) { + PORT_FreeArena(newarena, PR_FALSE); + return NULL; + } + + avaValue.data = utf8Val; + avaValue.len = utf8ValLen; + + } else if (convertUCS2toUTF8) { + + unsigned int utf8ValLen = avaValue.len * 3; + unsigned char *utf8Val = (unsigned char*) + PORT_ArenaZAlloc(newarena, utf8ValLen); + + if(!PORT_UCS2_UTF8Conversion(PR_FALSE, avaValue.data, avaValue.len, + utf8Val, utf8ValLen, &utf8ValLen)) { + PORT_FreeArena(newarena, PR_FALSE); + return NULL; + } + + avaValue.data = utf8Val; + avaValue.len = utf8ValLen; + } + + retItem = SECITEM_DupItem(&avaValue); + PORT_FreeArena(newarena, PR_FALSE); + return retItem; +} diff --git a/security/nss/lib/certdb/stanpcertdb.c b/security/nss/lib/certdb/stanpcertdb.c new file mode 100644 index 000000000..bd905e5fb --- /dev/null +++ b/security/nss/lib/certdb/stanpcertdb.c @@ -0,0 +1,978 @@ +/* + * 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. + */ + +#include "prtime.h" + +#include "cert.h" +#include "mcom_db.h" +#include "certdb.h" +#include "secitem.h" +#include "secder.h" + +/* Call to PK11_FreeSlot below */ + +#include "secasn1.h" +#include "secerr.h" +#include "nssilock.h" +#include "prmon.h" +#include "nsslocks.h" +#include "base64.h" +#include "sechash.h" +#include "plhash.h" +#include "pk11func.h" /* sigh */ + +#ifndef NSS_3_4_CODE +#define NSS_3_4_CODE +#endif /* NSS_3_4_CODE */ +#include "nsspki.h" +#include "pki.h" +#include "pkim.h" +#include "pki3hack.h" +#include "ckhelper.h" +#include "base.h" +#include "pkistore.h" +#include "dev3hack.h" +#include "dev.h" + +PRBool +SEC_CertNicknameConflict(char *nickname, SECItem *derSubject, + CERTCertDBHandle *handle) +{ + CERTCertificate *cert; + PRBool conflict = PR_FALSE; + + cert=CERT_FindCertByNickname(handle, nickname); + + if (!cert) { + return conflict; + } + + conflict = !SECITEM_ItemsAreEqual(derSubject,&cert->derSubject); + CERT_DestroyCertificate(cert); + return conflict; +} + +SECStatus +SEC_DeletePermCertificate(CERTCertificate *cert) +{ + PRStatus nssrv; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSCertificate *c = STAN_GetNSSCertificate(cert); + + /* get rid of the token instances */ + nssrv = NSSCertificate_DeleteStoredObject(c, NULL); + + /* get rid of the cache entry */ + nssTrustDomain_LockCertCache(td); + nssTrustDomain_RemoveCertFromCacheLOCKED(td, c); + nssTrustDomain_UnlockCertCache(td); + + return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; +} + +SECStatus +CERT_GetCertTrust(CERTCertificate *cert, CERTCertTrust *trust) +{ + SECStatus rv; + CERT_LockCertTrust(cert); + if ( cert->trust == NULL ) { + rv = SECFailure; + } else { + *trust = *cert->trust; + rv = SECSuccess; + } + CERT_UnlockCertTrust(cert); + return(rv); +} + +#ifdef notdef +static char * +cert_parseNickname(char *nickname) +{ + char *cp; + for (cp=nickname; *cp && *cp != ':'; cp++); + if (*cp == ':') return cp+1; + return nickname; +} +#endif + +SECStatus +CERT_ChangeCertTrust(CERTCertDBHandle *handle, CERTCertificate *cert, + CERTCertTrust *trust) +{ + SECStatus rv = SECFailure; + PRStatus ret; + + CERT_LockCertTrust(cert); + ret = STAN_ChangeCertTrust(cert, trust); + rv = (ret == PR_SUCCESS) ? SECSuccess : SECFailure; + CERT_UnlockCertTrust(cert); + return rv; +} + +extern const NSSError NSS_ERROR_INVALID_CERTIFICATE; + +SECStatus +__CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname, + CERTCertTrust *trust) +{ + NSSUTF8 *stanNick; + PK11SlotInfo *slot; + NSSToken *internal; + NSSCryptoContext *context; + nssCryptokiObject *permInstance; + NSSCertificate *c = STAN_GetNSSCertificate(cert); + context = c->object.cryptoContext; + if (!context) { + return SECFailure; /* wasn't a temp cert */ + } + stanNick = nssCertificate_GetNickname(c, NULL); + if (stanNick && nickname && strcmp(nickname, stanNick) != 0) { + /* take the new nickname */ + cert->nickname = NULL; + stanNick = NULL; + } + if (!stanNick && nickname) { + stanNick = nssUTF8_Duplicate((NSSUTF8 *)nickname, c->object.arena); + } + /* Delete the temp instance */ + nssCertificateStore_Lock(context->certStore); + nssCertificateStore_RemoveCertLOCKED(context->certStore, c); + nssCertificateStore_Unlock(context->certStore); + c->object.cryptoContext = NULL; + /* Import the perm instance onto the internal token */ + slot = PK11_GetInternalKeySlot(); + internal = PK11Slot_GetNSSToken(slot); + permInstance = nssToken_ImportCertificate(internal, NULL, + NSSCertificateType_PKIX, + &c->id, + stanNick, + &c->encoding, + &c->issuer, + &c->subject, + &c->serial, + cert->emailAddr, + PR_TRUE); + PK11_FreeSlot(slot); + if (!permInstance) { + if (NSS_GetError() == NSS_ERROR_INVALID_CERTIFICATE) { + PORT_SetError(SEC_ERROR_REUSED_ISSUER_AND_SERIAL); + } + return SECFailure; + } + nssPKIObject_AddInstance(&c->object, permInstance); + nssTrustDomain_AddCertsToCache(STAN_GetDefaultTrustDomain(), &c, 1); + /* reset the CERTCertificate fields */ + cert->nssCertificate = NULL; + cert = STAN_GetCERTCertificate(c); /* will return same pointer */ + if (!cert) { + return SECFailure; + } + cert->istemp = PR_FALSE; + cert->isperm = PR_TRUE; + if (!trust) { + return SECSuccess; + } + return (STAN_ChangeCertTrust(cert, trust) == PR_SUCCESS) ? + SECSuccess: SECFailure; +} + +SECStatus +CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname, + CERTCertTrust *trust) +{ + return __CERT_AddTempCertToPerm(cert, nickname, trust); +} + +CERTCertificate * +__CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert, + char *nickname, PRBool isperm, PRBool copyDER) +{ + PRStatus nssrv; + NSSCertificate *c; + CERTCertificate *cc; + NSSCertificate *tempCert; + nssPKIObject *pkio; + NSSCryptoContext *gCC = STAN_GetDefaultCryptoContext(); + NSSTrustDomain *gTD = STAN_GetDefaultTrustDomain(); + if (!isperm) { + NSSDER encoding; + NSSITEM_FROM_SECITEM(&encoding, derCert); + /* First, see if it is already a temp cert */ + c = NSSCryptoContext_FindCertificateByEncodedCertificate(gCC, + &encoding); + if (!c) { + /* Then, see if it is already a perm cert */ + c = NSSTrustDomain_FindCertificateByEncodedCertificate(handle, + &encoding); + /* actually, that search ends up going by issuer/serial, + * so it is still possible to return a cert with the same + * issuer/serial but a different encoding, and we're + * going to reject that + */ + if (c && !nssItem_Equal(&c->encoding, &encoding, NULL)) { + nssCertificate_Destroy(c); + PORT_SetError(SEC_ERROR_REUSED_ISSUER_AND_SERIAL); + return NULL; + } + } + if (c) { + return STAN_GetCERTCertificate(c); + } + } + pkio = nssPKIObject_Create(NULL, NULL, gTD, gCC); + if (!pkio) { + return NULL; + } + c = nss_ZNEW(pkio->arena, NSSCertificate); + if (!c) { + nssPKIObject_Destroy(pkio); + return NULL; + } + c->object = *pkio; + if (copyDER) { + nssItem_Create(c->object.arena, &c->encoding, + derCert->len, derCert->data); + } else { + NSSITEM_FROM_SECITEM(&c->encoding, derCert); + } + /* Forces a decoding of the cert in order to obtain the parts used + * below + */ + cc = STAN_GetCERTCertificate(c); + if (!cc) { + return NULL; + } + nssItem_Create(c->object.arena, + &c->issuer, cc->derIssuer.len, cc->derIssuer.data); + nssItem_Create(c->object.arena, + &c->subject, cc->derSubject.len, cc->derSubject.data); + if (PR_TRUE) { + /* CERTCertificate stores serial numbers decoded. I need the DER + * here. sigh. + */ + SECItem derSerial = { 0 }; + CERT_SerialNumberFromDERCert(&cc->derCert, &derSerial); + if (!derSerial.data) goto loser; + nssItem_Create(c->object.arena, &c->serial, derSerial.len, derSerial.data); + PORT_Free(derSerial.data); + } + if (nickname) { + c->object.tempName = nssUTF8_Create(c->object.arena, + nssStringType_UTF8String, + (NSSUTF8 *)nickname, + PORT_Strlen(nickname)); + } + if (cc->emailAddr) { + c->email = nssUTF8_Create(c->object.arena, + nssStringType_PrintableString, + (NSSUTF8 *)cc->emailAddr, + PORT_Strlen(cc->emailAddr)); + } + /* this function cannot detect if the cert exists as a temp cert now, but + * didn't when CERT_NewTemp was first called. + */ + nssrv = NSSCryptoContext_ImportCertificate(gCC, c); + if (nssrv != PR_SUCCESS) { + goto loser; + } + /* so find the entry in the temp store */ + tempCert = NSSCryptoContext_FindCertificateByIssuerAndSerialNumber(gCC, + &c->issuer, + &c->serial); + /* destroy the copy */ + NSSCertificate_Destroy(c); + if (tempCert) { + /* and use the "official" entry */ + c = tempCert; + cc = STAN_GetCERTCertificate(c); + if (!cc) { + return NULL; + } + } else { + return NULL; + } + cc->istemp = PR_TRUE; + cc->isperm = PR_FALSE; + return cc; +loser: + nssPKIObject_Destroy(&c->object); + return NULL; +} + +CERTCertificate * +CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert, + char *nickname, PRBool isperm, PRBool copyDER) +{ + return( __CERT_NewTempCertificate(handle, derCert, nickname, + isperm, copyDER) ); +} + +/* maybe all the wincx's should be some const for internal token login? */ +CERTCertificate * +CERT_FindCertByIssuerAndSN(CERTCertDBHandle *handle, CERTIssuerAndSN *issuerAndSN) +{ + PK11SlotInfo *slot; + CERTCertificate *cert; + + cert = PK11_FindCertByIssuerAndSN(&slot,issuerAndSN,NULL); + if (cert && slot) { + PK11_FreeSlot(slot); + } + + return cert; +} + +static NSSCertificate * +get_best_temp_or_perm(NSSCertificate *ct, NSSCertificate *cp) +{ + NSSUsage usage; + NSSCertificate *arr[3]; + if (!ct) { + return nssCertificate_AddRef(cp); + } else if (!cp) { + return nssCertificate_AddRef(ct); + } + arr[0] = ct; + arr[1] = cp; + arr[2] = NULL; + usage.anyUsage = PR_TRUE; + return nssCertificateArray_FindBestCertificate(arr, NULL, &usage, NULL); +} + +CERTCertificate * +CERT_FindCertByName(CERTCertDBHandle *handle, SECItem *name) +{ + NSSCertificate *cp, *ct, *c; + NSSDER subject; + NSSUsage usage; + NSSCryptoContext *cc; + NSSITEM_FROM_SECITEM(&subject, name); + usage.anyUsage = PR_TRUE; + cc = STAN_GetDefaultCryptoContext(); + ct = NSSCryptoContext_FindBestCertificateBySubject(cc, &subject, + NULL, &usage, NULL); + cp = NSSTrustDomain_FindBestCertificateBySubject(handle, &subject, + NULL, &usage, NULL); + c = get_best_temp_or_perm(ct, cp); + if (ct) { + CERTCertificate *cert = STAN_GetCERTCertificate(ct); + if (!cert) { + return NULL; + } + CERT_DestroyCertificate(cert); + } + if (cp) { + CERTCertificate *cert = STAN_GetCERTCertificate(cp); + if (!cert) { + return NULL; + } + CERT_DestroyCertificate(cert); + } + if (c) { + return STAN_GetCERTCertificate(c); + } else { + return NULL; + } +} + +CERTCertificate * +CERT_FindCertByKeyID(CERTCertDBHandle *handle, SECItem *name, SECItem *keyID) +{ + CERTCertList *list = + CERT_CreateSubjectCertList(NULL,handle,name,0,PR_FALSE); + CERTCertificate *cert = NULL; + CERTCertListNode *node = CERT_LIST_HEAD(list); + + if (list == NULL) return NULL; + + for (node = CERT_LIST_HEAD(list); node ; node = CERT_LIST_NEXT(node)) { + if (SECITEM_ItemsAreEqual(&cert->subjectKeyID, keyID) ) { + cert = CERT_DupCertificate(node->cert); + break; + } + } + return cert; +} + +CERTCertificate * +CERT_FindCertByNickname(CERTCertDBHandle *handle, char *nickname) +{ + NSSCryptoContext *cc; + NSSCertificate *c, *ct; + CERTCertificate *cert; + NSSUsage usage; + usage.anyUsage = PR_TRUE; + cc = STAN_GetDefaultCryptoContext(); + ct = NSSCryptoContext_FindBestCertificateByNickname(cc, nickname, + NULL, &usage, NULL); + cert = PK11_FindCertFromNickname(nickname, NULL); + c = NULL; + if (cert) { + c = get_best_temp_or_perm(ct, STAN_GetNSSCertificate(cert)); + CERT_DestroyCertificate(cert); + if (ct) { + CERTCertificate *cert2 = STAN_GetCERTCertificate(ct); + if (!cert2) { + return NULL; + } + CERT_DestroyCertificate(cert2); + } + } else { + c = ct; + } + if (c) { + return STAN_GetCERTCertificate(c); + } else { + return NULL; + } +} + +CERTCertificate * +CERT_FindCertByDERCert(CERTCertDBHandle *handle, SECItem *derCert) +{ + NSSCryptoContext *cc; + NSSCertificate *c; + NSSDER encoding; + NSSITEM_FROM_SECITEM(&encoding, derCert); + cc = STAN_GetDefaultCryptoContext(); + c = NSSCryptoContext_FindCertificateByEncodedCertificate(cc, &encoding); + if (!c) { + c = NSSTrustDomain_FindCertificateByEncodedCertificate(handle, + &encoding); + if (!c) return NULL; + } + return STAN_GetCERTCertificate(c); +} + +CERTCertificate * +CERT_FindCertByNicknameOrEmailAddr(CERTCertDBHandle *handle, char *name) +{ + NSSCryptoContext *cc; + NSSCertificate *c, *ct; + CERTCertificate *cert; + NSSUsage usage; + usage.anyUsage = PR_TRUE; + cc = STAN_GetDefaultCryptoContext(); + ct = NSSCryptoContext_FindBestCertificateByNickname(cc, name, + NULL, &usage, NULL); + if (!ct) { + ct = NSSCryptoContext_FindBestCertificateByEmail(cc, name, + NULL, &usage, NULL); + } + cert = PK11_FindCertFromNickname(name, NULL); + if (cert) { + c = get_best_temp_or_perm(ct, STAN_GetNSSCertificate(cert)); + CERT_DestroyCertificate(cert); + if (ct) { + CERTCertificate *cert2 = STAN_GetCERTCertificate(ct); + if (!cert2) { + return NULL; + } + CERT_DestroyCertificate(cert2); + } + } else { + c = ct; + } + if (c) { + return STAN_GetCERTCertificate(c); + } + return NULL; +} + +static void +add_to_subject_list(CERTCertList *certList, CERTCertificate *cert, + PRBool validOnly, int64 sorttime) +{ + SECStatus secrv; + if (!validOnly || + CERT_CheckCertValidTimes(cert, sorttime, PR_FALSE) + == secCertTimeValid) { + secrv = CERT_AddCertToListSorted(certList, cert, + CERT_SortCBValidity, + (void *)&sorttime); + if (secrv != SECSuccess) { + CERT_DestroyCertificate(cert); + } + } else { + CERT_DestroyCertificate(cert); + } +} + +CERTCertList * +CERT_CreateSubjectCertList(CERTCertList *certList, CERTCertDBHandle *handle, + SECItem *name, int64 sorttime, PRBool validOnly) +{ + NSSCryptoContext *cc; + NSSCertificate **tSubjectCerts, **pSubjectCerts; + NSSCertificate **ci; + CERTCertificate *cert; + NSSDER subject; + PRBool myList = PR_FALSE; + cc = STAN_GetDefaultCryptoContext(); + NSSITEM_FROM_SECITEM(&subject, name); + /* Collect both temp and perm certs for the subject */ + tSubjectCerts = NSSCryptoContext_FindCertificatesBySubject(cc, + &subject, + NULL, + 0, + NULL); + pSubjectCerts = NSSTrustDomain_FindCertificatesBySubject(handle, + &subject, + NULL, + 0, + NULL); + if (!tSubjectCerts && !pSubjectCerts) { + return NULL; + } + if (certList == NULL) { + certList = CERT_NewCertList(); + myList = PR_TRUE; + if (!certList) goto loser; + } + /* Iterate over the matching temp certs. Add them to the list */ + ci = tSubjectCerts; + while (ci && *ci) { + cert = STAN_GetCERTCertificate(*ci); + if (cert) { + add_to_subject_list(certList, cert, validOnly, sorttime); + } + ci++; + } + /* Iterate over the matching perm certs. Add them to the list */ + ci = pSubjectCerts; + while (ci && *ci) { + cert = STAN_GetCERTCertificate(*ci); + if (cert) { + add_to_subject_list(certList, cert, validOnly, sorttime); + } + ci++; + } + nss_ZFreeIf(tSubjectCerts); + nss_ZFreeIf(pSubjectCerts); + return certList; +loser: + nss_ZFreeIf(tSubjectCerts); + nss_ZFreeIf(pSubjectCerts); + if (myList && certList != NULL) { + CERT_DestroyCertList(certList); + } + return NULL; +} + +void +CERT_DestroyCertificate(CERTCertificate *cert) +{ + if ( cert ) { + /* don't use STAN_GetNSSCertificate because we don't want to + * go to the trouble of translating the CERTCertificate into + * an NSSCertificate just to destroy it. If it hasn't been done + * yet, don't do it at all. + */ + NSSCertificate *tmp = cert->nssCertificate; + if (tmp) { + /* delete the NSSCertificate */ + NSSCertificate_Destroy(tmp); + } else { + PORT_FreeArena(cert->arena, PR_FALSE); + } + } + return; +} + +#ifdef notdef +SECStatus +CERT_ChangeCertTrustByUsage(CERTCertDBHandle *certdb, + CERTCertificate *cert, SECCertUsage usage) +{ + SECStatus rv; + CERTCertTrust trust; + CERTCertTrust tmptrust; + unsigned int certtype; + PRBool saveit; + + saveit = PR_TRUE; + + PORT_Memset((void *)&trust, 0, sizeof(trust)); + + certtype = cert->nsCertType; + + /* if no app bits in cert type, then set all app bits */ + if ( ! ( certtype & NS_CERT_TYPE_APP ) ) { + certtype |= NS_CERT_TYPE_APP; + } + + switch ( usage ) { + case certUsageEmailSigner: + case certUsageEmailRecipient: + if ( certtype & NS_CERT_TYPE_EMAIL ) { + trust.emailFlags = CERTDB_VALID_PEER; + if ( ! ( cert->rawKeyUsage & KU_KEY_ENCIPHERMENT ) ) { + /* don't save it if KeyEncipherment is not allowed */ + saveit = PR_FALSE; + } + } + break; + case certUsageUserCertImport: + if ( certtype & NS_CERT_TYPE_EMAIL ) { + trust.emailFlags = CERTDB_VALID_PEER; + } + /* VALID_USER is already set if the cert was imported, + * in the case that the cert was already in the database + * through SMIME or other means, we should set the USER + * flags, if they are not already set. + */ + if( cert->isperm ) { + if ( certtype & NS_CERT_TYPE_SSL_CLIENT ) { + if( !(cert->trust->sslFlags & CERTDB_USER) ) { + trust.sslFlags |= CERTDB_USER; + } + } + + if ( certtype & NS_CERT_TYPE_EMAIL ) { + if( !(cert->trust->emailFlags & CERTDB_USER) ) { + trust.emailFlags |= CERTDB_USER; + } + } + + if ( certtype & NS_CERT_TYPE_OBJECT_SIGNING ) { + if( !(cert->trust->objectSigningFlags & CERTDB_USER) ) { + trust.objectSigningFlags |= CERTDB_USER; + } + } + } + break; + default: /* XXX added to quiet warnings; no other cases needed? */ + break; + } + + if ( (trust.sslFlags | trust.emailFlags | trust.objectSigningFlags) == 0 ){ + saveit = PR_FALSE; + } + + if ( saveit && cert->isperm ) { + /* Cert already in the DB. Just adjust flags */ + tmptrust = *cert->trust; + tmptrust.sslFlags |= trust.sslFlags; + tmptrust.emailFlags |= trust.emailFlags; + tmptrust.objectSigningFlags |= trust.objectSigningFlags; + + rv = CERT_ChangeCertTrust(cert->dbhandle, cert, + &tmptrust); + if ( rv != SECSuccess ) { + goto loser; + } + } + + rv = SECSuccess; + goto done; + +loser: + rv = SECFailure; +done: + + return(rv); +} +#endif + +int +CERT_GetDBContentVersion(CERTCertDBHandle *handle) +{ + /* should read the DB content version from the pkcs #11 device */ + return 0; +} + +SECStatus +certdb_SaveSingleProfile(CERTCertificate *cert, const char *emailAddr, + SECItem *emailProfile, SECItem *profileTime) +{ + int64 oldtime; + int64 newtime; + SECStatus rv = SECFailure; + PRBool saveit; + SECItem oldprof, oldproftime; + SECItem *oldProfile = NULL; + SECItem *oldProfileTime = NULL; + PK11SlotInfo *slot = NULL; + NSSCertificate *c; + NSSCryptoContext *cc; + nssSMIMEProfile *stanProfile = NULL; + PRBool freeOldProfile = PR_FALSE; + + c = STAN_GetNSSCertificate(cert); + if (!c) return SECFailure; + cc = c->object.cryptoContext; + if (cc != NULL) { + stanProfile = nssCryptoContext_FindSMIMEProfileForCertificate(cc, c); + if (stanProfile) { + PORT_Assert(stanProfile->profileData); + SECITEM_FROM_NSSITEM(&oldprof, stanProfile->profileData); + oldProfile = &oldprof; + SECITEM_FROM_NSSITEM(&oldproftime, stanProfile->profileTime); + oldProfileTime = &oldproftime; + } + } else { + oldProfile = PK11_FindSMimeProfile(&slot, (char *)emailAddr, + &cert->derSubject, &oldProfileTime); + freeOldProfile = PR_TRUE; + } + + saveit = PR_FALSE; + + /* both profileTime and emailProfile have to exist or not exist */ + if ( emailProfile == NULL ) { + profileTime = NULL; + } else if ( profileTime == NULL ) { + emailProfile = NULL; + } + + if ( oldProfileTime == NULL ) { + saveit = PR_TRUE; + } else { + /* there was already a profile for this email addr */ + if ( profileTime ) { + /* we have an old and new profile - save whichever is more recent*/ + if ( oldProfileTime->len == 0 ) { + /* always replace if old entry doesn't have a time */ + oldtime = LL_MININT; + } else { + rv = DER_UTCTimeToTime(&oldtime, oldProfileTime); + if ( rv != SECSuccess ) { + goto loser; + } + } + + rv = DER_UTCTimeToTime(&newtime, profileTime); + if ( rv != SECSuccess ) { + goto loser; + } + + if ( LL_CMP(newtime, >, oldtime ) ) { + /* this is a newer profile, save it and cert */ + saveit = PR_TRUE; + } + } else { + saveit = PR_TRUE; + } + } + + + if (saveit) { + if (cc) { + if (stanProfile) { + /* stanProfile is already stored in the crypto context, + * overwrite the data + */ + NSSArena *arena = stanProfile->object.arena; + stanProfile->profileTime = nssItem_Create(arena, + NULL, + profileTime->len, + profileTime->data); + stanProfile->profileData = nssItem_Create(arena, + NULL, + emailProfile->len, + emailProfile->data); + } else if (profileTime && emailProfile) { + PRStatus nssrv; + NSSDER subject; + NSSItem profTime, profData; + NSSItem *pprofTime, *pprofData; + NSSITEM_FROM_SECITEM(&subject, &cert->derSubject); + if (profileTime) { + NSSITEM_FROM_SECITEM(&profTime, profileTime); + pprofTime = &profTime; + } else { + pprofTime = NULL; + } + if (emailProfile) { + NSSITEM_FROM_SECITEM(&profData, emailProfile); + pprofData = &profData; + } else { + pprofData = NULL; + } + stanProfile = nssSMIMEProfile_Create(c, pprofTime, pprofData); + if (!stanProfile) goto loser; + nssrv = nssCryptoContext_ImportSMIMEProfile(cc, stanProfile); + rv = (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; + } + } else { + rv = PK11_SaveSMimeProfile(slot, (char *)emailAddr, + &cert->derSubject, emailProfile, profileTime); + } + } else { + rv = SECSuccess; + } + +loser: + if (oldProfile && freeOldProfile) { + SECITEM_FreeItem(oldProfile,PR_TRUE); + } + if (oldProfileTime && freeOldProfile) { + SECITEM_FreeItem(oldProfileTime,PR_TRUE); + } + if (stanProfile) { + nssSMIMEProfile_Destroy(stanProfile); + } + if (slot) { + PK11_FreeSlot(slot); + } + + return(rv); +} + +/* + * + * Manage S/MIME profiles + * + */ + +SECStatus +CERT_SaveSMimeProfile(CERTCertificate *cert, SECItem *emailProfile, + SECItem *profileTime) +{ + const char *emailAddr; + SECStatus rv; + + if (!cert) { + return SECFailure; + } + + if (cert->slot && !PK11_IsInternal(cert->slot)) { + /* this cert comes from an external source, we need to add it + to the cert db before creating an S/MIME profile */ + PK11SlotInfo* internalslot = PK11_GetInternalKeySlot(); + if (!internalslot) { + return SECFailure; + } + rv = PK11_ImportCert(internalslot, cert, + CK_INVALID_HANDLE, NULL, PR_FALSE); + + PK11_FreeSlot(internalslot); + if (rv != SECSuccess ) { + return SECFailure; + } + } + + + for (emailAddr = CERT_GetFirstEmailAddress(cert); emailAddr != NULL; + emailAddr = CERT_GetNextEmailAddress(cert,emailAddr)) { + rv = certdb_SaveSingleProfile(cert,emailAddr,emailProfile,profileTime); + if (rv != SECSuccess) { + return SECFailure; + } + } + return SECSuccess; + +} + + +SECItem * +CERT_FindSMimeProfile(CERTCertificate *cert) +{ + PK11SlotInfo *slot = NULL; + NSSCertificate *c; + NSSCryptoContext *cc; + SECItem *rvItem = NULL; + + c = STAN_GetNSSCertificate(cert); + if (!c) return NULL; + cc = c->object.cryptoContext; + if (cc != NULL) { + nssSMIMEProfile *stanProfile; + stanProfile = nssCryptoContext_FindSMIMEProfileForCertificate(cc, c); + if (stanProfile) { + rvItem = SECITEM_AllocItem(NULL, NULL, + stanProfile->profileData->size); + if (rvItem) { + rvItem->data = stanProfile->profileData->data; + } + nssSMIMEProfile_Destroy(stanProfile); + } + return rvItem; + } + rvItem = + PK11_FindSMimeProfile(&slot, cert->emailAddr, &cert->derSubject, NULL); + if (slot) { + PK11_FreeSlot(slot); + } + return rvItem; +} + +/* + * depricated functions that are now just stubs. + */ +/* + * Close the database + */ +void +__CERT_ClosePermCertDB(CERTCertDBHandle *handle) +{ + PORT_Assert("CERT_ClosePermCertDB is Depricated" == NULL); + return; +} + +SECStatus +CERT_OpenCertDBFilename(CERTCertDBHandle *handle, char *certdbname, + PRBool readOnly) +{ + PORT_Assert("CERT_OpenCertDBFilename is Depricated" == NULL); + return SECFailure; +} + +SECItem * +SECKEY_HashPassword(char *pw, SECItem *salt) +{ + PORT_Assert("SECKEY_HashPassword is Depricated" == NULL); + return NULL; +} + +SECStatus +__CERT_TraversePermCertsForSubject(CERTCertDBHandle *handle, + SECItem *derSubject, + void *cb, void *cbarg) +{ + PORT_Assert("CERT_TraversePermCertsForSubject is Depricated" == NULL); + return SECFailure; +} + + +SECStatus +__CERT_TraversePermCertsForNickname(CERTCertDBHandle *handle, char *nickname, + void *cb, void *cbarg) +{ + PORT_Assert("CERT_TraversePermCertsForNickname is Depricated" == NULL); + return SECFailure; +} + + + diff --git a/security/nss/lib/certdb/xauthkid.c b/security/nss/lib/certdb/xauthkid.c new file mode 100644 index 000000000..089d16f14 --- /dev/null +++ b/security/nss/lib/certdb/xauthkid.c @@ -0,0 +1,155 @@ +/* + * 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. + */ + +/* + * X.509 v3 Subject Key Usage Extension + * + */ + +#include "prtypes.h" +#include "mcom_db.h" +#include "seccomon.h" +#include "secdert.h" +#include "secoidt.h" +#include "secasn1t.h" +#include "secasn1.h" +#include "secport.h" +#include "certt.h" +#include "genname.h" +#include "secerr.h" + + +const SEC_ASN1Template CERTAuthKeyIDTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTAuthKeyID) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(CERTAuthKeyID,keyID), SEC_OctetStringTemplate}, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(CERTAuthKeyID, DERAuthCertIssuer), CERT_GeneralNamesTemplate}, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 2, + offsetof(CERTAuthKeyID,authCertSerialNumber), SEC_IntegerTemplate}, + { 0 } +}; + + + +SECStatus CERT_EncodeAuthKeyID (PRArenaPool *arena, CERTAuthKeyID *value, SECItem *encodedValue) +{ + SECStatus rv = SECFailure; + + PORT_Assert (value); + PORT_Assert (arena); + PORT_Assert (value->DERAuthCertIssuer == NULL); + PORT_Assert (encodedValue); + + do { + + /* If both of the authCertIssuer and the serial number exist, encode + the name first. Otherwise, it is an error if one exist and the other + is not. + */ + if (value->authCertIssuer) { + if (!value->authCertSerialNumber.data) { + PORT_SetError (SEC_ERROR_EXTENSION_VALUE_INVALID); + break; + } + + value->DERAuthCertIssuer = cert_EncodeGeneralNames + (arena, value->authCertIssuer); + if (!value->DERAuthCertIssuer) { + PORT_SetError (SEC_ERROR_EXTENSION_VALUE_INVALID); + break; + } + } + else if (value->authCertSerialNumber.data) { + PORT_SetError (SEC_ERROR_EXTENSION_VALUE_INVALID); + break; + } + + if (SEC_ASN1EncodeItem (arena, encodedValue, value, + CERTAuthKeyIDTemplate) == NULL) + break; + rv = SECSuccess; + + } while (0); + return(rv); +} + +CERTAuthKeyID * +CERT_DecodeAuthKeyID (PRArenaPool *arena, SECItem *encodedValue) +{ + CERTAuthKeyID * value = NULL; + SECStatus rv = SECFailure; + void * mark; + SECItem newEncodedValue; + + PORT_Assert (arena); + + do { + mark = PORT_ArenaMark (arena); + value = (CERTAuthKeyID*)PORT_ArenaZAlloc (arena, sizeof (*value)); + value->DERAuthCertIssuer = NULL; + if (value == NULL) + break; + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newEncodedValue, encodedValue); + if ( rv != SECSuccess ) { + break; + } + + rv = SEC_QuickDERDecodeItem + (arena, value, CERTAuthKeyIDTemplate, &newEncodedValue); + if (rv != SECSuccess) + break; + + value->authCertIssuer = cert_DecodeGeneralNames (arena, value->DERAuthCertIssuer); + if (value->authCertIssuer == NULL) + break; + + /* what if the general name contains other format but not URI ? + hl + */ + if ((value->authCertSerialNumber.data && !value->authCertIssuer) || + (!value->authCertSerialNumber.data && value->authCertIssuer)){ + PORT_SetError (SEC_ERROR_EXTENSION_VALUE_INVALID); + break; + } + } while (0); + + if (rv != SECSuccess) { + PORT_ArenaRelease (arena, mark); + return ((CERTAuthKeyID *)NULL); + } + PORT_ArenaUnmark(arena, mark); + return (value); +} diff --git a/security/nss/lib/certdb/xbsconst.c b/security/nss/lib/certdb/xbsconst.c new file mode 100644 index 000000000..fea0e5dc8 --- /dev/null +++ b/security/nss/lib/certdb/xbsconst.c @@ -0,0 +1,168 @@ +/* + * 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. + */ + +/* + * X.509 v3 Basic Constraints Extension + */ + +#include "prtypes.h" +#include "mcom_db.h" +#include "seccomon.h" +#include "secdert.h" +#include "secoidt.h" +#include "secasn1t.h" +#include "secasn1.h" +#include "certt.h" +#include "secder.h" +#include "prprf.h" +#include "secerr.h" + +typedef struct EncodedContext{ + SECItem isCA; + SECItem pathLenConstraint; + SECItem encodedValue; + PRArenaPool *arena; +}EncodedContext; + +static const SEC_ASN1Template CERTBasicConstraintsTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(EncodedContext) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */ + offsetof(EncodedContext,isCA)}, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, + offsetof(EncodedContext,pathLenConstraint) }, + { 0, } +}; + +static unsigned char hexTrue = 0xff; +static unsigned char hexFalse = 0x00; + +#define GEN_BREAK(status) rv = status; break; + +SECStatus CERT_EncodeBasicConstraintValue + (PRArenaPool *arena, CERTBasicConstraints *value, SECItem *encodedValue) +{ + EncodedContext encodeContext; + PRArenaPool *our_pool = NULL; + SECStatus rv = SECSuccess; + + do { + PORT_Memset (&encodeContext, 0, sizeof (encodeContext)); + if (!value->isCA && value->pathLenConstraint >= 0) { + PORT_SetError (SEC_ERROR_EXTENSION_VALUE_INVALID); + GEN_BREAK (SECFailure); + } + + encodeContext.arena = arena; + if (value->isCA == PR_TRUE) { + encodeContext.isCA.data = &hexTrue ; + encodeContext.isCA.len = 1; + } + + /* If the pathLenConstraint is less than 0, then it should be + * omitted from the encoding. + */ + if (value->isCA && value->pathLenConstraint >= 0) { + our_pool = PORT_NewArena (SEC_ASN1_DEFAULT_ARENA_SIZE); + if (our_pool == NULL) { + PORT_SetError (SEC_ERROR_NO_MEMORY); + GEN_BREAK (SECFailure); + } + if (SEC_ASN1EncodeUnsignedInteger + (our_pool, &encodeContext.pathLenConstraint, + (unsigned long)value->pathLenConstraint) == NULL) { + PORT_SetError (SEC_ERROR_NO_MEMORY); + GEN_BREAK (SECFailure); + } + } + if (SEC_ASN1EncodeItem (arena, encodedValue, &encodeContext, + CERTBasicConstraintsTemplate) == NULL) { + GEN_BREAK (SECFailure); + } + } while (0); + if (our_pool) + PORT_FreeArena (our_pool, PR_FALSE); + return(rv); + +} + +SECStatus CERT_DecodeBasicConstraintValue + (CERTBasicConstraints *value, SECItem *encodedValue) +{ + EncodedContext decodeContext; + PRArenaPool *our_pool; + SECStatus rv = SECSuccess; + + do { + PORT_Memset (&decodeContext, 0, sizeof (decodeContext)); + /* initialize the value just in case we got "0x30 00", or when the + pathLenConstraint is omitted. + */ + decodeContext.isCA.data =&hexFalse; + decodeContext.isCA.len = 1; + + our_pool = PORT_NewArena (SEC_ASN1_DEFAULT_ARENA_SIZE); + if (our_pool == NULL) { + PORT_SetError (SEC_ERROR_NO_MEMORY); + GEN_BREAK (SECFailure); + } + + rv = SEC_QuickDERDecodeItem + (our_pool, &decodeContext, CERTBasicConstraintsTemplate, encodedValue); + if (rv == SECFailure) + break; + + value->isCA = (PRBool)(*decodeContext.isCA.data); + if (decodeContext.pathLenConstraint.data == NULL) { + /* if the pathLenConstraint is not encoded, and the current setting + is CA, then the pathLenConstraint should be set to a negative number + for unlimited certificate path. + */ + if (value->isCA) + value->pathLenConstraint = CERT_UNLIMITED_PATH_CONSTRAINT; + } + else if (value->isCA) + value->pathLenConstraint = DER_GetUInteger (&decodeContext.pathLenConstraint); + else { + /* here we get an error where the subject is not a CA, but + the pathLenConstraint is set */ + PORT_SetError (SEC_ERROR_BAD_DER); + GEN_BREAK (SECFailure); + break; + } + + } while (0); + PORT_FreeArena (our_pool, PR_FALSE); + return (rv); + +} diff --git a/security/nss/lib/certdb/xconst.c b/security/nss/lib/certdb/xconst.c new file mode 100644 index 000000000..d86483098 --- /dev/null +++ b/security/nss/lib/certdb/xconst.c @@ -0,0 +1,257 @@ +/* + * 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. + */ + +/* + * X.509 Extension Encoding + */ + +#include "prtypes.h" +#include "mcom_db.h" +#include "seccomon.h" +#include "secdert.h" +#include "secoidt.h" +#include "secasn1t.h" +#include "secasn1.h" +#include "certt.h" +#include "secder.h" +#include "prprf.h" +#include "xconst.h" +#include "genname.h" +#include "secasn1.h" +#include "secerr.h" + + +static const SEC_ASN1Template CERTSubjectKeyIDTemplate[] = { + { SEC_ASN1_OCTET_STRING } +}; + + +static const SEC_ASN1Template CERTIA5TypeTemplate[] = { + { SEC_ASN1_IA5_STRING } +}; + + +static const SEC_ASN1Template CERTPrivateKeyUsagePeriodTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(PKUPEncodedContext) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(PKUPEncodedContext, notBefore), SEC_GeneralizedTimeTemplate}, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(PKUPEncodedContext, notAfter), SEC_GeneralizedTimeTemplate}, + { 0, } +}; + + +const SEC_ASN1Template CERTAltNameTemplate[] = { + { SEC_ASN1_CONSTRUCTED, offsetof(AltNameEncodedContext, encodedGenName), + CERT_GeneralNamesTemplate} +}; + +const SEC_ASN1Template CERTAuthInfoAccessItemTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTAuthInfoAccess) }, + { SEC_ASN1_OBJECT_ID, + offsetof(CERTAuthInfoAccess, method) }, + { SEC_ASN1_ANY, + offsetof(CERTAuthInfoAccess, derLocation) }, + { 0, } +}; + +const SEC_ASN1Template CERTAuthInfoAccessTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, 0, CERTAuthInfoAccessItemTemplate } +}; + + +SECStatus +CERT_EncodeSubjectKeyID(PRArenaPool *arena, char *value, int len, SECItem *encodedValue) +{ + SECItem encodeContext; + SECStatus rv = SECSuccess; + + + PORT_Memset (&encodeContext, 0, sizeof (encodeContext)); + + if (value != NULL) { + encodeContext.data = (unsigned char *)value; + encodeContext.len = len; + } + if (SEC_ASN1EncodeItem (arena, encodedValue, &encodeContext, + CERTSubjectKeyIDTemplate) == NULL) { + rv = SECFailure; + } + + return(rv); +} + + +SECStatus +CERT_EncodePublicKeyUsagePeriod(PRArenaPool *arena, PKUPEncodedContext *pkup, SECItem *encodedValue) +{ + SECStatus rv = SECSuccess; + + if (SEC_ASN1EncodeItem (arena, encodedValue, pkup, + CERTPrivateKeyUsagePeriodTemplate) == NULL) { + rv = SECFailure; + } + return(rv); +} + + +SECStatus +CERT_EncodeIA5TypeExtension(PRArenaPool *arena, char *value, SECItem *encodedValue) +{ + SECItem encodeContext; + SECStatus rv = SECSuccess; + + + PORT_Memset (&encodeContext, 0, sizeof (encodeContext)); + + if (value != NULL) { + encodeContext.data = (unsigned char *)value; + encodeContext.len = strlen(value); + } + if (SEC_ASN1EncodeItem (arena, encodedValue, &encodeContext, + CERTIA5TypeTemplate) == NULL) { + rv = SECFailure; + } + + return(rv); +} + +SECStatus +CERT_EncodeAltNameExtension(PRArenaPool *arena, CERTGeneralName *value, SECItem *encodedValue) +{ + SECItem **encodedGenName; + SECStatus rv = SECSuccess; + + encodedGenName = cert_EncodeGeneralNames(arena, value); + if (SEC_ASN1EncodeItem (arena, encodedValue, &encodedGenName, + CERT_GeneralNamesTemplate) == NULL) { + rv = SECFailure; + } + + return rv; +} + +CERTGeneralName * +CERT_DecodeAltNameExtension(PRArenaPool *arena, SECItem *EncodedAltName) +{ + SECStatus rv = SECSuccess; + AltNameEncodedContext encodedContext; + + encodedContext.encodedGenName = NULL; + PORT_Memset(&encodedContext, 0, sizeof(AltNameEncodedContext)); + rv = SEC_ASN1DecodeItem (arena, &encodedContext, CERT_GeneralNamesTemplate, + EncodedAltName); + if (rv == SECFailure) { + goto loser; + } + if (encodedContext.encodedGenName) + return cert_DecodeGeneralNames(arena, encodedContext.encodedGenName); + /* Extension contained an empty GeneralNames sequence */ + /* Treat as extension not found */ + PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); +loser: + return NULL; +} + + +SECStatus +CERT_EncodeNameConstraintsExtension(PRArenaPool *arena, + CERTNameConstraints *value, + SECItem *encodedValue) +{ + SECStatus rv = SECSuccess; + + rv = cert_EncodeNameConstraints(value, arena, encodedValue); + return rv; +} + + +CERTNameConstraints * +CERT_DecodeNameConstraintsExtension(PRArenaPool *arena, + SECItem *encodedConstraints) +{ + return cert_DecodeNameConstraints(arena, encodedConstraints); +} + + +CERTAuthInfoAccess ** +cert_DecodeAuthInfoAccessExtension(PRArenaPool *arena, + SECItem *encodedExtension) +{ + CERTAuthInfoAccess **info = NULL; + SECStatus rv; + int i; + + rv = SEC_ASN1DecodeItem(arena, &info, CERTAuthInfoAccessTemplate, + encodedExtension); + if (rv != SECSuccess || info == NULL) { + return NULL; + } + + for (i = 0; info[i] != NULL; i++) { + info[i]->location = CERT_DecodeGeneralName(arena, + &(info[i]->derLocation), + NULL); + } + return info; +} + +SECStatus +cert_EncodeAuthInfoAccessExtension(PRArenaPool *arena, + CERTAuthInfoAccess **info, + SECItem *dest) +{ + SECItem *dummy; + int i; + + PORT_Assert(info != NULL); + PORT_Assert(dest != NULL); + if (info == NULL || dest == NULL) { + return SECFailure; + } + + for (i = 0; info[i] != NULL; i++) { + if (CERT_EncodeGeneralName(info[i]->location, &(info[i]->derLocation), + arena) == NULL) + /* Note that this may leave some of the locations filled in. */ + return SECFailure; + } + dummy = SEC_ASN1EncodeItem(arena, dest, &info, + CERTAuthInfoAccessTemplate); + if (dummy == NULL) { + return SECFailure; + } + return SECSuccess; +} diff --git a/security/nss/lib/certdb/xconst.h b/security/nss/lib/certdb/xconst.h new file mode 100644 index 000000000..e615fa3b4 --- /dev/null +++ b/security/nss/lib/certdb/xconst.h @@ -0,0 +1,82 @@ +/* + * 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. + */ + +#include "certt.h" + +typedef struct PKUPEncodedContext{ + SECItem notBefore; + SECItem notAfter; + /* SECItem encodedValue; */ + PRArenaPool *arena; +}PKUPEncodedContext; + +typedef struct AltNameEncodedContext{ + SECItem **encodedGenName; +}AltNameEncodedContext; + + +typedef struct NameConstraint{ + CERTGeneralName generalName; + int min; + int max; +}NameConstraint; + + + +extern SECStatus +CERT_EncodePublicKeyUsagePeriod(PRArenaPool *arena, PKUPEncodedContext *pkup, + SECItem *encodedValue); + +extern SECStatus +CERT_EncodeNameConstraintsExtension(PRArenaPool *arena, CERTNameConstraints *value, + SECItem *encodedValue); +extern CERTGeneralName * +CERT_DecodeAltNameExtension(PRArenaPool *arena, SECItem *EncodedAltName); + +extern CERTNameConstraints * +CERT_DecodeNameConstraintsExtension(PRArenaPool *arena, SECItem *encodedConstraints); + +extern SECStatus +CERT_EncodeSubjectKeyID(PRArenaPool *arena, char *value, int len, SECItem *encodedValue); + +extern SECStatus +CERT_EncodeIA5TypeExtension(PRArenaPool *arena, char *value, SECItem *encodedValue); + +CERTAuthInfoAccess ** +cert_DecodeAuthInfoAccessExtension(PRArenaPool *arena, + SECItem *encodedExtension); + +SECStatus +cert_EncodeAuthInfoAccessExtension(PRArenaPool *arena, + CERTAuthInfoAccess **info, + SECItem *dest); |