summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/certdb
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/lib/certdb
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/lib/certdb')
-rw-r--r--security/nss/lib/certdb/Makefile45
-rw-r--r--security/nss/lib/certdb/alg1485.c1590
-rw-r--r--security/nss/lib/certdb/cert.h1606
-rw-r--r--security/nss/lib/certdb/certdb.c3411
-rw-r--r--security/nss/lib/certdb/certdb.gyp34
-rw-r--r--security/nss/lib/certdb/certdb.h89
-rw-r--r--security/nss/lib/certdb/certi.h407
-rw-r--r--security/nss/lib/certdb/certt.h1359
-rw-r--r--security/nss/lib/certdb/certv3.c222
-rw-r--r--security/nss/lib/certdb/certxutl.c496
-rw-r--r--security/nss/lib/certdb/certxutl.h44
-rw-r--r--security/nss/lib/certdb/crl.c3046
-rw-r--r--security/nss/lib/certdb/exports.gyp36
-rw-r--r--security/nss/lib/certdb/genname.c2011
-rw-r--r--security/nss/lib/certdb/genname.h93
-rw-r--r--security/nss/lib/certdb/manifest.mn41
-rw-r--r--security/nss/lib/certdb/polcyxtn.c806
-rw-r--r--security/nss/lib/certdb/secname.c701
-rw-r--r--security/nss/lib/certdb/stanpcertdb.c1137
-rw-r--r--security/nss/lib/certdb/xauthkid.c128
-rw-r--r--security/nss/lib/certdb/xbsconst.c146
-rw-r--r--security/nss/lib/certdb/xconst.c269
-rw-r--r--security/nss/lib/certdb/xconst.h30
23 files changed, 17747 insertions, 0 deletions
diff --git a/security/nss/lib/certdb/Makefile b/security/nss/lib/certdb/Makefile
new file mode 100644
index 0000000000..bb88772853
--- /dev/null
+++ b/security/nss/lib/certdb/Makefile
@@ -0,0 +1,45 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#######################################################################
+# (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). #
+#######################################################################
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+
diff --git a/security/nss/lib/certdb/alg1485.c b/security/nss/lib/certdb/alg1485.c
new file mode 100644
index 0000000000..0cf9602f18
--- /dev/null
+++ b/security/nss/lib/certdb/alg1485.c
@@ -0,0 +1,1590 @@
+/* alg1485.c - implementation of RFCs 1485, 1779 and 2253.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prprf.h"
+#include "cert.h"
+#include "certi.h"
+#include "xconst.h"
+#include "genname.h"
+#include "secitem.h"
+#include "secerr.h"
+
+typedef struct NameToKindStr {
+ const char* name;
+ unsigned int maxLen; /* max bytes in UTF8 encoded string value */
+ SECOidTag kind;
+ int valueType;
+} NameToKind;
+
+/* local type for directory string--could be printable_string or utf8 */
+#define SEC_ASN1_DS SEC_ASN1_HIGH_TAG_NUMBER
+
+/* clang-format off */
+
+/* Add new entries to this table, and maybe to function ParseRFC1485AVA */
+static const NameToKind name2kinds[] = {
+/* IANA registered type names
+ * (See: http://www.iana.org/assignments/ldap-parameters)
+ */
+/* RFC 3280, 4630 MUST SUPPORT */
+ { "CN", 640, SEC_OID_AVA_COMMON_NAME, SEC_ASN1_DS},
+ { "ST", 128, SEC_OID_AVA_STATE_OR_PROVINCE,
+ SEC_ASN1_DS},
+ { "O", 128, SEC_OID_AVA_ORGANIZATION_NAME,
+ SEC_ASN1_DS},
+ { "OU", 128, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME,
+ SEC_ASN1_DS},
+ { "dnQualifier", 32767, SEC_OID_AVA_DN_QUALIFIER, SEC_ASN1_PRINTABLE_STRING},
+ { "C", 2, SEC_OID_AVA_COUNTRY_NAME, SEC_ASN1_PRINTABLE_STRING},
+ { "serialNumber", 64, SEC_OID_AVA_SERIAL_NUMBER,SEC_ASN1_PRINTABLE_STRING},
+
+/* RFC 3280, 4630 SHOULD SUPPORT */
+ { "L", 128, SEC_OID_AVA_LOCALITY, SEC_ASN1_DS},
+ { "title", 64, SEC_OID_AVA_TITLE, SEC_ASN1_DS},
+ { "SN", 64, SEC_OID_AVA_SURNAME, SEC_ASN1_DS},
+ { "givenName", 64, SEC_OID_AVA_GIVEN_NAME, SEC_ASN1_DS},
+ { "initials", 64, SEC_OID_AVA_INITIALS, SEC_ASN1_DS},
+ { "generationQualifier",
+ 64, SEC_OID_AVA_GENERATION_QUALIFIER,
+ SEC_ASN1_DS},
+/* RFC 3280, 4630 MAY SUPPORT */
+ { "DC", 128, SEC_OID_AVA_DC, SEC_ASN1_IA5_STRING},
+ { "MAIL", 256, SEC_OID_RFC1274_MAIL, SEC_ASN1_IA5_STRING},
+ { "UID", 256, SEC_OID_RFC1274_UID, SEC_ASN1_DS},
+
+/* ------------------ "strict" boundary ---------------------------------
+ * In strict mode, cert_NameToAscii does not encode any of the attributes
+ * below this line. The first SECOidTag below this line must be used to
+ * conditionally define the "endKind" in function AppendAVA() below.
+ * Most new attribute names should be added below this line.
+ * Maybe this line should be up higher? Say, after the 3280 MUSTs and
+ * before the 3280 SHOULDs?
+ */
+
+/* values from draft-ietf-ldapbis-user-schema-05 (not in RFC 3280) */
+ { "postalAddress", 128, SEC_OID_AVA_POSTAL_ADDRESS, SEC_ASN1_DS},
+ { "postalCode", 40, SEC_OID_AVA_POSTAL_CODE, SEC_ASN1_DS},
+ { "postOfficeBox", 40, SEC_OID_AVA_POST_OFFICE_BOX,SEC_ASN1_DS},
+ { "houseIdentifier",64, SEC_OID_AVA_HOUSE_IDENTIFIER,SEC_ASN1_DS},
+/* end of IANA registered type names */
+
+/* legacy keywords */
+ { "E", 128, SEC_OID_PKCS9_EMAIL_ADDRESS,SEC_ASN1_IA5_STRING},
+ { "STREET", 128, SEC_OID_AVA_STREET_ADDRESS, SEC_ASN1_DS},
+ { "pseudonym", 64, SEC_OID_AVA_PSEUDONYM, SEC_ASN1_DS},
+
+/* values defined by the CAB Forum for EV */
+ { "incorporationLocality", 128, SEC_OID_EV_INCORPORATION_LOCALITY,
+ SEC_ASN1_DS},
+ { "incorporationState", 128, SEC_OID_EV_INCORPORATION_STATE,
+ SEC_ASN1_DS},
+ { "incorporationCountry", 2, SEC_OID_EV_INCORPORATION_COUNTRY,
+ SEC_ASN1_PRINTABLE_STRING},
+ { "businessCategory", 64, SEC_OID_BUSINESS_CATEGORY, SEC_ASN1_DS},
+
+/* values defined in X.520 */
+ { "name", 64, SEC_OID_AVA_NAME, SEC_ASN1_DS},
+
+ { 0, 256, SEC_OID_UNKNOWN, 0},
+};
+
+/* Table facilitates conversion of ASCII hex to binary. */
+static const PRInt16 x2b[256] = {
+/* #0x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #1x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #2x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #3x */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+/* #4x */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #5x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #6x */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #7x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #8x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #9x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #ax */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #bx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #cx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #dx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #ex */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+/* #fx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+#define IS_HEX(c) (x2b[(PRUint8)(c)] >= 0)
+
+#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))
+
+
+#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) == '?'))
+
+/* clang-format on */
+
+/* RFC 2253 says we must escape ",+\"\\<>;=" EXCEPT inside a quoted string.
+ * Inside a quoted string, we only need to escape " and \
+ * We choose to quote strings containing any of those special characters,
+ * so we only need to escape " and \
+ */
+#define NEEDS_ESCAPE(c) (c == C_DOUBLE_QUOTE || c == C_BACKSLASH)
+
+#define NEEDS_HEX_ESCAPE(c) ((PRUint8)c < 0x20 || c == 0x7f)
+
+int
+cert_AVAOidTagToMaxLen(SECOidTag tag)
+{
+ const NameToKind* n2k = name2kinds;
+
+ while (n2k->kind != tag && n2k->kind != SEC_OID_UNKNOWN) {
+ ++n2k;
+ }
+ return (n2k->kind != SEC_OID_UNKNOWN) ? n2k->maxLen : -1;
+}
+
+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 void
+skipSpace(const char** pbp, const char* endptr)
+{
+ const char* bp = *pbp;
+ while (bp < endptr && OPTIONAL_SPACE(*bp)) {
+ bp++;
+ }
+ *pbp = bp;
+}
+
+static SECStatus
+scanTag(const char** pbp, const char* endptr, char* tagBuf, int tagBufSize)
+{
+ const char* bp;
+ char* 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;
+}
+
+/* Returns the number of bytes in the value. 0 means failure. */
+static int
+scanVal(const char** pbp, const char* endptr, char* valBuf, int valBufSize)
+{
+ const char* bp;
+ char* valBufp;
+ int vallen = 0;
+ PRBool isQuoted;
+
+ PORT_Assert(valBufSize > 0);
+
+ /* skip optional leading space */
+ skipSpace(pbp, endptr);
+ if (*pbp == endptr) {
+ /* nothing left */
+ return 0;
+ }
+
+ bp = *pbp;
+
+ /* quoted? */
+ if (*bp == C_DOUBLE_QUOTE) {
+ isQuoted = PR_TRUE;
+ /* skip over it */
+ bp++;
+ } else {
+ isQuoted = PR_FALSE;
+ }
+
+ valBufp = valBuf;
+ 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 0;
+ }
+ c = *bp;
+ if (IS_HEX(c) && (endptr - bp) >= 2 && IS_HEX(bp[1])) {
+ bp++;
+ c = (char)((x2b[(PRUint8)c] << 4) | x2b[(PRUint8)*bp]);
+ }
+ } else if (c == '#' && bp == *pbp) {
+ /* ignore leading #, quotation not required for it. */
+ } 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 0;
+ }
+ *valBufp++ = c;
+ bp++;
+ }
+
+ /* strip trailing spaces from unquoted values */
+ if (!isQuoted) {
+ while (valBufp > valBuf) {
+ char c = valBufp[-1];
+ if (!OPTIONAL_SPACE(c))
+ break;
+ --valBufp;
+ }
+ vallen = valBufp - valBuf;
+ }
+
+ if (isQuoted) {
+ /* insist that we stopped on a double quote */
+ if (*bp != C_DOUBLE_QUOTE) {
+ *pbp = bp;
+ return 0;
+ }
+ /* skip over the quote and skip optional space */
+ bp++;
+ skipSpace(&bp, endptr);
+ }
+
+ *pbp = bp;
+
+ /* null-terminate valBuf -- guaranteed at least one space left */
+ *valBufp = 0;
+
+ return vallen;
+}
+
+/* Caller must set error code upon failure */
+static SECStatus
+hexToBin(PLArenaPool* pool, SECItem* destItem, const char* src, int len)
+{
+ PRUint8* dest;
+
+ destItem->data = NULL;
+ if (len <= 0 || (len & 1)) {
+ goto loser;
+ }
+ len >>= 1;
+ if (!SECITEM_AllocItem(pool, destItem, len)) {
+ goto loser;
+ }
+ dest = destItem->data;
+ for (; len > 0; len--, src += 2) {
+ PRUint16 bin = ((PRUint16)x2b[(PRUint8)src[0]] << 4);
+ bin |= (PRUint16)x2b[(PRUint8)src[1]];
+ if (bin >> 15) { /* is negative */
+ goto loser;
+ }
+ *dest++ = (PRUint8)bin;
+ }
+ return SECSuccess;
+loser:
+ if (!pool)
+ SECITEM_FreeItem(destItem, PR_FALSE);
+ return SECFailure;
+}
+
+/* Parses one AVA, starting at *pbp. Stops at endptr.
+ * Advances *pbp past parsed AVA and trailing separator (if present).
+ * On any error, returns NULL and *pbp is undefined.
+ * On success, returns CERTAVA allocated from arena, and (*pbp)[-1] was
+ * the last character parsed. *pbp is either equal to endptr or
+ * points to first character after separator.
+ */
+static CERTAVA*
+ParseRFC1485AVA(PLArenaPool* arena, const char** pbp, const char* endptr)
+{
+ CERTAVA* a;
+ const NameToKind* n2k;
+ const char* bp;
+ int vt = -1;
+ int valLen;
+ PRBool isDottedOid = PR_FALSE;
+ SECOidTag kind = SEC_OID_UNKNOWN;
+ SECStatus rv = SECFailure;
+ SECItem derOid = { 0, NULL, 0 };
+ SECItem derVal = { 0, NULL, 0 };
+ char sep = 0;
+
+ char tagBuf[32];
+ char valBuf[1024];
+
+ PORT_Assert(arena);
+ if (SECSuccess != scanTag(pbp, endptr, tagBuf, sizeof tagBuf) ||
+ !(valLen = scanVal(pbp, endptr, valBuf, sizeof valBuf))) {
+ goto loser;
+ }
+
+ bp = *pbp;
+ if (bp < endptr) {
+ sep = *bp++; /* skip over separator */
+ }
+ *pbp = bp;
+ /* if we haven't finished, insist that we've stopped on a separator */
+ if (sep && sep != ',' && sep != ';' && sep != '+') {
+ goto loser;
+ }
+
+ /* is this a dotted decimal OID attribute type ? */
+ if (!PL_strncasecmp("oid.", tagBuf, 4) || isdigit(tagBuf[0])) {
+ rv = SEC_StringToOID(arena, &derOid, tagBuf, strlen(tagBuf));
+ isDottedOid = (PRBool)(rv == SECSuccess);
+ } else {
+ for (n2k = name2kinds; n2k->name; n2k++) {
+ SECOidData* oidrec;
+ if (PORT_Strcasecmp(n2k->name, tagBuf) == 0) {
+ kind = n2k->kind;
+ vt = n2k->valueType;
+ oidrec = SECOID_FindOIDByTag(kind);
+ if (oidrec == NULL)
+ goto loser;
+ derOid = oidrec->oid;
+ break;
+ }
+ }
+ }
+ if (kind == SEC_OID_UNKNOWN && rv != SECSuccess)
+ goto loser;
+
+ /* Is this a hex encoding of a DER attribute value ? */
+ if ('#' == valBuf[0]) {
+ /* convert attribute value from hex to binary */
+ rv = hexToBin(arena, &derVal, valBuf + 1, valLen - 1);
+ if (rv)
+ goto loser;
+ a = CERT_CreateAVAFromRaw(arena, &derOid, &derVal);
+ } else {
+ if (kind == SEC_OID_AVA_COUNTRY_NAME && valLen != 2)
+ goto loser;
+ if (vt == SEC_ASN1_PRINTABLE_STRING &&
+ !IsPrintable((unsigned char*)valBuf, valLen))
+ goto loser;
+ if (vt == SEC_ASN1_DS) {
+ /* RFC 4630: choose PrintableString or UTF8String */
+ if (IsPrintable((unsigned char*)valBuf, valLen))
+ vt = SEC_ASN1_PRINTABLE_STRING;
+ else
+ vt = SEC_ASN1_UTF8_STRING;
+ }
+
+ derVal.data = (unsigned char*)valBuf;
+ derVal.len = valLen;
+ if (kind == SEC_OID_UNKNOWN && isDottedOid) {
+ a = CERT_CreateAVAFromRaw(arena, &derOid, &derVal);
+ } else {
+ a = CERT_CreateAVAFromSECItem(arena, kind, vt, &derVal);
+ }
+ }
+ return a;
+
+loser:
+ /* matched no kind -- invalid tag */
+ PORT_SetError(SEC_ERROR_INVALID_AVA);
+ return 0;
+}
+
+static CERTName*
+ParseRFC1485Name(const char* buf, int len)
+{
+ SECStatus rv;
+ CERTName* name;
+ const char *bp, *e;
+ CERTAVA* ava;
+ CERTRDN* rdn = NULL;
+
+ name = CERT_CreateName(NULL);
+ if (name == NULL) {
+ return NULL;
+ }
+
+ e = buf + len;
+ bp = buf;
+ while (bp < e) {
+ ava = ParseRFC1485AVA(name->arena, &bp, e);
+ if (ava == 0)
+ goto loser;
+ if (!rdn) {
+ rdn = CERT_CreateRDN(name->arena, ava, (CERTAVA*)0);
+ if (rdn == 0)
+ goto loser;
+ rv = CERT_AddRDN(name, rdn);
+ } else {
+ rv = CERT_AddAVA(name->arena, rdn, ava);
+ }
+ if (rv)
+ goto loser;
+ if (bp[-1] != '+')
+ rdn = NULL; /* done with this RDN */
+ 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(const 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
+
+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 = PR_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;
+}
+
+typedef enum {
+ minimalEscape = 0, /* only hex escapes, and " and \ */
+ minimalEscapeAndQuote, /* as above, plus quoting */
+ fullEscape /* no quoting, full escaping */
+} EQMode;
+
+/* Some characters must be escaped as a hex string, e.g. c -> \nn .
+ * Others must be escaped by preceding with a '\', e.g. c -> \c , but
+ * there are certain "special characters" that may be handled by either
+ * escaping them, or by enclosing the entire attribute value in quotes.
+ * A NULL value for pEQMode implies selecting minimalEscape mode.
+ * Some callers will do quoting when needed, others will not.
+ * If a caller selects minimalEscapeAndQuote, and the string does not
+ * need quoting, then this function changes it to minimalEscape.
+ */
+static int
+cert_RFC1485_GetRequiredLen(const char* src, int srclen, EQMode* pEQMode)
+{
+ int i, reqLen = 0;
+ EQMode mode = pEQMode ? *pEQMode : minimalEscape;
+ PRBool needsQuoting = PR_FALSE;
+ char lastC = 0;
+
+ /* need to make an initial pass to determine if quoting is needed */
+ for (i = 0; i < srclen; i++) {
+ char c = src[i];
+ reqLen++;
+ if (NEEDS_HEX_ESCAPE(c)) { /* c -> \xx */
+ reqLen += 2;
+ } else if (NEEDS_ESCAPE(c)) { /* c -> \c */
+ reqLen++;
+ } else if (SPECIAL_CHAR(c)) {
+ if (mode == minimalEscapeAndQuote) /* quoting is allowed */
+ needsQuoting = PR_TRUE; /* entirety will need quoting */
+ else if (mode == fullEscape)
+ reqLen++; /* MAY escape this character */
+ } else if (OPTIONAL_SPACE(c) && OPTIONAL_SPACE(lastC)) {
+ if (mode == minimalEscapeAndQuote) /* quoting is allowed */
+ needsQuoting = PR_TRUE; /* entirety will need quoting */
+ }
+ lastC = c;
+ }
+ /* if it begins or ends in optional space it needs quoting */
+ if (!needsQuoting && srclen > 0 && mode == minimalEscapeAndQuote &&
+ (OPTIONAL_SPACE(src[srclen - 1]) || OPTIONAL_SPACE(src[0]))) {
+ needsQuoting = PR_TRUE;
+ }
+
+ if (needsQuoting)
+ reqLen += 2;
+ if (pEQMode && mode == minimalEscapeAndQuote && !needsQuoting)
+ *pEQMode = minimalEscape;
+ return reqLen;
+}
+
+static const char hexChars[16] = { "0123456789abcdef" };
+
+static SECStatus
+escapeAndQuote(char* dst, int dstlen, char* src, int srclen, EQMode* pEQMode)
+{
+ int i, reqLen = 0;
+ EQMode mode = pEQMode ? *pEQMode : minimalEscape;
+
+ /* space for terminal null */
+ reqLen = cert_RFC1485_GetRequiredLen(src, srclen, &mode) + 1;
+ if (reqLen > dstlen) {
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ return SECFailure;
+ }
+
+ if (mode == minimalEscapeAndQuote)
+ *dst++ = C_DOUBLE_QUOTE;
+ for (i = 0; i < srclen; i++) {
+ char c = src[i];
+ if (NEEDS_HEX_ESCAPE(c)) {
+ *dst++ = C_BACKSLASH;
+ *dst++ = hexChars[(c >> 4) & 0x0f];
+ *dst++ = hexChars[c & 0x0f];
+ } else {
+ if (NEEDS_ESCAPE(c) || (SPECIAL_CHAR(c) && mode == fullEscape)) {
+ *dst++ = C_BACKSLASH;
+ }
+ *dst++ = c;
+ }
+ }
+ if (mode == minimalEscapeAndQuote)
+ *dst++ = C_DOUBLE_QUOTE;
+ *dst++ = 0;
+ if (pEQMode)
+ *pEQMode = mode;
+ return SECSuccess;
+}
+
+SECStatus
+CERT_RFC1485_EscapeAndQuote(char* dst, int dstlen, char* src, int srclen)
+{
+ EQMode mode = minimalEscapeAndQuote;
+ return escapeAndQuote(dst, dstlen, src, srclen, &mode);
+}
+
+/* convert an OID to dotted-decimal representation */
+/* Returns a string that must be freed with PR_smprintf_free(), */
+char*
+CERT_GetOidString(const SECItem* oid)
+{
+ PRUint8* stop; /* points to first byte after OID string */
+ PRUint8* first; /* byte of an OID component integer */
+ PRUint8* last; /* byte of an OID component integer */
+ char* rvString = NULL;
+ char* prefix = NULL;
+
+#define MAX_OID_LEN 1024 /* bytes */
+
+ if (oid->len > MAX_OID_LEN) {
+ PORT_SetError(SEC_ERROR_INPUT_LEN);
+ return NULL;
+ }
+
+ /* If the OID has length 1, we bail. */
+ if (oid->len < 2) {
+ return NULL;
+ }
+
+ /* first will point to the next sequence of bytes to decode */
+ first = (PRUint8*)oid->data;
+ /* stop points to one past the legitimate data */
+ stop = &first[oid->len];
+
+ /*
+ * Check for our pseudo-encoded single-digit OIDs
+ */
+ if ((*first == 0x80) && (2 == oid->len)) {
+ /* Funky encoding. The second byte is the number */
+ rvString = PR_smprintf("%lu", (PRUint32)first[1]);
+ if (!rvString) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ return rvString;
+ }
+
+ for (; first < stop; first = last + 1) {
+ unsigned int bytesBeforeLast;
+
+ for (last = first; last < stop; last++) {
+ if (0 == (*last & 0x80)) {
+ break;
+ }
+ }
+ /* There's no first bit set, so this isn't valid. Bail.*/
+ if (last == stop) {
+ goto unsupported;
+ }
+ bytesBeforeLast = (unsigned int)(last - first);
+ if (bytesBeforeLast <= 3U) { /* 0-28 bit number */
+ PRUint32 n = 0;
+ PRUint32 c;
+
+#define CGET(i, m) \
+ c = last[-i] & m; \
+ n |= c << (7 * i)
+
+#define CASE(i, m) \
+ case i: \
+ CGET(i, m); \
+ if (!n) \
+ goto unsupported /* fall-through */
+
+ switch (bytesBeforeLast) {
+ CASE(3, 0x7f);
+ CASE(2, 0x7f);
+ CASE(1, 0x7f);
+ case 0:
+ n |= last[0] & 0x7f;
+ break;
+ }
+ if (last[0] & 0x80) {
+ goto unsupported;
+ }
+
+ if (!rvString) {
+ /* This is the first number.. decompose it */
+ PRUint32 one = PR_MIN(n / 40, 2); /* never > 2 */
+ PRUint32 two = n - (one * 40);
+
+ rvString = PR_smprintf("OID.%lu.%lu", one, two);
+ } else {
+ prefix = rvString;
+ rvString = PR_smprintf("%s.%lu", prefix, n);
+ }
+ } else if (bytesBeforeLast <= 9U) { /* 29-64 bit number */
+ PRUint64 n = 0;
+ PRUint64 c;
+
+ switch (bytesBeforeLast) {
+ CASE(9, 0x01);
+ CASE(8, 0x7f);
+ CASE(7, 0x7f);
+ CASE(6, 0x7f);
+ CASE(5, 0x7f);
+ CASE(4, 0x7f);
+ CGET(3, 0x7f);
+ CGET(2, 0x7f);
+ CGET(1, 0x7f);
+ CGET(0, 0x7f);
+ break;
+ }
+ if (last[0] & 0x80)
+ goto unsupported;
+
+ if (!rvString) {
+ /* This is the first number.. decompose it */
+ PRUint64 one = PR_MIN(n / 40, 2); /* never > 2 */
+ PRUint64 two = n - (one * 40);
+
+ rvString = PR_smprintf("OID.%llu.%llu", one, two);
+ } else {
+ prefix = rvString;
+ rvString = PR_smprintf("%s.%llu", prefix, n);
+ }
+ } else {
+ /* More than a 64-bit number, or not minimal encoding. */
+ unsupported:
+ if (!rvString)
+ rvString = PR_smprintf("OID.UNSUPPORTED");
+ else {
+ prefix = rvString;
+ rvString = PR_smprintf("%s.UNSUPPORTED", prefix);
+ }
+ }
+
+ if (prefix) {
+ PR_smprintf_free(prefix);
+ prefix = NULL;
+ }
+ if (!rvString) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ break;
+ }
+ }
+ return rvString;
+}
+
+/* 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;
+}
+
+/* For compliance with RFC 2253, RFC 3280 and RFC 4630, we choose to
+ * use the NAME=STRING form, rather than the OID.N.N=#hexXXXX form,
+ * when both of these conditions are met:
+ * 1) The attribute name OID (kind) has a known name string that is
+ * defined in one of those RFCs, or in RFCs that they cite, AND
+ * 2) The attribute's value encoding is RFC compliant for the kind
+ * (e.g., the value's encoding tag is correct for the kind, and
+ * the value's length is in the range allowed for the kind, and
+ * the value's contents are appropriate for the encoding tag).
+ * Otherwise, we use the OID.N.N=#hexXXXX form.
+ *
+ * If the caller prefers maximum human readability to RFC compliance,
+ * then
+ * - We print the kind in NAME= string form if we know the name
+ * string for the attribute type OID, regardless of whether the
+ * value is correctly encoded or not. else we use the OID.N.N= form.
+ * - We use the non-hex STRING form for the attribute value if the
+ * value can be represented in such a form. Otherwise, we use
+ * the hex string form.
+ * This implies that, for maximum human readability, in addition to
+ * the two forms allowed by the RFC, we allow two other forms of output:
+ * - the OID.N.N=STRING form, and
+ * - the NAME=#hexXXXX form
+ * When the caller prefers maximum human readability, we do not allow
+ * the value of any attribute to exceed the length allowed by the RFC.
+ * If the attribute value exceeds the allowed length, we truncate it to
+ * the allowed length and append "...".
+ * Also in this case, we arbitrarily impose a limit on the length of the
+ * entire AVA encoding, regardless of the form, of 384 bytes per AVA.
+ * This limit includes the trailing NULL character. If the encoded
+ * AVA length exceeds that limit, this function reports failure to encode
+ * the AVA.
+ *
+ * An ASCII representation of an AVA is said to be "invertible" if
+ * conversion back to DER reproduces the original DER encoding exactly.
+ * The RFC 2253 rules do not ensure that all ASCII AVAs derived according
+ * to its rules are invertible. That is because the RFCs allow some
+ * attribute values to be encoded in any of a number of encodings,
+ * and the encoding type information is lost in the non-hex STRING form.
+ * This is particularly true of attributes of type DirectoryString.
+ * The encoding type information is always preserved in the hex string
+ * form, because the hex includes the entire DER encoding of the value.
+ *
+ * So, when the caller perfers maximum invertibility, we apply the
+ * RFC compliance rules stated above, and add a third required
+ * condition on the use of the NAME=STRING form.
+ * 3) The attribute's kind is not is allowed to be encoded in any of
+ * several different encodings, such as DirectoryStrings.
+ *
+ * The chief difference between CERT_N2A_STRICT and CERT_N2A_INVERTIBLE
+ * is that the latter forces DirectoryStrings to be hex encoded.
+ *
+ * As a simplification, we assume the value is correctly encoded for
+ * its encoding type. That is, we do not test that all the characters
+ * in a string encoded type are allowed by that type. We assume it.
+ */
+static SECStatus
+AppendAVA(stringBuf* bufp, CERTAVA* ava, CertStrictnessLevel strict)
+{
+#define TMPBUF_LEN 2048
+ const NameToKind* pn2k = name2kinds;
+ SECItem* avaValue = NULL;
+ char* unknownTag = NULL;
+ char* encodedAVA = NULL;
+ PRBool useHex = PR_FALSE; /* use =#hexXXXX form */
+ PRBool truncateName = PR_FALSE;
+ PRBool truncateValue = PR_FALSE;
+ SECOidTag endKind;
+ SECStatus rv;
+ unsigned int len;
+ unsigned int nameLen, valueLen;
+ unsigned int maxName, maxValue;
+ EQMode mode = minimalEscapeAndQuote;
+ NameToKind n2k = { NULL, 32767, SEC_OID_UNKNOWN, SEC_ASN1_DS };
+ char tmpBuf[TMPBUF_LEN];
+
+#define tagName n2k.name /* non-NULL means use NAME= form */
+#define maxBytes n2k.maxLen
+#define tag n2k.kind
+#define vt n2k.valueType
+
+ /* READABLE mode recognizes more names from the name2kinds table
+ * than do STRICT or INVERTIBLE modes. This assignment chooses the
+ * point in the table where the attribute type name scanning stops.
+ */
+ endKind = (strict == CERT_N2A_READABLE) ? SEC_OID_UNKNOWN
+ : SEC_OID_AVA_POSTAL_ADDRESS;
+ tag = CERT_GetAVATag(ava);
+ while (pn2k->kind != tag && pn2k->kind != endKind) {
+ ++pn2k;
+ }
+
+ if (pn2k->kind != endKind) {
+ n2k = *pn2k;
+ } else if (strict != CERT_N2A_READABLE) {
+ useHex = PR_TRUE;
+ }
+ /* For invertable form, force Directory Strings to use hex form. */
+ if (strict == CERT_N2A_INVERTIBLE && vt == SEC_ASN1_DS) {
+ tagName = NULL; /* must use OID.N form */
+ useHex = PR_TRUE; /* must use hex string */
+ }
+ if (!useHex) {
+ avaValue = CERT_DecodeAVAValue(&ava->value);
+ if (!avaValue) {
+ useHex = PR_TRUE;
+ if (strict != CERT_N2A_READABLE) {
+ tagName = NULL; /* must use OID.N form */
+ }
+ }
+ }
+ if (!tagName) {
+ /* handle unknown attribute types per RFC 2253 */
+ tagName = unknownTag = CERT_GetOidString(&ava->type);
+ if (!tagName) {
+ if (avaValue)
+ SECITEM_FreeItem(avaValue, PR_TRUE);
+ return SECFailure;
+ }
+ }
+ if (useHex) {
+ avaValue = get_hex_string(&ava->value);
+ if (!avaValue) {
+ if (unknownTag)
+ PR_smprintf_free(unknownTag);
+ return SECFailure;
+ }
+ }
+
+ nameLen = strlen(tagName);
+ valueLen =
+ (useHex ? avaValue->len : cert_RFC1485_GetRequiredLen((char*)avaValue->data, avaValue->len, &mode));
+ len = nameLen + valueLen + 2; /* Add 2 for '=' and trailing NUL */
+
+ maxName = nameLen;
+ maxValue = valueLen;
+ if (len <= sizeof(tmpBuf)) {
+ encodedAVA = tmpBuf;
+ } else if (strict != CERT_N2A_READABLE) {
+ encodedAVA = PORT_Alloc(len);
+ if (!encodedAVA) {
+ SECITEM_FreeItem(avaValue, PR_TRUE);
+ if (unknownTag)
+ PR_smprintf_free(unknownTag);
+ return SECFailure;
+ }
+ } else {
+ /* Must make output fit in tmpbuf */
+ unsigned int fair = (sizeof tmpBuf) / 2 - 1; /* for = and \0 */
+
+ if (nameLen < fair) {
+ /* just truncate the value */
+ maxValue = (sizeof tmpBuf) - (nameLen + 6); /* for "=...\0",
+ and possibly '"' */
+ } else if (valueLen < fair) {
+ /* just truncate the name */
+ maxName = (sizeof tmpBuf) - (valueLen + 5); /* for "=...\0" */
+ } else {
+ /* truncate both */
+ maxName = maxValue = fair - 3; /* for "..." */
+ }
+ if (nameLen > maxName) {
+ PORT_Assert(unknownTag && unknownTag == tagName);
+ truncateName = PR_TRUE;
+ nameLen = maxName;
+ }
+ encodedAVA = tmpBuf;
+ }
+
+ memcpy(encodedAVA, tagName, nameLen);
+ if (truncateName) {
+ /* If tag name is too long, we know it is an OID form that was
+ * allocated from the heap, so we can modify it in place
+ */
+ encodedAVA[nameLen - 1] = '.';
+ encodedAVA[nameLen - 2] = '.';
+ encodedAVA[nameLen - 3] = '.';
+ }
+ encodedAVA[nameLen++] = '=';
+ if (unknownTag)
+ PR_smprintf_free(unknownTag);
+
+ if (strict == CERT_N2A_READABLE && maxValue > maxBytes)
+ maxValue = maxBytes;
+ if (valueLen > maxValue) {
+ valueLen = maxValue;
+ truncateValue = PR_TRUE;
+ }
+ /* escape and quote as necessary - don't quote hex strings */
+ if (useHex) {
+ char* end = encodedAVA + nameLen + valueLen;
+ memcpy(encodedAVA + nameLen, (char*)avaValue->data, valueLen);
+ end[0] = '\0';
+ if (truncateValue) {
+ end[-1] = '.';
+ end[-2] = '.';
+ end[-3] = '.';
+ }
+ rv = SECSuccess;
+ } else if (!truncateValue) {
+ rv = escapeAndQuote(encodedAVA + nameLen, len - nameLen,
+ (char*)avaValue->data, avaValue->len, &mode);
+ } else {
+ /* must truncate the escaped and quoted value */
+ char bigTmpBuf[TMPBUF_LEN * 3 + 3];
+ PORT_Assert(valueLen < sizeof tmpBuf);
+ rv = escapeAndQuote(bigTmpBuf, sizeof bigTmpBuf, (char*)avaValue->data,
+ PR_MIN(avaValue->len, valueLen), &mode);
+
+ bigTmpBuf[valueLen--] = '\0'; /* hard stop here */
+ /* See if we're in the middle of a multi-byte UTF8 character */
+ while (((bigTmpBuf[valueLen] & 0xc0) == 0x80) && valueLen > 0) {
+ bigTmpBuf[valueLen--] = '\0';
+ }
+ /* add ellipsis to signify truncation. */
+ bigTmpBuf[++valueLen] = '.';
+ bigTmpBuf[++valueLen] = '.';
+ bigTmpBuf[++valueLen] = '.';
+ if (bigTmpBuf[0] == '"')
+ bigTmpBuf[++valueLen] = '"';
+ bigTmpBuf[++valueLen] = '\0';
+ PORT_Assert(nameLen + valueLen <= (sizeof tmpBuf) - 1);
+ memcpy(encodedAVA + nameLen, bigTmpBuf, valueLen + 1);
+ }
+
+ SECITEM_FreeItem(avaValue, PR_TRUE);
+ if (rv == SECSuccess)
+ rv = AppendStr(bufp, encodedAVA);
+ if (encodedAVA != tmpBuf)
+ PORT_Free(encodedAVA);
+ return rv;
+}
+
+#undef tagName
+#undef maxBytes
+#undef tag
+#undef vt
+
+char*
+CERT_NameToAsciiInvertible(CERTName* name, CertStrictnessLevel strict)
+{
+ CERTRDN** rdns;
+ CERTRDN** lastRdn;
+ CERTRDN** rdn;
+ 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--) {
+ CERTAVA** avas = (*rdn)->avas;
+ CERTAVA* ava;
+ PRBool newRDN = PR_TRUE;
+
+ /*
+ * XXX Do we need to traverse the AVAs in reverse order, too?
+ */
+ while (avas && (ava = *avas++) != NULL) {
+ SECStatus rv;
+ /* Put in comma or plus separator */
+ if (!first) {
+ /* Use of spaces is deprecated in RFC 2253. */
+ rv = AppendStr(&strBuf, newRDN ? "," : "+");
+ if (rv)
+ goto loser;
+ } else {
+ first = PR_FALSE;
+ }
+
+ /* Add in tag type plus value into strBuf */
+ rv = AppendAVA(&strBuf, ava, strict);
+ if (rv)
+ goto loser;
+ newRDN = PR_FALSE;
+ }
+ }
+ return strBuf.buffer;
+loser:
+ if (strBuf.buffer) {
+ PORT_Free(strBuf.buffer);
+ }
+ return NULL;
+}
+
+char*
+CERT_NameToAscii(CERTName* name)
+{
+ return CERT_NameToAsciiInvertible(name, CERT_N2A_READABLE);
+}
+
+/*
+ * Return the string representation of a DER encoded distinguished name
+ * "dername" - The DER encoded name to convert
+ */
+char*
+CERT_DerNameToAscii(SECItem* dername)
+{
+ int rv;
+ PLArenaPool* 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*
+avaToString(PLArenaPool* arena, CERTAVA* ava)
+{
+ char* buf = NULL;
+ SECItem* avaValue;
+ int valueLen;
+
+ avaValue = CERT_DecodeAVAValue(&ava->value);
+ if (!avaValue) {
+ return buf;
+ }
+ valueLen =
+ cert_RFC1485_GetRequiredLen((char*)avaValue->data, avaValue->len, NULL) + 1;
+ if (arena) {
+ buf = (char*)PORT_ArenaZAlloc(arena, valueLen);
+ } else {
+ buf = (char*)PORT_ZAlloc(valueLen);
+ }
+ if (buf) {
+ SECStatus rv =
+ escapeAndQuote(buf, valueLen, (char*)avaValue->data, avaValue->len, NULL);
+ if (rv != SECSuccess) {
+ if (!arena)
+ PORT_Free(buf);
+ buf = NULL;
+ }
+ }
+ SECITEM_FreeItem(avaValue, PR_TRUE);
+ return buf;
+}
+
+/* RDNs are sorted from most general to most specific.
+ * This code returns the FIRST one found, the most general one found.
+ */
+static char*
+CERT_GetNameElement(PLArenaPool* arena, const CERTName* name, int wantedTag)
+{
+ CERTRDN** rdns = name->rdns;
+ CERTRDN* rdn;
+ CERTAVA* ava = NULL;
+
+ while (rdns && (rdn = *rdns++) != 0) {
+ CERTAVA** avas = rdn->avas;
+ while (avas && (ava = *avas++) != 0) {
+ int tag = CERT_GetAVATag(ava);
+ if (tag == wantedTag) {
+ avas = NULL;
+ rdns = NULL; /* break out of all loops */
+ }
+ }
+ }
+ return ava ? avaToString(arena, ava) : NULL;
+}
+
+/* RDNs are sorted from most general to most specific.
+ * This code returns the LAST one found, the most specific one found.
+ * This is particularly appropriate for Common Name. See RFC 2818.
+ */
+static char*
+CERT_GetLastNameElement(PLArenaPool* arena, const CERTName* name, int wantedTag)
+{
+ CERTRDN** rdns = name->rdns;
+ CERTRDN* rdn;
+ CERTAVA* lastAva = NULL;
+
+ while (rdns && (rdn = *rdns++) != 0) {
+ CERTAVA** avas = rdn->avas;
+ CERTAVA* ava;
+ while (avas && (ava = *avas++) != 0) {
+ int tag = CERT_GetAVATag(ava);
+ if (tag == wantedTag) {
+ lastAva = ava;
+ }
+ }
+ }
+ return lastAva ? avaToString(arena, lastAva) : NULL;
+}
+
+char*
+CERT_GetCertificateEmailAddress(CERTCertificate* cert)
+{
+ char* rawEmailAddr = NULL;
+ SECItem subAltName;
+ SECStatus rv;
+ CERTGeneralName* nameList = NULL;
+ CERTGeneralName* current;
+ PLArenaPool* 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_GetNextGeneralName(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;
+}
+
+#undef NEEDS_HEX_ESCAPE
+#define NEEDS_HEX_ESCAPE(c) (c < 0x20)
+
+static char*
+appendItemToBuf(char* dest, SECItem* src, PRUint32* pRemaining)
+{
+ if (dest && src && src->data && src->len && src->data[0]) {
+ PRUint32 len = src->len;
+ PRUint32 i;
+ PRUint32 reqLen = len + 1;
+ /* are there any embedded control characters ? */
+ for (i = 0; i < len; i++) {
+ if (NEEDS_HEX_ESCAPE(src->data[i]))
+ reqLen += 2;
+ }
+ if (*pRemaining > reqLen) {
+ for (i = 0; i < len; ++i) {
+ PRUint8 c = src->data[i];
+ if (NEEDS_HEX_ESCAPE(c)) {
+ *dest++ =
+ C_BACKSLASH;
+ *dest++ =
+ hexChars[(c >> 4) & 0x0f];
+ *dest++ =
+ hexChars[c & 0x0f];
+ } else {
+ *dest++ =
+ tolower(c);
+ }
+ }
+ *dest++ = '\0';
+ *pRemaining -= reqLen;
+ }
+ }
+ 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;
+ PORTCheapArenaPool tmpArena;
+ PRUint32 maxLen = 0;
+ PRInt32 finalLen = 0;
+ SECStatus rv;
+ SECItem subAltName;
+
+ PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+
+ 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.arena, maxLen + 1);
+ if (!addrBuf)
+ goto loser;
+
+ rawEmailAddr = CERT_GetNameElement(&tmpArena.arena, &cert->subject,
+ SEC_OID_PKCS9_EMAIL_ADDRESS);
+ pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
+
+ rawEmailAddr = CERT_GetNameElement(&tmpArena.arena, &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.arena, &subAltName))) {
+ CERTGeneralName* current = nameList;
+ do {
+ if (current->type == certDirectoryName) {
+ rawEmailAddr =
+ CERT_GetNameElement(&tmpArena.arena,
+ &current->name.directoryName,
+ SEC_OID_PKCS9_EMAIL_ADDRESS);
+ pBuf =
+ appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
+
+ rawEmailAddr =
+ CERT_GetNameElement(&tmpArena.arena,
+ &current->name.directoryName,
+ SEC_OID_RFC1274_MAIL);
+ pBuf =
+ appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
+ } else if (current->type == certRFC822Name) {
+ pBuf =
+ appendItemToBuf(pBuf, &current->name.other, &maxLen);
+ }
+ current = CERT_GetNextGeneralName(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 = NULL;
+ if (finalLen > 1) {
+ pBuf = PORT_ArenaAlloc(cert->arena, finalLen);
+ if (pBuf) {
+ PORT_Memcpy(pBuf, addrBuf, finalLen);
+ }
+ }
+loser:
+ PORT_DestroyCheapArena(&tmpArena);
+
+ 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(const 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);
+}
+
+/* The return value must be freed with PORT_Free. */
+char*
+CERT_GetCommonName(const CERTName* name)
+{
+ return (CERT_GetLastNameElement(NULL, name, SEC_OID_AVA_COMMON_NAME));
+}
+
+char*
+CERT_GetCountryName(const CERTName* name)
+{
+ return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_COUNTRY_NAME));
+}
+
+char*
+CERT_GetLocalityName(const CERTName* name)
+{
+ return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_LOCALITY));
+}
+
+char*
+CERT_GetStateName(const CERTName* name)
+{
+ return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_STATE_OR_PROVINCE));
+}
+
+char*
+CERT_GetOrgName(const CERTName* name)
+{
+ return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATION_NAME));
+}
+
+char*
+CERT_GetDomainComponentName(const CERTName* name)
+{
+ return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_DC));
+}
+
+char*
+CERT_GetOrgUnitName(const CERTName* name)
+{
+ return (
+ CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME));
+}
+
+char*
+CERT_GetDnQualifier(const CERTName* name)
+{
+ return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_DN_QUALIFIER));
+}
+
+char*
+CERT_GetCertUid(const 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 0000000000..33d37b39a0
--- /dev/null
+++ b/security/nss/lib/certdb/cert.h
@@ -0,0 +1,1606 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * cert.h - public data structures and prototypes for the certificate library
+ */
+
+#ifndef _CERT_H_
+#define _CERT_H_
+
+#include "utilrename.h"
+#include "plarena.h"
+#include "plhash.h"
+#include "prlong.h"
+#include "prlog.h"
+
+#include "seccomon.h"
+#include "secdert.h"
+#include "secoidt.h"
+#include "keythi.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(const char *string);
+
+/*
+** Convert an CERTName into its RFC1485 encoded equivalent.
+** Returns a string that must be freed with PORT_Free().
+** This version produces a string for maximum human readability,
+** not for strict RFC compliance.
+*/
+extern char *CERT_NameToAscii(CERTName *name);
+
+/*
+** Convert an CERTName into its RFC1485 encoded equivalent.
+** Returns a string that must be freed with PORT_Free().
+** Caller chooses encoding rules.
+*/
+extern char *CERT_NameToAsciiInvertible(CERTName *name,
+ CertStrictnessLevel strict);
+
+extern CERTAVA *CERT_CopyAVA(PLArenaPool *arena, CERTAVA *src);
+
+/* convert an OID to dotted-decimal representation */
+/* Returns a string that must be freed with PR_smprintf_free(). */
+extern char *CERT_GetOidString(const SECItem *oid);
+
+/*
+** 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(const CERTAVA *a, const CERTAVA *b);
+
+/*
+** Create an RDN (relative-distinguished-name). The argument list is a
+** NULL terminated list of AVA's.
+*/
+extern CERTRDN *CERT_CreateRDN(PLArenaPool *arena, CERTAVA *avas, ...);
+
+/*
+** Make a copy of "src" storing it in "dest".
+*/
+extern SECStatus CERT_CopyRDN(PLArenaPool *arena, CERTRDN *dest, CERTRDN *src);
+
+/*
+** Add an AVA to an RDN.
+** "rdn" the RDN to add to
+** "ava" the AVA to add
+*/
+extern SECStatus CERT_AddAVA(PLArenaPool *arena, CERTRDN *rdn, CERTAVA *ava);
+
+/*
+** Compare two RDN's, returning the difference between them.
+*/
+extern SECComparison CERT_CompareRDN(const CERTRDN *a, const 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(PLArenaPool *arena, CERTName *dest,
+ const 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(const CERTName *a, const 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);
+
+/*
+** Converts DER string (with explicit length) into zString, if destination
+** buffer is big enough to receive it. Does quoting and/or escaping as
+** specified in RFC 1485. Input string must be single or multi-byte DER
+** character set, (ASCII, UTF8, or ISO 8851-x) not a wide character set.
+** Returns SECSuccess or SECFailure with error code set. If output buffer
+** is too small, sets error code SEC_ERROR_OUTPUT_LEN.
+*/
+extern SECStatus CERT_RFC1485_EscapeAndQuote(char *dst, int dstlen, char *src,
+ int srclen);
+
+/******************************************************************************
+ *
+ * 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(PRTime notBefore, PRTime 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(PLArenaPool *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);
+
+/* Access the DER of the certificate. This only creates a reference to the DER
+ * in the outparam not a copy. To avoid the pointer becoming invalid, use
+ * CERT_DupCertificate() and keep a reference to the duplicate alive.
+ */
+extern SECStatus CERT_GetCertificateDer(const CERTCertificate *c, SECItem *der);
+
+/*
+** 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);
+
+/*
+** Start adding extensions to a certificate request.
+*/
+void *CERT_StartCertificateRequestAttributes(CERTCertificateRequest *req);
+
+/*
+** Reformat the certificate extension list into a CertificateRequest
+** attribute list.
+*/
+SECStatus CERT_FinishCertificateRequestAttributes(CERTCertificateRequest *req);
+
+/*
+** Extract the Extension Requests from a DER CertRequest attribute list.
+*/
+SECStatus CERT_GetCertificateRequestExtensions(CERTCertificateRequest *req,
+ CERTCertExtension ***exts);
+
+/*
+** Extract a public key object from a certificate
+*/
+extern SECKEYPublicKey *CERT_ExtractPublicKey(CERTCertificate *cert);
+
+/*
+** Retrieve the Key Type associated with the cert we're dealing with
+*/
+
+extern KeyType CERT_GetCertKeyType(const 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,
+ PRTime 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(PLArenaPool *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);
+
+extern SECItem *CERT_EncodeGeneralName(CERTGeneralName *genName, SECItem *dest,
+ PLArenaPool *arena);
+
+extern CERTGeneralName *CERT_DecodeGeneralName(PLArenaPool *reqArena,
+ SECItem *encodedName,
+ CERTGeneralName *genName);
+
+/*
+** 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(PLArenaPool *reqArena, SECItem *derCert,
+ SECItem *key);
+
+extern SECStatus CERT_KeyFromIssuerAndSN(PLArenaPool *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(PLArenaPool *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);
+
+/*
+** Extract the list of host names, host name patters, IP address strings
+** this cert is valid for.
+** This function does NOT return nicknames.
+** Type CERTCertNicknames is being used because it's a convenient
+** data structure to carry a list of strings and its count.
+*/
+extern CERTCertNicknames *CERT_GetValidDNSPatternsFromCert(
+ CERTCertificate *cert);
+
+/*
+** 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(const 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 into a CERTSignedCrl structure
+** "derSignedCrl" is the DER encoded signed CRL.
+** "type" must be SEC_CRL_TYPE.
+*/
+#define SEC_CRL_TYPE 1
+#define SEC_KRL_TYPE 0 /* deprecated */
+
+extern CERTSignedCrl *CERT_DecodeDERCrl(PLArenaPool *arena,
+ SECItem *derSignedCrl, int type);
+
+/*
+ * same as CERT_DecodeDERCrl, plus allow options to be passed in
+ */
+
+extern CERTSignedCrl *CERT_DecodeDERCrlWithFlags(PLArenaPool *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
+#define CRL_DECODE_ADOPT_HEAP_DER 0x00000008
+
+/* 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);
+
+/* add the specified DER CRL object to the CRL cache. Doing so will allow
+ certificate verification functions (such as CERT_VerifyCertificate)
+ to automatically find and make use of this CRL object.
+ Once a CRL is added to the CRL cache, the application must hold on to
+ the object's memory, because the cache will reference it directly. The
+ application can only free the object after it calls CERT_UncacheCRL to
+ remove it from the CRL cache.
+*/
+SECStatus CERT_CacheCRL(CERTCertDBHandle *dbhandle, SECItem *newcrl);
+
+/* remove a previously added CRL object from the CRL cache. It is OK
+ for the application to free the memory after a successful removal
+*/
+SECStatus CERT_UncacheCRL(CERTCertDBHandle *dbhandle, SECItem *oldcrl);
+
+/*
+** 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);
+extern CERTCertificate *CERT_FindCertByIssuerAndSNCX(
+ CERTCertDBHandle *handle, CERTIssuerAndSN *issuerAndSN, void *wincx);
+
+/*
+** 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);
+
+/*
+** Encode Certificate SKID (Subject Key ID) extension.
+**
+*/
+extern SECStatus CERT_EncodeSubjectKeyID(PLArenaPool *arena,
+ const SECItem *srcString,
+ SECItem *encodedValue);
+
+/*
+** Find a certificate in the database by a nickname
+** "nickname" is the ascii string nickname to look for
+*/
+extern CERTCertificate *CERT_FindCertByNickname(CERTCertDBHandle *handle,
+ const 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,
+ const char *name);
+CERTCertificate *CERT_FindCertByNicknameOrEmailAddrCX(CERTCertDBHandle *handle,
+ const char *name,
+ void *wincx);
+
+/*
+** Find a certificate in the database by a email address or nickname
+** and require it to have the given usage.
+** "name" is the email address or nickname to look up
+*/
+CERTCertificate *CERT_FindCertByNicknameOrEmailAddrForUsage(
+ CERTCertDBHandle *handle, const char *name, SECCertUsage lookingForUsage);
+CERTCertificate *CERT_FindCertByNicknameOrEmailAddrForUsageCX(
+ CERTCertDBHandle *handle, const char *name, SECCertUsage lookingForUsage,
+ void *wincx);
+
+/*
+** 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, PRTime 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(const CERTCertificate *cert,
+ PRTime t,
+ PRBool allowOverride);
+
+/*
+** WARNING - this function is deprecated, 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(const CERTCertificate *c, PRTime *notBefore,
+ PRTime *notAfter);
+
+/*
+** Extract the issuer and serial number from a certificate
+*/
+extern CERTIssuerAndSN *CERT_GetCertIssuerAndSN(PLArenaPool *,
+ 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, PRTime 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(const 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,
+ PRTime 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, PRTime 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, PRTime 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,
+ PRTime t, void *wincx, CERTVerifyLog *log);
+
+/*
+** 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);
+
+/*
+** 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(const SECItem *derAVAValue);
+
+/*
+** extract various element strings from a distinguished name.
+** "name" the distinguished name
+*/
+
+extern char *CERT_GetCertificateEmailAddress(CERTCertificate *cert);
+
+extern char *CERT_GetCertEmailAddress(const CERTName *name);
+
+extern const char *CERT_GetFirstEmailAddress(CERTCertificate *cert);
+
+extern const char *CERT_GetNextEmailAddress(CERTCertificate *cert,
+ const char *prev);
+
+/* The return value must be freed with PORT_Free. */
+extern char *CERT_GetCommonName(const CERTName *name);
+
+extern char *CERT_GetCountryName(const CERTName *name);
+
+extern char *CERT_GetLocalityName(const CERTName *name);
+
+extern char *CERT_GetStateName(const CERTName *name);
+
+extern char *CERT_GetOrgName(const CERTName *name);
+
+extern char *CERT_GetOrgUnitName(const CERTName *name);
+
+extern char *CERT_GetDomainComponentName(const CERTName *name);
+
+extern char *CERT_GetCertUid(const CERTName *name);
+
+/* manipulate the trust parameters of a certificate */
+
+extern SECStatus CERT_GetCertTrust(const 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(PLArenaPool *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);
+
+/*
+** Merge an external list of extensions into a cert's extension list, adding one
+** only when its OID matches none of the cert's existing extensions. Call this
+** immediately before calling CERT_FinishExtensions().
+*/
+SECStatus CERT_MergeExtensions(void *exthandle, CERTCertExtension **exts);
+
+/* 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(PLArenaPool *arena,
+ CERTBasicConstraints *value,
+ SECItem *encodedValue);
+
+/*
+** Encode the value of the authorityKeyIdentifier extension.
+*/
+extern SECStatus CERT_EncodeAuthKeyID(PLArenaPool *arena, CERTAuthKeyID *value,
+ SECItem *encodedValue);
+
+/*
+** Encode the value of the crlDistributionPoints extension.
+*/
+extern SECStatus CERT_EncodeCRLDistributionPoints(
+ PLArenaPool *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,
+ const 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(PLArenaPool *arena,
+ const 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(
+ PLArenaPool *arena, SECItem *der);
+
+/* Extract certain name type from a generalName */
+extern void *CERT_GetGeneralNameByType(CERTGeneralName *genNames,
+ CERTGeneralNameType type,
+ PRBool derFormat);
+
+extern CERTOidSequence *CERT_DecodeOidSequence(const SECItem *seqItem);
+
+/****************************************************************************
+ *
+ * Find extension values of a certificate
+ *
+ ***************************************************************************/
+
+extern SECStatus CERT_FindCertExtension(const 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_FindCertExtensionByOID(CERTCertificate *cert,
+ SECItem *oid, SECItem *value);
+
+/* 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(PLArenaPool *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, PRTime *value);
+
+/*
+** Set up a crl for adding X509v3 extensions. Returns an opaque handle
+** used by routines that take an exthandle (void*) argument .
+** "crl" is the CRL we are adding extensions to
+*/
+extern void *CERT_StartCRLExtensions(CERTCrl *crl);
+
+/*
+** Set up a crl entry for adding X509v3 extensions. Returns an opaque handle
+** used by routines that take an exthandle (void*) argument .
+** "crl" is the crl we are adding certs entries to
+** "entry" is the crl entry we are adding extensions to
+*/
+extern void *CERT_StartCRLEntryExtensions(CERTCrl *crl, CERTCrlEntry *entry);
+
+extern CERTCertNicknames *CERT_GetCertNicknames(CERTCertDBHandle *handle,
+ int what, void *wincx);
+
+/*
+** Finds the crlNumber extension and decodes its value into 'value'
+*/
+extern SECStatus CERT_FindCRLNumberExten(PLArenaPool *arena, CERTCrl *crl,
+ SECItem *value);
+
+extern SECStatus CERT_FindCRLEntryReasonExten(CERTCrlEntry *crlEntry,
+ CERTCRLEntryReasonCode *value);
+
+extern void CERT_FreeNicknames(CERTCertNicknames *nicknames);
+
+extern PRBool CERT_CompareCerts(const CERTCertificate *c1,
+ const 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);
+
+/* Duplicate distinguished name array */
+extern CERTDistNames *CERT_DupDistNames(CERTDistNames *orig);
+
+/*
+** Generate an array of Distinguished names from an array of nicknames
+*/
+extern CERTDistNames *CERT_DistNamesFromNicknames(CERTCertDBHandle *handle,
+ char **nicknames, int nnames);
+
+/*
+** Generate an array of Distinguished names from a list of certs.
+*/
+extern CERTDistNames *CERT_DistNamesFromCertList(CERTCertList *list);
+
+/*
+** 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(
+ const 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(const char *emailAddr);
+
+/* decode string representation of trust flags into trust struct */
+SECStatus CERT_DecodeTrustString(CERTCertTrust *trust, const 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);
+
+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);
+
+CERTCertificatePolicies *CERT_DecodeCertificatePoliciesExtension(
+ const SECItem *extnValue);
+
+void CERT_DestroyCertificatePoliciesExtension(
+ CERTCertificatePolicies *policies);
+
+CERTCertificatePolicyMappings *CERT_DecodePolicyMappingsExtension(
+ SECItem *encodedCertPolicyMaps);
+
+SECStatus CERT_DestroyPolicyMappingsExtension(
+ CERTCertificatePolicyMappings *mappings);
+
+SECStatus CERT_DecodePolicyConstraintsExtension(
+ CERTCertificatePolicyConstraints *decodedValue,
+ const SECItem *encodedValue);
+
+SECStatus CERT_DecodeInhibitAnyExtension(
+ CERTCertificateInhibitAny *decodedValue, SECItem *extnValue);
+
+CERTUserNotice *CERT_DecodeUserNotice(SECItem *noticeItem);
+
+extern CERTGeneralName *CERT_DecodeAltNameExtension(PLArenaPool *reqArena,
+ SECItem *EncodedAltName);
+
+extern CERTNameConstraints *CERT_DecodeNameConstraintsExtension(
+ PLArenaPool *arena, const SECItem *encodedConstraints);
+
+/* returns addr of a NULL termainated array of pointers to CERTAuthInfoAccess */
+extern CERTAuthInfoAccess **CERT_DecodeAuthInfoAccessExtension(
+ PLArenaPool *reqArena, const SECItem *encodedExtension);
+
+extern CERTPrivKeyUsagePeriod *CERT_DecodePrivKeyUsagePeriodExtension(
+ PLArenaPool *arena, SECItem *extnValue);
+
+extern CERTGeneralName *CERT_GetNextGeneralName(CERTGeneralName *current);
+
+extern CERTGeneralName *CERT_GetPrevGeneralName(CERTGeneralName *current);
+
+/*
+ * Look up name constraints for some certs that do not include name constraints
+ * (Most importantly, root certificates)
+ *
+ * If a matching subject is found, |extensions| will be populated with a copy of
+ * the
+ * DER-encoded name constraints extension. The data in |extensions| will point
+ * to
+ * memory that the caller owns.
+ *
+ * There is no mechanism to configure imposed name constraints right now. All
+ * imposed name constraints are built into NSS.
+ */
+SECStatus CERT_GetImposedNameConstraints(const SECItem *derSubject,
+ SECItem *extensions);
+
+CERTNameConstraint *CERT_GetNextNameConstraint(CERTNameConstraint *current);
+
+CERTNameConstraint *CERT_GetPrevNameConstraint(CERTNameConstraint *current);
+
+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);
+
+CERTCertList *CERT_MatchUserCert(CERTCertDBHandle *handle, SECCertUsage usage,
+ int nCANames, char **caNames, void *proto_win);
+
+CERTCertList *CERT_NewCertList(void);
+
+/* free the cert list and all the certs in the list */
+void CERT_DestroyCertList(CERTCertList *certs);
+
+/* remove the node and free the cert */
+void CERT_RemoveCertListNode(CERTCertListNode *node);
+
+/* equivalent to CERT_AddCertToListTailWithData(certs, cert, NULL) */
+SECStatus CERT_AddCertToListTail(CERTCertList *certs, CERTCertificate *cert);
+
+/* equivalent to CERT_AddCertToListHeadWithData(certs, cert, NULL) */
+SECStatus CERT_AddCertToListHead(CERTCertList *certs, CERTCertificate *cert);
+
+/*
+ * The new cert list node takes ownership of "cert". "cert" is freed
+ * when the list node is removed.
+ */
+SECStatus CERT_AddCertToListTailWithData(CERTCertList *certs,
+ CERTCertificate *cert, void *appData);
+
+/*
+ * The new cert list node takes ownership of "cert". "cert" is freed
+ * when the list node is removed.
+ */
+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,
+ PLArenaPool *arena);
+
+CERTGeneralName *CERT_GetConstrainedCertificateNames(
+ const CERTCertificate *cert, PLArenaPool *arena,
+ PRBool includeSubjectCommonName);
+
+/*
+ * 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,
+ const SECItem *name, PRTime 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,
+ const 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);
+
+/*
+ * Filter a list of certificates, removing those certs that don't match the
+ * nickname.
+ */
+SECStatus CERT_FilterCertListByNickname(CERTCertList *certList, char *nickname,
+ void *pwarg);
+
+/* return true if cert is in cert list */
+PRBool CERT_IsInList(const CERTCertificate *cert, const CERTCertList *certList);
+
+/* returned certList is the intersection of the certs on certList and the
+ * certs on filterList */
+SECStatus CERT_FilterCertListByCertList(CERTCertList *certList,
+ const CERTCertList *filterList);
+
+/*
+ * 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(PLArenaPool *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,
+ PRTime validTime, PRBool validOnly);
+
+/*
+ * 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);
+
+/*
+ * Release the cert reference count lock
+ */
+void CERT_UnlockCertRefCount(CERTCertificate *cert);
+
+/*
+ * Digest the cert's subject public key using the specified algorithm.
+ * NOTE: this digests the value of the BIT STRING subjectPublicKey (excluding
+ * the tag, length, and number of unused bits) rather than the whole
+ * subjectPublicKeyInfo field.
+ *
+ * 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_GetSubjectPublicKeyDigest(PLArenaPool *arena,
+ const CERTCertificate *cert,
+ SECOidTag digestAlg,
+ SECItem *fill);
+
+/*
+ * Digest the cert's subject name using the specified algorithm.
+ */
+extern SECItem *CERT_GetSubjectNameDigest(PLArenaPool *arena,
+ const CERTCertificate *cert,
+ SECOidTag digestAlg, SECItem *fill);
+
+SECStatus CERT_CheckCRL(CERTCertificate *cert, CERTCertificate *issuer,
+ const SECItem *dp, PRTime t, void *wincx);
+
+/*
+ * Add a CERTNameConstraint to the CERTNameConstraint list
+ */
+extern CERTNameConstraint *CERT_AddNameConstraint(
+ CERTNameConstraint *list, CERTNameConstraint *constraint);
+
+/*
+ * Allocate space and copy CERTNameConstraint from src to dest.
+ * Arena is used to allocate result(if dest eq NULL) and its members
+ * SECItem data.
+ */
+extern CERTNameConstraint *CERT_CopyNameConstraint(PLArenaPool *arena,
+ CERTNameConstraint *dest,
+ CERTNameConstraint *src);
+
+/*
+ * Verify name against all the constraints relevant to that type of
+ * the name.
+ */
+extern SECStatus CERT_CheckNameSpace(PLArenaPool *arena,
+ const CERTNameConstraints *constraints,
+ const CERTGeneralName *currentName);
+
+/*
+ * Extract and allocate the name constraints extension from the CA cert.
+ * If the certificate contains no name constraints extension, but
+ * CERT_GetImposedNameConstraints returns a name constraints extension
+ * for the subject of the certificate, then that extension will be returned.
+ */
+extern SECStatus CERT_FindNameConstraintsExten(
+ PLArenaPool *arena, CERTCertificate *cert,
+ CERTNameConstraints **constraints);
+
+/*
+ * Initialize a new GERTGeneralName fields (link)
+ */
+extern CERTGeneralName *CERT_NewGeneralName(PLArenaPool *arena,
+ CERTGeneralNameType type);
+
+/*
+ * Lookup a CERTGeneralNameType constant by its human readable string.
+ */
+extern CERTGeneralNameType CERT_GetGeneralNameTypeFromString(
+ const char *string);
+
+/*
+ * PKIX extension encoding routines
+ */
+extern SECStatus CERT_EncodePolicyConstraintsExtension(
+ PLArenaPool *arena, CERTCertificatePolicyConstraints *constr,
+ SECItem *dest);
+extern SECStatus CERT_EncodeInhibitAnyExtension(
+ PLArenaPool *arena, CERTCertificateInhibitAny *inhibitAny, SECItem *dest);
+extern SECStatus CERT_EncodePolicyMappingExtension(
+ PLArenaPool *arena, CERTCertificatePolicyMappings *maps, SECItem *dest);
+
+extern SECStatus CERT_EncodeInfoAccessExtension(PLArenaPool *arena,
+ CERTAuthInfoAccess **info,
+ SECItem *dest);
+extern SECStatus CERT_EncodeUserNotice(PLArenaPool *arena,
+ CERTUserNotice *notice, SECItem *dest);
+
+extern SECStatus CERT_EncodeDisplayText(PLArenaPool *arena, SECItem *text,
+ SECItem *dest);
+
+extern SECStatus CERT_EncodeCertPoliciesExtension(PLArenaPool *arena,
+ CERTPolicyInfo **info,
+ SECItem *dest);
+extern SECStatus CERT_EncodeNoticeReference(PLArenaPool *arena,
+ CERTNoticeReference *reference,
+ SECItem *dest);
+
+/*
+ * Returns a pointer to a static structure.
+ */
+extern const CERTRevocationFlags *CERT_GetPKIXVerifyNistRevocationPolicy(void);
+
+/*
+ * Returns a pointer to a static structure.
+ */
+extern const CERTRevocationFlags *CERT_GetClassicOCSPEnabledSoftFailurePolicy(
+ void);
+
+/*
+ * Returns a pointer to a static structure.
+ */
+extern const CERTRevocationFlags *CERT_GetClassicOCSPEnabledHardFailurePolicy(
+ void);
+
+/*
+ * Returns a pointer to a static structure.
+ */
+extern const CERTRevocationFlags *CERT_GetClassicOCSPDisabledPolicy(void);
+
+/*
+ * Verify a Cert with libpkix
+ * paramsIn control the verification options. If a value isn't specified
+ * in paramsIn, it reverts to the application default.
+ * paramsOut specifies the parameters the caller would like to get back.
+ * the caller may pass NULL, in which case no parameters are returned.
+ */
+extern SECStatus CERT_PKIXVerifyCert(CERTCertificate *cert,
+ SECCertificateUsage usages,
+ CERTValInParam *paramsIn,
+ CERTValOutParam *paramsOut, void *wincx);
+
+/* Makes old cert validation APIs(CERT_VerifyCert, CERT_VerifyCertificate)
+ * to use libpkix validation engine. The function should be called ones at
+ * application initialization time.
+ * Function is not thread safe.*/
+extern SECStatus CERT_SetUsePKIXForValidation(PRBool enable);
+
+/* The function return PR_TRUE if cert validation should use
+ * libpkix cert validation engine. */
+extern PRBool CERT_GetUsePKIXForValidation(void);
+
+/*
+ * Allocate a parameter container of type CERTRevocationFlags,
+ * and allocate the inner arrays of the given sizes.
+ * To cleanup call CERT_DestroyCERTRevocationFlags.
+ */
+extern CERTRevocationFlags *CERT_AllocCERTRevocationFlags(
+ PRUint32 number_leaf_methods, PRUint32 number_leaf_pref_methods,
+ PRUint32 number_chain_methods, PRUint32 number_chain_pref_methods);
+
+/*
+ * Destroy the arrays inside flags,
+ * and destroy the object pointed to by flags, too.
+ */
+extern void CERT_DestroyCERTRevocationFlags(CERTRevocationFlags *flags);
+
+/*
+ * Get istemp and isperm fields from a cert in a thread safe way.
+ */
+extern SECStatus CERT_GetCertIsTemp(const CERTCertificate *cert, PRBool *istemp);
+extern SECStatus CERT_GetCertIsPerm(const CERTCertificate *cert, PRBool *isperm);
+
+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 0000000000..0d7cdab768
--- /dev/null
+++ b/security/nss/lib/certdb/certdb.c
@@ -0,0 +1,3411 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Certificate handling code
+ */
+
+#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 "certdb.h"
+#include "prprf.h"
+#include "sechash.h"
+#include "prlong.h"
+#include "certxutl.h"
+#include "portreg.h"
+#include "secerr.h"
+#include "sslerr.h"
+#include "pk11func.h"
+#include "xconst.h" /* for CERT_DecodeAltNameExtension */
+
+#include "pki.h"
+#include "pki3hack.h"
+
+SEC_ASN1_MKSUB(CERT_TimeChoiceTemplate)
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+SEC_ASN1_MKSUB(SEC_BitStringTemplate)
+SEC_ASN1_MKSUB(SEC_IntegerTemplate)
+SEC_ASN1_MKSUB(SEC_SkipTemplate)
+
+/*
+ * 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_TimeChoiceTemplate[] = {
+ { SEC_ASN1_CHOICE, offsetof(SECItem, type), 0, sizeof(SECItem) },
+ { SEC_ASN1_UTC_TIME, 0, 0, siUTCTime },
+ { SEC_ASN1_GENERALIZED_TIME, 0, 0, siGeneralizedTime },
+ { 0 }
+};
+
+const SEC_ASN1Template CERT_ValidityTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTValidity) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTValidity, notBefore),
+ SEC_ASN1_SUB(CERT_TimeChoiceTemplate), 0 },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTValidity, notAfter),
+ SEC_ASN1_SUB(CERT_TimeChoiceTemplate), 0 },
+ { 0 }
+};
+
+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 | SEC_ASN1_XTRN | 0, /* XXX DER_DEFAULT */
+ offsetof(CERTCertificate, version),
+ SEC_ASN1_SUB(SEC_IntegerTemplate) },
+ { SEC_ASN1_INTEGER, offsetof(CERTCertificate, serialNumber) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCertificate, signature),
+ SEC_ASN1_SUB(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_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
+ offsetof(CERTCertificate, issuerID),
+ SEC_ASN1_SUB(SEC_BitStringTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2,
+ offsetof(CERTCertificate, subjectID),
+ SEC_ASN1_SUB(SEC_BitStringTemplate) },
+ { 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 | SEC_ASN1_XTRN,
+ offsetof(CERTCertificate, signatureWrap.signatureAlgorithm),
+ SEC_ASN1_SUB(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 | SEC_ASN1_XTRN | 0,
+ 0, SEC_ASN1_SUB(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 | SEC_ASN1_XTRN | 0,
+ 0, SEC_ASN1_SUB(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 | SEC_ASN1_XTRN | 0,
+ 0, SEC_ASN1_SUB(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 | SEC_ASN1_XTRN | 0,
+ 0, SEC_ASN1_SUB(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_TimeChoiceTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SignedCertificateTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SequenceOfCertExtensionTemplate)
+
+SECStatus
+CERT_KeyFromIssuerAndSN(PLArenaPool *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;
+ PLArenaPool *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;
+ PORTCheapArenaPool tmpArena;
+ CERTSignedData sd;
+ void *tmpptr;
+
+ PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+
+ PORT_Memset(&sd, 0, sizeof(CERTSignedData));
+ rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &sd, CERT_SignedDataTemplate,
+ derCert);
+ if (rv) {
+ goto loser;
+ }
+
+ PORT_Memset(derName, 0, sizeof(SECItem));
+ rv = SEC_QuickDERDecodeItem(&tmpArena.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_DestroyCheapArena(&tmpArena);
+ return (SECSuccess);
+
+loser:
+ PORT_DestroyCheapArena(&tmpArena);
+ return (SECFailure);
+}
+
+SECStatus
+CERT_SerialNumberFromDERCert(SECItem *derCert, SECItem *derName)
+{
+ int rv;
+ PORTCheapArenaPool tmpArena;
+ CERTSignedData sd;
+ void *tmpptr;
+
+ PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+
+ PORT_Memset(&sd, 0, sizeof(CERTSignedData));
+ rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &sd, CERT_SignedDataTemplate,
+ derCert);
+ if (rv) {
+ goto loser;
+ }
+
+ PORT_Memset(derName, 0, sizeof(SECItem));
+ rv = SEC_QuickDERDecodeItem(&tmpArena.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_DestroyCheapArena(&tmpArena);
+ return (SECSuccess);
+
+loser:
+ PORT_DestroyCheapArena(&tmpArena);
+ return (SECFailure);
+}
+
+/*
+ * Generate a database key, based on serial number and issuer, from a
+ * DER certificate.
+ */
+SECStatus
+CERT_KeyFromDERCert(PLArenaPool *reqArena, SECItem *derCert, SECItem *key)
+{
+ int rv;
+ CERTSignedData sd;
+ CERTCertKey certkey;
+
+ if (!reqArena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ PORT_Memset(&sd, 0, sizeof(CERTSignedData));
+ rv =
+ SEC_QuickDERDecodeItem(reqArena, &sd, CERT_SignedDataTemplate, derCert);
+
+ if (rv) {
+ goto loser;
+ }
+
+ PORT_Memset(&certkey, 0, sizeof(CERTCertKey));
+ rv = SEC_QuickDERDecodeItem(reqArena, &certkey, CERT_CertKeyTemplate,
+ &sd.data);
+
+ if (rv) {
+ goto loser;
+ }
+
+ return (CERT_KeyFromIssuerAndSN(reqArena, &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.len ? tmpitem.data[0] : 0;
+ cert->keyUsagePresent = PR_TRUE;
+ cert->keyUsage = cert->rawKeyUsage;
+
+ 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);
+}
+
+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)
+{
+ PRUint32 nsCertType;
+
+ if (cert->nsCertType) {
+ /* once set, no need to recalculate */
+ return SECSuccess;
+ }
+ nsCertType = cert_ComputeCertType(cert);
+
+ /* Assert that it is safe to cast &cert->nsCertType to "PRInt32 *" */
+ PORT_Assert(sizeof(cert->nsCertType) == sizeof(PRInt32));
+ PR_ATOMIC_SET((PRInt32 *)&cert->nsCertType, nsCertType);
+ return SECSuccess;
+}
+
+PRBool
+cert_IsIPsecOID(CERTOidSequence *extKeyUsage)
+{
+ if (findOIDinOIDSeqByTagNum(
+ extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_IKE) == SECSuccess) {
+ return PR_TRUE;
+ }
+ if (findOIDinOIDSeqByTagNum(
+ extKeyUsage, SEC_OID_IPSEC_IKE_END) == SECSuccess) {
+ return PR_TRUE;
+ }
+ if (findOIDinOIDSeqByTagNum(
+ extKeyUsage, SEC_OID_IPSEC_IKE_INTERMEDIATE) == SECSuccess) {
+ return PR_TRUE;
+ }
+ /* these are now deprecated, but may show up. Treat them the same as IKE */
+ if (findOIDinOIDSeqByTagNum(
+ extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_END) == SECSuccess) {
+ return PR_TRUE;
+ }
+ if (findOIDinOIDSeqByTagNum(
+ extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_TUNNEL) == SECSuccess) {
+ return PR_TRUE;
+ }
+ if (findOIDinOIDSeqByTagNum(
+ extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_USER) == SECSuccess) {
+ return PR_TRUE;
+ }
+ /* this one should probably be in cert_ComputeCertType and set all usages? */
+ if (findOIDinOIDSeqByTagNum(
+ extKeyUsage, SEC_OID_X509_ANY_EXT_KEY_USAGE) == SECSuccess) {
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+PRUint32
+cert_ComputeCertType(CERTCertificate *cert)
+{
+ SECStatus rv;
+ SECItem tmpitem;
+ SECItem encodedExtKeyUsage;
+ CERTOidSequence *extKeyUsage = NULL;
+ CERTBasicConstraints basicConstraint;
+ PRUint32 nsCertType = 0;
+ PRBool isCA = PR_FALSE;
+
+ tmpitem.data = NULL;
+ CERT_FindNSCertTypeExtension(cert, &tmpitem);
+ encodedExtKeyUsage.data = NULL;
+ 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) {
+ isCA = basicConstraint.isCA;
+ }
+ if (tmpitem.data != NULL || extKeyUsage != NULL) {
+ if (tmpitem.data == NULL || tmpitem.len == 0) {
+ 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 &&
+ cert->emailAddr[0]) {
+ 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) {
+ nsCertType |= isCA ? NS_CERT_TYPE_EMAIL_CA : NS_CERT_TYPE_EMAIL;
+ }
+ if (findOIDinOIDSeqByTagNum(
+ extKeyUsage, SEC_OID_EXT_KEY_USAGE_SERVER_AUTH) == SECSuccess) {
+ nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_SERVER;
+ }
+ /*
+ * Treat certs with step-up OID as also having SSL server type.
+ * COMODO needs this behaviour until June 2020. See Bug 737802.
+ */
+ if (findOIDinOIDSeqByTagNum(extKeyUsage,
+ SEC_OID_NS_KEY_USAGE_GOVT_APPROVED) ==
+ SECSuccess) {
+ nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_SERVER;
+ }
+ if (findOIDinOIDSeqByTagNum(
+ extKeyUsage, SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH) == SECSuccess) {
+ nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_CLIENT;
+ }
+ if (cert_IsIPsecOID(extKeyUsage)) {
+ nsCertType |= isCA ? NS_CERT_TYPE_IPSEC_CA : NS_CERT_TYPE_IPSEC;
+ }
+ if (findOIDinOIDSeqByTagNum(
+ extKeyUsage, SEC_OID_EXT_KEY_USAGE_CODE_SIGN) == SECSuccess) {
+ nsCertType |= isCA ? NS_CERT_TYPE_OBJECT_SIGNING_CA : 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 NS Cert Type extension and no EKU extension, then */
+ nsCertType = 0;
+ if (CERT_IsCACert(cert, &nsCertType))
+ nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER;
+ /* if the basic constraint extension says the cert is a CA, then
+ allow SSL CA and EMAIL CA and Status Responder */
+ if (isCA) {
+ nsCertType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA |
+ EXT_KEY_USAGE_STATUS_RESPONDER);
+ }
+ /* 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;
+ }
+
+ /* IPSEC is allowed to use SSL client and server certs as well as email certs */
+ if (nsCertType & (NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_EMAIL)) {
+ nsCertType |= NS_CERT_TYPE_IPSEC;
+ }
+ if (nsCertType & (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA)) {
+ nsCertType |= NS_CERT_TYPE_IPSEC_CA;
+ }
+
+ if (encodedExtKeyUsage.data != NULL) {
+ PORT_Free(encodedExtKeyUsage.data);
+ }
+ if (extKeyUsage != NULL) {
+ CERT_DestroyOidSequence(extKeyUsage);
+ }
+ return nsCertType;
+}
+
+/*
+ * cert_GetKeyID() - extract or generate the subjectKeyID from a certificate
+ */
+SECStatus
+cert_GetKeyID(CERTCertificate *cert)
+{
+ SECItem tmpitem;
+ SECStatus rv;
+
+ 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, 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;
+ PLArenaPool *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) {
+ cert->options.bits.hasUnsupportedCriticalExt = PR_TRUE;
+ }
+
+ /* 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;
+ }
+
+ /* determine if this is a root cert */
+ cert->isRoot = cert_IsRootCert(cert);
+
+ /* initialize the certType */
+ rv = cert_GetCertType(cert);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ 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);
+}
+
+CERTValidity *
+CERT_CreateValidity(PRTime notBefore, PRTime notAfter)
+{
+ CERTValidity *v;
+ int rv;
+ PLArenaPool *arena;
+
+ if (notBefore > notAfter) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+
+ if (!arena) {
+ return (0);
+ }
+
+ v = (CERTValidity *)PORT_ArenaZAlloc(arena, sizeof(CERTValidity));
+ if (v) {
+ v->arena = arena;
+ rv = DER_EncodeTimeChoice(arena, &v->notBefore, notBefore);
+ if (rv)
+ goto loser;
+ rv = DER_EncodeTimeChoice(arena, &v->notAfter, notAfter);
+ if (rv)
+ goto loser;
+ }
+ return v;
+
+loser:
+ CERT_DestroyValidity(v);
+ return 0;
+}
+
+SECStatus
+CERT_CopyValidity(PLArenaPool *arena, CERTValidity *to, CERTValidity *from)
+{
+ SECStatus rv;
+
+ CERT_DestroyValidity(to);
+ to->arena = arena;
+
+ rv = SECITEM_CopyItem(arena, &to->notBefore, &from->notBefore);
+ if (rv)
+ return rv;
+ rv = SECITEM_CopyItem(arena, &to->notAfter, &from->notAfter);
+ return rv;
+}
+
+void
+CERT_DestroyValidity(CERTValidity *v)
+{
+ if (v && v->arena) {
+ PORT_FreeArena(v->arena, PR_FALSE);
+ }
+ return;
+}
+
+/*
+** 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(const CERTCertificate *c, PRTime *notBefore, PRTime *notAfter)
+{
+ SECStatus rv;
+
+ if (!c || !notBefore || !notAfter) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* convert DER not-before time */
+ rv = DER_DecodeTimeChoice(notBefore, &c->validity.notBefore);
+ if (rv) {
+ return (SECFailure);
+ }
+
+ /* convert DER not-after time */
+ rv = DER_DecodeTimeChoice(notAfter, &c->validity.notAfter);
+ if (rv) {
+ return (SECFailure);
+ }
+
+ return (SECSuccess);
+}
+
+/*
+ * Check the validity times of a certificate
+ */
+SECCertTimeValidity
+CERT_CheckCertValidTimes(const CERTCertificate *c, PRTime t,
+ PRBool allowOverride)
+{
+ PRTime notBefore, notAfter, llPendingSlop, tmp1;
+ SECStatus rv;
+
+ if (!c) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return (secCertTimeUndetermined);
+ }
+ /* if cert is already marked OK, then don't bother to check */
+ if (allowOverride && c->timeOK) {
+ return (secCertTimeValid);
+ }
+
+ rv = CERT_GetCertTimes(c, &notBefore, &notAfter);
+
+ 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_DecodeTimeChoice(notBefore, &date->lastUpdate);
+ if (rv) {
+ return (SECFailure);
+ }
+
+ /* convert DER not-after time */
+ if (date->nextUpdate.data) {
+ rv = DER_DecodeTimeChoice(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;
+
+ if (!crl) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return (secCertTimeUndetermined);
+ }
+
+ rv = SEC_GetCrlTimes(crl, &notBefore, &notAfter);
+
+ 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)) {
+ PORT_SetError(SEC_ERROR_CRL_EXPIRED);
+ 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)) {
+ PORT_SetError(SEC_ERROR_CRL_EXPIRED);
+ 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 certUsageIPsec:
+ requiredKeyUsage = KU_KEY_CERT_SIGN;
+ requiredCertType = NS_CERT_TYPE_IPSEC_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 certUsageVerifyCA:
+ 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:
+ /*
+ * RFC 5280 lists digitalSignature and keyAgreement for
+ * id-kp-clientAuth. NSS does not support the *_fixed_dh and
+ * *_fixed_ecdh client certificate types.
+ */
+ 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 certUsageIPsec:
+ /* RFC 4945 Section 5.1.3.2 */
+ requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION;
+ requiredCertType = NS_CERT_TYPE_IPSEC;
+ 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_OR_NON_REPUDIATION;
+ requiredCertType = NS_CERT_TYPE_EMAIL;
+ break;
+ case certUsageEmailRecipient:
+ requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT;
+ requiredCertType = NS_CERT_TYPE_EMAIL;
+ break;
+ case certUsageObjectSigner:
+ /* RFC 5280 lists only digitalSignature for id-kp-codeSigning.
+ */
+ requiredKeyUsage = KU_DIGITAL_SIGNATURE;
+ requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING;
+ break;
+ case certUsageStatusResponder:
+ requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION;
+ 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)
+{
+ if (!cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ /* choose between key agreement or key encipherment based on key
+ * type in cert
+ */
+ if (requiredUsage & KU_KEY_AGREEMENT_OR_ENCIPHERMENT) {
+ KeyType keyType = CERT_GetCertKeyType(&cert->subjectPublicKeyInfo);
+ /* turn off the special bit */
+ requiredUsage &= (~KU_KEY_AGREEMENT_OR_ENCIPHERMENT);
+
+ switch (keyType) {
+ case rsaKey:
+ requiredUsage |= KU_KEY_ENCIPHERMENT;
+ break;
+ case rsaPssKey:
+ case dsaKey:
+ requiredUsage |= KU_DIGITAL_SIGNATURE;
+ break;
+ case dhKey:
+ requiredUsage |= KU_KEY_AGREEMENT;
+ break;
+ case ecKey:
+ /* Accept either signature or agreement. */
+ if (!(cert->keyUsage &
+ (KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT)))
+ goto loser;
+ break;
+ default:
+ goto loser;
+ }
+ }
+
+ /* Allow either digital signature or non-repudiation */
+ if (requiredUsage & KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION) {
+ /* turn off the special bit */
+ requiredUsage &= (~KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION);
+
+ if (!(cert->keyUsage & (KU_DIGITAL_SIGNATURE | KU_NON_REPUDIATION)))
+ goto loser;
+ }
+
+ if ((cert->keyUsage & requiredUsage) == requiredUsage)
+ return SECSuccess;
+
+loser:
+ PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
+ return SECFailure;
+}
+
+CERTCertificate *
+CERT_DupCertificate(CERTCertificate *c)
+{
+ if (c) {
+ NSSCertificate *tmp = STAN_GetNSSCertificate(c);
+ nssCertificate_AddRef(tmp);
+ }
+ return c;
+}
+
+SECStatus
+CERT_GetCertificateDer(const CERTCertificate *c, SECItem *der)
+{
+ if (!c || !der) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ *der = c->derCert;
+ return SECSuccess;
+}
+
+/*
+ * 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;
+}
+
+static PRBool
+cert_IsIPAddr(const char *hn)
+{
+ PRBool isIPaddr = PR_FALSE;
+ PRNetAddr netAddr;
+ isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr));
+ return isIPaddr;
+}
+
+/*
+** 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));
+ if (!domainOK) {
+ return SECFailure; /* error code is already set. */
+ }
+ domainOK->name = (char *)PORT_ArenaZAlloc(cert->arena, newNameLen + 1);
+ if (!domainOK->name) {
+ return SECFailure; /* error code is already set. */
+ }
+
+ PORT_Strncpy(domainOK->name, hn, newNameLen + 1);
+ 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.
+**
+** This function may modify string cn, so caller must pass a modifiable copy.
+*/
+static SECStatus
+cert_TestHostName(char *cn, const char *hn)
+{
+ static int useShellExp = -1;
+
+ if (useShellExp < 0) {
+ useShellExp = (NULL != PR_GetEnvSecure("NSS_USE_SHEXP_IN_CERT_NAME"));
+ }
+ if (useShellExp) {
+ /* Backward compatible code, uses Shell Expressions (SHEXP). */
+ int 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;
+ }
+ } else {
+ /* New approach conforms to RFC 6125. */
+ char *wildcard = PORT_Strchr(cn, '*');
+ char *firstcndot = PORT_Strchr(cn, '.');
+ char *secondcndot =
+ firstcndot ? PORT_Strchr(firstcndot + 1, '.') : NULL;
+ char *firsthndot = PORT_Strchr(hn, '.');
+
+ /* For a cn pattern to be considered valid, the wildcard character...
+ * - may occur only in a DNS name with at least 3 components, and
+ * - may occur only as last character in the first component, and
+ * - may be preceded by additional characters, and
+ * - must not be preceded by an IDNA ACE prefix (xn--)
+ */
+ if (wildcard && secondcndot && secondcndot[1] && firsthndot &&
+ firstcndot - wildcard == 1 /* wildcard is last char in first component */
+ && secondcndot - firstcndot > 1 /* second component is non-empty */
+ && PORT_Strrchr(cn, '*') == wildcard /* only one wildcard in cn */
+ && !PORT_Strncasecmp(cn, hn, wildcard - cn) &&
+ !PORT_Strcasecmp(firstcndot, firsthndot)
+ /* If hn starts with xn--, then cn must start with wildcard */
+ && (PORT_Strncasecmp(hn, "xn--", 4) || wildcard == cn)) {
+ /* valid wildcard pattern match */
+ return SECSuccess;
+ }
+ }
+ /* String cn has no wildcard or shell expression.
+ * Compare entire string hn with cert name.
+ */
+ if (PORT_Strcasecmp(hn, cn) == 0) {
+ return SECSuccess;
+ }
+
+ PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
+ return SECFailure;
+}
+
+SECStatus
+cert_VerifySubjectAltName(const CERTCertificate *cert, const char *hn)
+{
+ PLArenaPool *arena = NULL;
+ CERTGeneralName *nameList = NULL;
+ CERTGeneralName *current;
+ char *cn;
+ int cnBufLen;
+ int DNSextCount = 0;
+ int IPextCount = 0;
+ PRBool isIPaddr = PR_FALSE;
+ SECStatus rv = SECFailure;
+ SECItem subAltName;
+ PRNetAddr netAddr;
+ char cnbuf[128];
+
+ subAltName.data = NULL;
+ cn = cnbuf;
+ cnBufLen = sizeof cnbuf;
+
+ rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
+ &subAltName);
+ if (rv != SECSuccess) {
+ goto fail;
+ }
+ isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr));
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena)
+ goto fail;
+
+ nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName);
+ if (!current)
+ goto fail;
+
+ 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;
+ rv = CERT_RFC1485_EscapeAndQuote(
+ cn, cnBufLen, (char *)current->name.other.data, cnLen);
+ if (rv != SECSuccess &&
+ PORT_GetError() == SEC_ERROR_OUTPUT_LEN) {
+ cnBufLen =
+ cnLen * 3 + 3; /* big enough for worst case */
+ cn = (char *)PORT_ArenaAlloc(arena, cnBufLen);
+ if (!cn)
+ goto fail;
+ rv = CERT_RFC1485_EscapeAndQuote(
+ cn, cnBufLen, (char *)current->name.other.data,
+ cnLen);
+ }
+ if (rv == SECSuccess)
+ 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_GetNextGeneralName(current);
+ } while (current != nameList);
+
+fail:
+
+ if (!(isIPaddr ? IPextCount : DNSextCount)) {
+ /* 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;
+}
+
+/*
+ * If found:
+ * - subAltName contains the extension (caller must free)
+ * - return value is the decoded namelist (allocated off arena)
+ * if not found, or if failure to decode:
+ * - return value is NULL
+ */
+CERTGeneralName *
+cert_GetSubjectAltNameList(const CERTCertificate *cert, PLArenaPool *arena)
+{
+ CERTGeneralName *nameList = NULL;
+ SECStatus rv = SECFailure;
+ SECItem subAltName;
+
+ if (!cert || !arena)
+ return NULL;
+
+ subAltName.data = NULL;
+
+ rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
+ &subAltName);
+ if (rv != SECSuccess)
+ return NULL;
+
+ nameList = CERT_DecodeAltNameExtension(arena, &subAltName);
+ SECITEM_FreeItem(&subAltName, PR_FALSE);
+ return nameList;
+}
+
+PRUint32
+cert_CountDNSPatterns(CERTGeneralName *firstName)
+{
+ CERTGeneralName *current;
+ PRUint32 count = 0;
+
+ if (!firstName)
+ return 0;
+
+ current = firstName;
+ do {
+ switch (current->type) {
+ case certDNSName:
+ case certIPAddress:
+ ++count;
+ break;
+ default:
+ break;
+ }
+ current = CERT_GetNextGeneralName(current);
+ } while (current != firstName);
+
+ return count;
+}
+
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
+/* will fill nickNames,
+ * will allocate all data from nickNames->arena,
+ * numberOfGeneralNames should have been obtained from cert_CountDNSPatterns,
+ * will ensure the numberOfGeneralNames matches the number of output entries.
+ */
+SECStatus
+cert_GetDNSPatternsFromGeneralNames(CERTGeneralName *firstName,
+ PRUint32 numberOfGeneralNames,
+ CERTCertNicknames *nickNames)
+{
+ CERTGeneralName *currentInput;
+ char **currentOutput;
+
+ if (!firstName || !nickNames || !numberOfGeneralNames)
+ return SECFailure;
+
+ nickNames->numnicknames = numberOfGeneralNames;
+ nickNames->nicknames = PORT_ArenaAlloc(
+ nickNames->arena, sizeof(char *) * numberOfGeneralNames);
+ if (!nickNames->nicknames)
+ return SECFailure;
+
+ currentInput = firstName;
+ currentOutput = nickNames->nicknames;
+ do {
+ char *cn = NULL;
+ char ipbuf[INET6_ADDRSTRLEN];
+ PRNetAddr addr;
+
+ if (numberOfGeneralNames < 1) {
+ /* internal consistency error */
+ return SECFailure;
+ }
+
+ switch (currentInput->type) {
+ case certDNSName:
+ /* DNS name currentInput->name.other.data is not null
+ *terminated.
+ ** so must copy it.
+ */
+ cn = (char *)PORT_ArenaAlloc(nickNames->arena,
+ currentInput->name.other.len + 1);
+ if (!cn)
+ return SECFailure;
+ PORT_Memcpy(cn, currentInput->name.other.data,
+ currentInput->name.other.len);
+ cn[currentInput->name.other.len] = 0;
+ break;
+ case certIPAddress:
+ if (currentInput->name.other.len == 4) {
+ addr.inet.family = PR_AF_INET;
+ memcpy(&addr.inet.ip, currentInput->name.other.data,
+ currentInput->name.other.len);
+ } else if (currentInput->name.other.len == 16) {
+ addr.ipv6.family = PR_AF_INET6;
+ memcpy(&addr.ipv6.ip, currentInput->name.other.data,
+ currentInput->name.other.len);
+ }
+ if (PR_NetAddrToString(&addr, ipbuf, sizeof(ipbuf)) ==
+ PR_FAILURE)
+ return SECFailure;
+ cn = PORT_ArenaStrdup(nickNames->arena, ipbuf);
+ if (!cn)
+ return SECFailure;
+ break;
+ default:
+ break;
+ }
+ if (cn) {
+ *currentOutput = cn;
+ nickNames->totallen += PORT_Strlen(cn);
+ ++currentOutput;
+ --numberOfGeneralNames;
+ }
+ currentInput = CERT_GetNextGeneralName(currentInput);
+ } while (currentInput != firstName);
+
+ return (numberOfGeneralNames == 0) ? SECSuccess : SECFailure;
+}
+
+/*
+ * Collect all valid DNS names from the given cert.
+ * The output arena will reference some temporaray data,
+ * but this saves us from dealing with two arenas.
+ * The caller may free all data by freeing CERTCertNicknames->arena.
+ */
+CERTCertNicknames *
+CERT_GetValidDNSPatternsFromCert(CERTCertificate *cert)
+{
+ CERTGeneralName *generalNames;
+ CERTCertNicknames *nickNames;
+ PLArenaPool *arena;
+ char *singleName;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena) {
+ return NULL;
+ }
+
+ nickNames = PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames));
+ if (!nickNames) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+
+ /* init the structure */
+ nickNames->arena = arena;
+ nickNames->head = NULL;
+ nickNames->numnicknames = 0;
+ nickNames->nicknames = NULL;
+ nickNames->totallen = 0;
+
+ generalNames = cert_GetSubjectAltNameList(cert, arena);
+ if (generalNames) {
+ SECStatus rv_getnames = SECFailure;
+ PRUint32 numNames = cert_CountDNSPatterns(generalNames);
+
+ if (numNames) {
+ rv_getnames = cert_GetDNSPatternsFromGeneralNames(
+ generalNames, numNames, nickNames);
+ }
+
+ /* if there were names, we'll exit now, either with success or failure
+ */
+ if (numNames) {
+ if (rv_getnames == SECSuccess) {
+ return nickNames;
+ }
+
+ /* failure to produce output */
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+ }
+
+ /* no SAN extension or no names found in extension */
+ singleName = CERT_GetCommonName(&cert->subject);
+ if (singleName) {
+ nickNames->numnicknames = 1;
+ nickNames->nicknames = PORT_ArenaAlloc(arena, sizeof(char *));
+ if (nickNames->nicknames) {
+ *nickNames->nicknames = PORT_ArenaStrdup(arena, singleName);
+ }
+ PORT_Free(singleName);
+
+ /* Did we allocate both the buffer of pointers and the string? */
+ if (nickNames->nicknames && *nickNames->nicknames) {
+ return nickNames;
+ }
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+}
+
+/* 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(const 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;
+
+ cn = CERT_GetCommonName(&cert->subject);
+ if (cn) {
+ PRBool isIPaddr = cert_IsIPAddr(hn);
+ if (isIPaddr) {
+ if (PORT_Strcasecmp(hn, cn) == 0) {
+ rv = SECSuccess;
+ } else {
+ PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
+ rv = SECFailure;
+ }
+ } else {
+ rv = cert_TestHostName(cn, hn);
+ }
+ PORT_Free(cn);
+ } else
+ PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
+ return rv;
+}
+
+PRBool
+CERT_CompareCerts(const CERTCertificate *c1, const 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(PLArenaPool *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;
+
+ 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 done;
+ }
+
+ 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 done;
+ }
+
+ /* look up the nickname to make sure it isn't in use already */
+ dummycert = CERT_FindCertByNickname(cert->dbhandle, nickname);
+
+ if (dummycert == NULL) {
+ goto done;
+ }
+
+ /* found a cert, destroy it and loop */
+ CERT_DestroyCertificate(dummycert);
+
+ /* free the nickname */
+ PORT_Free(nickname);
+
+ count++;
+ }
+
+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);
+}
+
+static int
+cert_Version(CERTCertificate *cert)
+{
+ int version = 0;
+ if (cert && cert->version.data && cert->version.len) {
+ version = DER_GetInteger(&cert->version);
+ if (version < 0)
+ version = 0;
+ }
+ return version;
+}
+
+static unsigned int
+cert_ComputeTrustOverrides(CERTCertificate *cert, unsigned int cType)
+{
+ CERTCertTrust trust;
+ SECStatus rv = SECFailure;
+
+ rv = CERT_GetCertTrust(cert, &trust);
+
+ if (rv == SECSuccess &&
+ (trust.sslFlags | trust.emailFlags | trust.objectSigningFlags)) {
+
+ if (trust.sslFlags & (CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED))
+ cType |= NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_SSL_CLIENT;
+ if (trust.sslFlags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA))
+ cType |= NS_CERT_TYPE_SSL_CA;
+#if defined(CERTDB_NOT_TRUSTED)
+ if (trust.sslFlags & CERTDB_NOT_TRUSTED)
+ cType &= ~(NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_SSL_CLIENT |
+ NS_CERT_TYPE_SSL_CA);
+#endif
+ if (trust.emailFlags & (CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED))
+ cType |= NS_CERT_TYPE_EMAIL;
+ if (trust.emailFlags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA))
+ cType |= NS_CERT_TYPE_EMAIL_CA;
+#if defined(CERTDB_NOT_TRUSTED)
+ if (trust.emailFlags & CERTDB_NOT_TRUSTED)
+ cType &= ~(NS_CERT_TYPE_EMAIL | NS_CERT_TYPE_EMAIL_CA);
+#endif
+ if (trust.objectSigningFlags &
+ (CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED))
+ cType |= NS_CERT_TYPE_OBJECT_SIGNING;
+ if (trust.objectSigningFlags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA))
+ cType |= NS_CERT_TYPE_OBJECT_SIGNING_CA;
+#if defined(CERTDB_NOT_TRUSTED)
+ if (trust.objectSigningFlags & CERTDB_NOT_TRUSTED)
+ cType &=
+ ~(NS_CERT_TYPE_OBJECT_SIGNING | NS_CERT_TYPE_OBJECT_SIGNING_CA);
+#endif
+ }
+ return cType;
+}
+
+/*
+ * 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)
+{
+ unsigned int cType = cert->nsCertType;
+ PRBool ret = PR_FALSE;
+
+ /*
+ * Check if the constraints are available and it's a CA, OR if it's
+ * a X.509 v1 Root CA.
+ */
+ CERTBasicConstraints constraints;
+ if ((CERT_FindBasicConstraintExten(cert, &constraints) == SECSuccess &&
+ constraints.isCA) ||
+ (cert->isRoot && cert_Version(cert) < SEC_CERTIFICATE_VERSION_3))
+ cType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA);
+
+ /*
+ * Apply trust overrides, if any.
+ */
+ cType = cert_ComputeTrustOverrides(cert, cType);
+ ret = (cType & (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA |
+ NS_CERT_TYPE_OBJECT_SIGNING_CA))
+ ? PR_TRUE
+ : PR_FALSE;
+
+ if (rettype) {
+ *rettype = cType;
+ }
+
+ 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;
+}
+
+CERTCompareValidityStatus
+CERT_CompareValidityTimes(CERTValidity *val_a, CERTValidity *val_b)
+{
+ PRTime notBeforeA, notBeforeB, notAfterA, notAfterB;
+
+ if (!val_a || !val_b) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return certValidityUndetermined;
+ }
+
+ if (SECSuccess != DER_DecodeTimeChoice(&notBeforeA, &val_a->notBefore) ||
+ SECSuccess != DER_DecodeTimeChoice(&notBeforeB, &val_b->notBefore) ||
+ SECSuccess != DER_DecodeTimeChoice(&notAfterA, &val_a->notAfter) ||
+ SECSuccess != DER_DecodeTimeChoice(&notAfterB, &val_b->notAfter)) {
+ return certValidityUndetermined;
+ }
+
+ /* sanity check */
+ if (LL_CMP(notBeforeA, >, notAfterA) || LL_CMP(notBeforeB, >, notAfterB)) {
+ PORT_SetError(SEC_ERROR_INVALID_TIME);
+ return certValidityUndetermined;
+ }
+
+ if (LL_CMP(notAfterA, !=, notAfterB)) {
+ /* one cert validity goes farther into the future, select it */
+ return LL_CMP(notAfterA, <, notAfterB) ? certValidityChooseB
+ : certValidityChooseA;
+ }
+ /* the two certs have the same expiration date */
+ PORT_Assert(LL_CMP(notAfterA, ==, notAfterB));
+ /* do they also have the same start date ? */
+ if (LL_CMP(notBeforeA, ==, notBeforeB)) {
+ return certValidityEqual;
+ }
+ /* choose cert with the later start date */
+ return LL_CMP(notBeforeA, <, notBeforeB) ? certValidityChooseB
+ : certValidityChooseA;
+}
+
+/*
+ * 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, &notBeforeA, &notAfterA);
+ if (rv != SECSuccess) {
+ return (PR_FALSE);
+ }
+
+ rv = CERT_GetCertTimes(certb, &notBeforeB, &notAfterB);
+ 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 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(const 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, const char *trusts)
+{
+ unsigned int i;
+ unsigned int *pflags;
+
+ if (!trust) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ trust->sslFlags = 0;
+ trust->emailFlags = 0;
+ trust->objectSigningFlags = 0;
+ if (!trusts) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ pflags = &trust->sslFlags;
+
+ for (i = 0; i < PORT_Strlen(trusts); i++) {
+ switch (trusts[i]) {
+ case 'p':
+ *pflags = *pflags | CERTDB_TERMINAL_RECORD;
+ break;
+
+ case 'P':
+ *pflags = *pflags | CERTDB_TRUSTED | CERTDB_TERMINAL_RECORD;
+ 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;
+
+ case 'i':
+ *pflags = *pflags | CERTDB_INVISIBLE_CA;
+ break;
+ case 'g':
+ *pflags = *pflags | CERTDB_GOVT_APPROVED_CA;
+ break;
+
+ case ',':
+ if (pflags == &trust->sslFlags) {
+ pflags = &trust->emailFlags;
+ } else {
+ pflags = &trust->objectSigningFlags;
+ }
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ 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_TERMINAL_RECORD)
+ 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);
+}
+
+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;
+ unsigned int fcerts = 0;
+
+ if (ncerts) {
+ certs = PORT_ZNewArray(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]) {
+ SECItem subjKeyID = { siBuffer, NULL, 0 };
+ if (CERT_FindSubjectKeyIDExtension(certs[fcerts], &subjKeyID) ==
+ SECSuccess) {
+ if (subjKeyID.data) {
+ cert_AddSubjectKeyIDMapping(&subjKeyID, certs[fcerts]);
+ }
+ SECITEM_FreeItem(&subjKeyID, PR_FALSE);
+ }
+ fcerts++;
+ }
+ }
+
+ if (keepCerts) {
+ for (i = 0; i < fcerts; i++) {
+ char *canickname = NULL;
+ PRBool isCA;
+
+ SECKEY_UpdateCertPQG(certs[i]);
+
+ isCA = CERT_IsCACert(certs[i], NULL);
+ if (isCA) {
+ canickname = CERT_MakeCANickname(certs[i]);
+ }
+
+ if (isCA && (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.
+ */
+ /* Bug 1192442 - propagate errors from these calls. */
+ (void)CERT_AddTempCertToPerm(certs[i], canickname, NULL);
+ } else {
+ (void)CERT_AddTempCertToPerm(
+ certs[i], nickname ? nickname : canickname, NULL);
+ }
+
+ PORT_Free(canickname);
+ /* don't care if it fails - keep going */
+ }
+ }
+ }
+
+ if (retCerts) {
+ *retCerts = certs;
+ } else {
+ if (certs) {
+ CERT_DestroyCertArray(certs, fcerts);
+ }
+ }
+
+ return (fcerts || !ncerts) ? SECSuccess : SECFailure;
+}
+
+/*
+ * a real list of certificates - need to convert CERTCertificateList
+ * stuff and ASN 1 encoder/decoder over to using this...
+ */
+CERTCertList *
+CERT_NewCertList(void)
+{
+ PLArenaPool *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;
+
+ if (!certs) {
+ return;
+ }
+
+ 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) {
+ goto loser;
+ }
+
+ 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, &notBeforeA, &notAfterA);
+ if (rv != SECSuccess) {
+ return (PR_FALSE);
+ }
+
+ rv = CERT_GetCertTimes(certb, &notBeforeB, &notAfterB);
+ 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;
+ SECStatus rv;
+
+ 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)) {
+
+ PRBool bad = (PRBool)(!node->cert);
+
+ /* bad key usage ? */
+ if (!bad &&
+ CERT_CheckKeyUsage(node->cert, requiredKeyUsage) != SECSuccess) {
+ bad = PR_TRUE;
+ }
+ /* bad cert type ? */
+ if (!bad) {
+ unsigned int certType = 0;
+ 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.
+ */
+ (void)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)
+{
+ CERTCertTrust trust;
+ SECStatus rv = SECFailure;
+
+ rv = CERT_GetCertTrust(cert, &trust);
+ if (rv == SECSuccess &&
+ ((trust.sslFlags & CERTDB_USER) || (trust.emailFlags & CERTDB_USER) ||
+ (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);
+}
+
+/* return true if cert is in the list */
+PRBool
+CERT_IsInList(const CERTCertificate *cert, const CERTCertList *certList)
+{
+ CERTCertListNode *node;
+ for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
+ node = CERT_LIST_NEXT(node)) {
+ if (node->cert == cert) {
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+/* returned certList is the intersection of the certs on certList and the
+ * certs on filterList */
+SECStatus
+CERT_FilterCertListByCertList(CERTCertList *certList,
+ const CERTCertList *filterList)
+{
+ CERTCertListNode *node, *freenode;
+ CERTCertificate *cert;
+
+ if (!certList) {
+ return SECFailure;
+ }
+
+ if (!filterList || CERT_LIST_EMPTY(certList)) {
+ /* if the filterList is empty, just clear out certList and return */
+ for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);) {
+ freenode = node;
+ node = CERT_LIST_NEXT(node);
+ CERT_RemoveCertListNode(freenode);
+ }
+ return SECSuccess;
+ }
+
+ node = CERT_LIST_HEAD(certList);
+
+ while (!CERT_LIST_END(node, certList)) {
+ cert = node->cert;
+ if (!CERT_IsInList(cert, filterList)) {
+ // no matching cert on filter list, remove it from certlist */
+ freenode = node;
+ node = CERT_LIST_NEXT(node);
+ CERT_RemoveCertListNode(freenode);
+ } else {
+ /* matching cert, keep it around */
+ node = CERT_LIST_NEXT(node);
+ }
+ }
+
+ return (SECSuccess);
+}
+
+SECStatus
+CERT_FilterCertListByNickname(CERTCertList *certList, char *nickname,
+ void *pwarg)
+{
+ CERTCertList *nameList;
+ SECStatus rv;
+
+ if (!certList) {
+ return SECFailure;
+ }
+
+ /* we could try to match the nickname to the individual cert,
+ * but nickname parsing is quite complicated, so it's best just
+ * to use the existing code and get a list of certs that match the
+ * nickname. We can then compare that list with our input cert list
+ * and return only those certs that are on both. */
+ nameList = PK11_FindCertsFromNickname(nickname, pwarg);
+
+ /* namelist could be NULL, this will force certList to become empty */
+ rv = CERT_FilterCertListByCertList(certList, nameList);
+ /* CERT_DestroyCertList can now accept a NULL pointer */
+ CERT_DestroyCertList(nameList);
+ return rv;
+}
+
+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)
+{
+ PORT_Assert(certRefCountLock != NULL);
+ PZ_Lock(certRefCountLock);
+ return;
+}
+
+/*
+ * Free the cert reference count lock
+ */
+void
+CERT_UnlockCertRefCount(CERTCertificate *cert)
+{
+ PORT_Assert(certRefCountLock != NULL);
+ PRStatus prstat = PZ_Unlock(certRefCountLock);
+ PORT_AssertArg(prstat == PR_SUCCESS);
+}
+
+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(const CERTCertificate *cert)
+{
+ PORT_Assert(certTrustLock != NULL);
+ PZ_Lock(certTrustLock);
+}
+
+static PZLock *certTempPermCertLock = NULL;
+
+/*
+ * Acquire the cert temp/perm/nssCert lock
+ */
+void
+CERT_LockCertTempPerm(const CERTCertificate *cert)
+{
+ PORT_Assert(certTempPermCertLock != NULL);
+ PZ_Lock(certTempPermCertLock);
+}
+
+/* Maybe[Lock, Unlock] variants are only to be used by
+ * CERT_DestroyCertificate, since an application could
+ * call this after NSS_Shutdown destroys cert locks. */
+void
+CERT_MaybeLockCertTempPerm(const CERTCertificate *cert)
+{
+ if (certTempPermCertLock) {
+ PZ_Lock(certTempPermCertLock);
+ }
+}
+
+SECStatus
+cert_InitLocks(void)
+{
+ if (certRefCountLock == NULL) {
+ certRefCountLock = PZ_NewLock(nssILockRefLock);
+ PORT_Assert(certRefCountLock != NULL);
+ if (!certRefCountLock) {
+ return SECFailure;
+ }
+ }
+
+ if (certTrustLock == NULL) {
+ certTrustLock = PZ_NewLock(nssILockCertDB);
+ PORT_Assert(certTrustLock != NULL);
+ if (!certTrustLock) {
+ PZ_DestroyLock(certRefCountLock);
+ certRefCountLock = NULL;
+ return SECFailure;
+ }
+ }
+
+ if (certTempPermCertLock == NULL) {
+ certTempPermCertLock = PZ_NewLock(nssILockCertDB);
+ PORT_Assert(certTempPermCertLock != NULL);
+ if (!certTempPermCertLock) {
+ PZ_DestroyLock(certTrustLock);
+ PZ_DestroyLock(certRefCountLock);
+ certRefCountLock = NULL;
+ certTrustLock = NULL;
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}
+
+SECStatus
+cert_DestroyLocks(void)
+{
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(certRefCountLock != NULL);
+ if (certRefCountLock) {
+ PZ_DestroyLock(certRefCountLock);
+ certRefCountLock = NULL;
+ } else {
+ rv = SECFailure;
+ }
+
+ PORT_Assert(certTrustLock != NULL);
+ if (certTrustLock) {
+ PZ_DestroyLock(certTrustLock);
+ certTrustLock = NULL;
+ } else {
+ rv = SECFailure;
+ }
+
+ PORT_Assert(certTempPermCertLock != NULL);
+ if (certTempPermCertLock) {
+ PZ_DestroyLock(certTempPermCertLock);
+ certTempPermCertLock = NULL;
+ } else {
+ rv = SECFailure;
+ }
+ return rv;
+}
+
+/*
+ * Free the cert trust lock
+ */
+void
+CERT_UnlockCertTrust(const CERTCertificate *cert)
+{
+ PORT_Assert(certTrustLock != NULL);
+ PRStatus prstat = PZ_Unlock(certTrustLock);
+ PORT_AssertArg(prstat == PR_SUCCESS);
+}
+
+/*
+ * Free the temp/perm/nssCert lock
+ */
+void
+CERT_UnlockCertTempPerm(const CERTCertificate *cert)
+{
+ PORT_Assert(certTempPermCertLock != NULL);
+ PRStatus prstat = PZ_Unlock(certTempPermCertLock);
+ PORT_AssertArg(prstat == PR_SUCCESS);
+}
+
+void
+CERT_MaybeUnlockCertTempPerm(const CERTCertificate *cert)
+{
+ if (certTempPermCertLock) {
+ PZ_Unlock(certTempPermCertLock);
+ }
+}
+
+/*
+ * 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 PLHashTable *gSubjKeyIDSlotCheckHash = NULL;
+static PRLock *gSubjKeyIDSlotCheckLock = 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_CreateSubjectKeyIDSlotCheckHash(void)
+{
+ /*
+ * This hash is used to remember the series of a slot
+ * when we last checked for user certs
+ */
+ gSubjKeyIDSlotCheckHash =
+ PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
+ SECITEM_HashCompare, &cert_AllocOps, NULL);
+ if (!gSubjKeyIDSlotCheckHash) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ gSubjKeyIDSlotCheckLock = PR_NewLock();
+ if (!gSubjKeyIDSlotCheckLock) {
+ PL_HashTableDestroy(gSubjKeyIDSlotCheckHash);
+ gSubjKeyIDSlotCheckHash = NULL;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+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;
+ }
+ /* initialize the companion hash (for remembering slot series) */
+ if (cert_CreateSubjectKeyIDSlotCheckHash() != SECSuccess) {
+ cert_DestroySubjectKeyIDHashTable();
+ 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_UpdateSubjectKeyIDSlotCheck(SECItem *slotid, int series)
+{
+ SECItem *oldSeries, *newSlotid, *newSeries;
+ SECStatus rv = SECFailure;
+
+ if (!gSubjKeyIDSlotCheckLock) {
+ return rv;
+ }
+
+ newSlotid = SECITEM_DupItem(slotid);
+ newSeries = SECITEM_AllocItem(NULL, NULL, sizeof(int));
+ if (!newSlotid || !newSeries) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(newSeries->data, &series, sizeof(int));
+
+ PR_Lock(gSubjKeyIDSlotCheckLock);
+ oldSeries = (SECItem *)PL_HashTableLookup(gSubjKeyIDSlotCheckHash, slotid);
+ if (oldSeries) {
+ /*
+ * make sure we don't leak the key of an existing entry
+ * (similar to cert_AddSubjectKeyIDMapping, see comment there)
+ */
+ PL_HashTableRemove(gSubjKeyIDSlotCheckHash, slotid);
+ }
+ rv = (PL_HashTableAdd(gSubjKeyIDSlotCheckHash, newSlotid, newSeries))
+ ? SECSuccess
+ : SECFailure;
+ PR_Unlock(gSubjKeyIDSlotCheckLock);
+ if (rv == SECSuccess) {
+ return rv;
+ }
+
+loser:
+ if (newSlotid) {
+ SECITEM_FreeItem(newSlotid, PR_TRUE);
+ }
+ if (newSeries) {
+ SECITEM_FreeItem(newSeries, PR_TRUE);
+ }
+ return rv;
+}
+
+int
+cert_SubjectKeyIDSlotCheckSeries(SECItem *slotid)
+{
+ SECItem *seriesItem = NULL;
+ int series;
+
+ if (!gSubjKeyIDSlotCheckLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return -1;
+ }
+
+ PR_Lock(gSubjKeyIDSlotCheckLock);
+ seriesItem = (SECItem *)PL_HashTableLookup(gSubjKeyIDSlotCheckHash, slotid);
+ PR_Unlock(gSubjKeyIDSlotCheckLock);
+ /* getting a null series just means we haven't registered one yet,
+ * just return 0 */
+ if (seriesItem == NULL) {
+ return 0;
+ }
+ /* if we got a series back, assert if it's not the proper length. */
+ PORT_Assert(seriesItem->len == sizeof(int));
+ if (seriesItem->len != sizeof(int)) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return -1;
+ }
+ PORT_Memcpy(&series, seriesItem->data, sizeof(int));
+ return series;
+}
+
+SECStatus
+cert_DestroySubjectKeyIDSlotCheckHash(void)
+{
+ if (gSubjKeyIDSlotCheckHash) {
+ PR_Lock(gSubjKeyIDSlotCheckLock);
+ PL_HashTableDestroy(gSubjKeyIDSlotCheckHash);
+ gSubjKeyIDSlotCheckHash = NULL;
+ PR_Unlock(gSubjKeyIDSlotCheckLock);
+ PR_DestroyLock(gSubjKeyIDSlotCheckLock);
+ gSubjKeyIDSlotCheckLock = NULL;
+ }
+ return SECSuccess;
+}
+
+SECStatus
+cert_DestroySubjectKeyIDHashTable(void)
+{
+ if (gSubjKeyIDHash) {
+ PR_Lock(gSubjKeyIDLock);
+ PL_HashTableDestroy(gSubjKeyIDHash);
+ gSubjKeyIDHash = NULL;
+ PR_Unlock(gSubjKeyIDLock);
+ PR_DestroyLock(gSubjKeyIDLock);
+ gSubjKeyIDLock = NULL;
+ }
+ cert_DestroySubjectKeyIDSlotCheckHash();
+ 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.gyp b/security/nss/lib/certdb/certdb.gyp
new file mode 100644
index 0000000000..673d56da31
--- /dev/null
+++ b/security/nss/lib/certdb/certdb.gyp
@@ -0,0 +1,34 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'certdb',
+ 'type': 'static_library',
+ 'sources': [
+ 'alg1485.c',
+ 'certdb.c',
+ 'certv3.c',
+ 'certxutl.c',
+ 'crl.c',
+ 'genname.c',
+ 'polcyxtn.c',
+ 'secname.c',
+ 'stanpcertdb.c',
+ 'xauthkid.c',
+ 'xbsconst.c',
+ 'xconst.c'
+ ],
+ 'dependencies': [
+ '<(DEPTH)/exports.gyp:nss_exports'
+ ]
+ }
+ ],
+ 'variables': {
+ 'module': 'nss'
+ }
+} \ No newline at end of file
diff --git a/security/nss/lib/certdb/certdb.h b/security/nss/lib/certdb/certdb.h
new file mode 100644
index 0000000000..53d8a39190
--- /dev/null
+++ b/security/nss/lib/certdb/certdb.h
@@ -0,0 +1,89 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _CERTDB_H_
+#define _CERTDB_H_
+
+/* common flags for all types of certificates */
+#define CERTDB_TERMINAL_RECORD (1u << 0)
+#define CERTDB_TRUSTED (1u << 1)
+#define CERTDB_SEND_WARN (1u << 2)
+#define CERTDB_VALID_CA (1u << 3)
+#define CERTDB_TRUSTED_CA (1u << 4) /* trusted for issuing server certs */
+#define CERTDB_NS_TRUSTED_CA (1u << 5)
+#define CERTDB_USER (1u << 6)
+#define CERTDB_TRUSTED_CLIENT_CA (1u << 7) /* trusted for issuing client certs */
+#define CERTDB_INVISIBLE_CA (1u << 8) /* don't show in UI */
+#define CERTDB_GOVT_APPROVED_CA (1u << 9) /* can do strong crypto in export ver */
+
+/* old usage, to keep old programs compiling */
+/* On Windows, Mac, and Linux (and other gcc platforms), we can give compile
+ * time deprecation warnings when applications use the old CERTDB_VALID_PEER
+ * define */
+#if __GNUC__ > 3
+#if (__GNUC__ == 4) && (__GNUC_MINOR__ < 5)
+typedef unsigned int __CERTDB_VALID_PEER __attribute__((deprecated));
+#else
+typedef unsigned int __CERTDB_VALID_PEER __attribute__((
+ deprecated("CERTDB_VALID_PEER is now CERTDB_TERMINAL_RECORD")));
+#endif
+#define CERTDB_VALID_PEER ((__CERTDB_VALID_PEER)CERTDB_TERMINAL_RECORD)
+#else
+#ifdef _WIN32
+#pragma deprecated(CERTDB_VALID_PEER)
+#endif
+#define CERTDB_VALID_PEER CERTDB_TERMINAL_RECORD
+#endif
+
+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(const char *nickname, const 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);
+
+CERTSignedCrl *SEC_DupCrl(CERTSignedCrl *acrl);
+
+SECStatus CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname,
+ CERTCertTrust *trust);
+
+SECStatus SEC_DeletePermCertificate(CERTCertificate *cert);
+
+PRBool SEC_CrlIsNewer(CERTCrl *inNew, CERTCrl *old);
+
+/*
+** Extract the validity times from a CRL
+** "crl" is the CRL
+** "notBefore" is the start of the validity period (last update)
+** "notAfter" is the end of the validity period (next update)
+*/
+SECStatus SEC_GetCrlTimes(CERTCrl *crl, PRTime *notBefore, PRTime *notAfter);
+
+/*
+** Check the validity times of a crl vs. time 't', allowing
+** some slop for broken clocks and stuff.
+** "crl" is the certificate to be checked
+** "t" is the time to check against
+*/
+SECCertTimeValidity SEC_CheckCrlTimes(CERTCrl *crl, PRTime t);
+
+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 0000000000..2a8ae2758f
--- /dev/null
+++ b/security/nss/lib/certdb/certi.h
@@ -0,0 +1,407 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * certi.h - private data structures for the certificate library
+ */
+#ifndef _CERTI_H_
+#define _CERTI_H_
+
+#include "certt.h"
+#include "nssrwlkt.h"
+
+/*
+#define GLOBAL_RWLOCK 1
+*/
+
+#define DPC_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;
+typedef struct CachedCrlStr CachedCrl;
+typedef struct NamedCRLCacheStr NamedCRLCache;
+typedef struct NamedCRLCacheEntryStr NamedCRLCacheEntry;
+
+struct OpaqueCRLFieldsStr {
+ PRBool partial;
+ PRBool decodingError;
+ PRBool badEntries;
+ PRBool badDER;
+ PRBool badExtensions;
+ PRBool heapDER;
+};
+
+typedef struct PreAllocatorStr PreAllocator;
+
+struct PreAllocatorStr {
+ PRSize len;
+ void* data;
+ PRSize used;
+ PLArenaPool* 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 */
+
+typedef enum {
+ CRL_OriginToken = 0, /* CRL came from PKCS#11 token */
+ CRL_OriginExplicit = 1 /* CRL was explicitly added to the cache, from RAM */
+} CRLOrigin;
+
+typedef enum {
+ dpcacheNoEntry = 0, /* no entry found for this SN */
+ dpcacheFoundEntry = 1, /* entry found for this SN */
+ dpcacheCallerError = 2, /* invalid args */
+ dpcacheInvalidCacheError = 3, /* CRL in cache may be bad DER */
+ /* or unverified */
+ dpcacheEmpty = 4, /* no CRL in cache */
+ dpcacheLookupError = 5 /* internal error */
+} dpcacheStatus;
+
+struct CachedCrlStr {
+ CERTSignedCrl* crl;
+ CRLOrigin origin;
+ /* 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 */
+ PRBool sigChecked; /* this CRL signature has already been checked */
+ PRBool sigValid; /* signature verification status .
+ Only meaningful if checked is PR_TRUE . */
+ PRBool unbuildable; /* Avoid using assosiated CRL is it fails
+ * a decoding step */
+};
+
+/* 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 DPC_RWLOCK
+ NSSRWLock* lock;
+#else
+ PRLock* lock;
+#endif
+ SECItem* issuerDERCert; /* issuer DER cert. Don't hold a reference
+ to the actual cert so the trust can be
+ updated on the cert automatically.
+ XXX there may be multiple issuer certs,
+ with different validity dates. Also
+ need to deal with SKID/AKID . See
+ bugzilla 217387, 233118 */
+
+ CERTCertDBHandle* dbHandle;
+
+ 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).
+ Currently not used. */
+
+ /* array of full CRLs matching this distribution point */
+ PRUint32 ncrls; /* total number of CRLs in crls */
+ CachedCrl** crls; /* array of all matching CRLs */
+ /* 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 */
+ CachedCrl* selected; /* full CRL selected for use in the cache */
+#if 0
+ /* for future use */
+ PRInt32 numdeltas; /* number of delta CRLs used for the cache */
+ CachedCrl** deltas; /* delta CRLs used for the cache */
+#endif
+ /* cache 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 to have unknown status.
+ The invalid state can only be cleared during an update if all
+ error states are cleared */
+ PRBool refresh; /* manual refresh from tokens has been forced */
+ PRBool mustchoose; /* trigger reselection algorithm, for case when
+ RAM CRL objects are dropped from the cache */
+ PRTime lastfetch; /* time a CRL token fetch was last performed */
+ PRTime lastcheck; /* time CRL token objects were last checked for
+ existence */
+};
+
+/* 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* dpp;
+};
+
+/* CRL revocation cache object
+ This object tracks all the issuer caches
+*/
+
+struct CRLCacheStr {
+#ifdef GLOBAL_RWLOCK
+ NSSRWLock* lock;
+#else
+ PRLock* lock;
+#endif
+ /* 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
+ * and to keep track of the checks for user certificates in each slot
+ */
+SECStatus cert_CreateSubjectKeyIDHashTable(void);
+
+SECStatus cert_AddSubjectKeyIDMapping(SECItem* subjKeyID,
+ CERTCertificate* cert);
+
+SECStatus cert_UpdateSubjectKeyIDSlotCheck(SECItem* slotid, int series);
+
+int cert_SubjectKeyIDSlotCheckSeries(SECItem* slotid);
+
+/*
+ * 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);
+
+/* return maximum length of AVA value based on its type OID tag. */
+extern int cert_AVAOidTagToMaxLen(SECOidTag tag);
+
+/* Make an AVA, allocated from pool, from OID and DER encoded value */
+extern CERTAVA* CERT_CreateAVAFromRaw(PLArenaPool* pool, const SECItem* OID,
+ const SECItem* value);
+
+/* Make an AVA from binary input specified by SECItem */
+extern CERTAVA* CERT_CreateAVAFromSECItem(PLArenaPool* arena, SECOidTag kind,
+ int valueType, SECItem* value);
+
+/*
+ * get a DPCache object for the given issuer subject and dp
+ * Automatically creates the cache object if it doesn't exist yet.
+ */
+SECStatus AcquireDPCache(CERTCertificate* issuer, const SECItem* subject,
+ const SECItem* dp, PRTime t, void* wincx,
+ CRLDPCache** dpcache, PRBool* writeLocked);
+
+/* check if a particular SN is in the CRL cache and return its entry */
+dpcacheStatus DPCache_Lookup(CRLDPCache* cache, const SECItem* sn,
+ CERTCrlEntry** returned);
+
+/* release a DPCache object that was previously acquired */
+void ReleaseDPCache(CRLDPCache* dpcache, PRBool writeLocked);
+
+/*
+ * map Stan errors into NSS errors
+ * This function examines the stan error stack and automatically sets
+ * PORT_SetError(); to the appropriate SEC_ERROR value.
+ */
+void CERT_MapStanError();
+
+/* Like CERT_VerifyCert, except with an additional argument, flags. The
+ * flags are defined immediately below.
+ */
+SECStatus cert_VerifyCertWithFlags(CERTCertDBHandle* handle,
+ CERTCertificate* cert, PRBool checkSig,
+ SECCertUsage certUsage, PRTime t,
+ PRUint32 flags, void* wincx,
+ CERTVerifyLog* log);
+
+/* Use the default settings.
+ * cert_VerifyCertWithFlags(..., CERT_VERIFYCERT_USE_DEFAULTS, ...) is
+ * equivalent to CERT_VerifyCert(...);
+ */
+#define CERT_VERIFYCERT_USE_DEFAULTS 0
+
+/* Skip all the OCSP checks during certificate verification, regardless of
+ * the global OCSP settings. By default, certificate |cert| will have its
+ * revocation status checked via OCSP according to the global OCSP settings.
+ *
+ * OCSP checking is always skipped when certUsage is certUsageStatusResponder.
+ */
+#define CERT_VERIFYCERT_SKIP_OCSP 1
+
+/* Interface function for libpkix cert validation engine:
+ * cert_verify wrapper. */
+SECStatus cert_VerifyCertChainPkix(CERTCertificate* cert, PRBool checkSig,
+ SECCertUsage requiredUsage, PRTime time,
+ void* wincx, CERTVerifyLog* log,
+ PRBool* sigError, PRBool* revoked);
+
+SECStatus cert_InitLocks(void);
+
+SECStatus cert_DestroyLocks(void);
+
+/*
+ * fill in nsCertType field of the cert based on the cert extension
+ */
+extern SECStatus cert_GetCertType(CERTCertificate* cert);
+
+/*
+ * compute and return the value of nsCertType for cert, but do not
+ * update the CERTCertificate.
+ */
+extern PRUint32 cert_ComputeCertType(CERTCertificate* cert);
+
+extern PRBool cert_EKUAllowsIPsecIKE(CERTCertificate* cert,
+ PRBool* isCritical);
+
+void cert_AddToVerifyLog(CERTVerifyLog* log, CERTCertificate* cert,
+ long errorCode, unsigned int depth, void* arg);
+
+/* Insert a DER CRL into the CRL cache, and take ownership of it.
+ *
+ * cert_CacheCRLByGeneralName takes ownership of the memory in crl argument
+ * completely. crl must be freeable by SECITEM_FreeItem. It will be freed
+ * immediately if it is rejected from the CRL cache, or later during cache
+ * updates when a new crl is available, or at shutdown time.
+ *
+ * canonicalizedName represents the source of the CRL, a GeneralName.
+ * The format of the encoding is not restricted, but all callers of
+ * cert_CacheCRLByGeneralName and cert_FindCRLByGeneralName must use
+ * the same encoding. To facilitate X.500 name matching, a canonicalized
+ * encoding of the GeneralName should be used, if available.
+ */
+
+SECStatus cert_CacheCRLByGeneralName(CERTCertDBHandle* dbhandle, SECItem* crl,
+ const SECItem* canonicalizedName);
+
+struct NamedCRLCacheStr {
+ PRLock* lock;
+ PLHashTable* entries;
+};
+
+/* NamedCRLCacheEntryStr is filled in by cert_CacheCRLByGeneralName,
+ * and read by cert_FindCRLByGeneralName */
+struct NamedCRLCacheEntryStr {
+ SECItem* canonicalizedName;
+ SECItem* crl; /* DER, kept only if CRL
+ * is successfully cached */
+ PRBool inCRLCache;
+ PRTime successfulInsertionTime; /* insertion time */
+ PRTime lastAttemptTime; /* time of last call to
+ cert_CacheCRLByGeneralName with this name */
+ PRBool badDER; /* ASN.1 error */
+ PRBool dupe; /* matching DER CRL already in CRL cache */
+ PRBool unsupported; /* IDP, delta, any other reason */
+};
+
+typedef enum {
+ certRevocationStatusRevoked = 0,
+ certRevocationStatusValid = 1,
+ certRevocationStatusUnknown = 2
+} CERTRevocationStatus;
+
+/* Returns detailed status of the cert(revStatus variable). Tells if
+ * issuer cache has OriginFetchedWithTimeout crl in it. */
+SECStatus cert_CheckCertRevocationStatus(CERTCertificate* cert,
+ CERTCertificate* issuer,
+ const SECItem* dp, PRTime t,
+ void* wincx,
+ CERTRevocationStatus* revStatus,
+ CERTCRLEntryReasonCode* revReason);
+
+SECStatus cert_AcquireNamedCRLCache(NamedCRLCache** returned);
+
+/* cert_FindCRLByGeneralName must be called only while the named cache is
+ * acquired, and the entry is only valid until cache is released.
+ */
+SECStatus cert_FindCRLByGeneralName(NamedCRLCache* ncc,
+ const SECItem* canonicalizedName,
+ NamedCRLCacheEntry** retEntry);
+
+SECStatus cert_ReleaseNamedCRLCache(NamedCRLCache* ncc);
+
+/* This is private for now. Maybe shoule be public. */
+CERTGeneralName* cert_GetSubjectAltNameList(const CERTCertificate* cert,
+ PLArenaPool* arena);
+
+/* Count DNS names and IP addresses in a list of GeneralNames */
+PRUint32 cert_CountDNSPatterns(CERTGeneralName* firstName);
+
+/*
+ * returns the trust status of the leaf certificate based on usage.
+ * If the leaf is explicitly untrusted, this function will fail and
+ * failedFlags will be set to the trust bit value that lead to the failure.
+ * If the leaf is trusted, isTrusted is set to true and the function returns
+ * SECSuccess. This function does not check if the cert is fit for a
+ * particular usage.
+ */
+SECStatus cert_CheckLeafTrust(CERTCertificate* cert, SECCertUsage usage,
+ unsigned int* failedFlags, PRBool* isTrusted);
+
+/*
+ * Acquire the cert temp/perm lock
+ */
+void CERT_LockCertTempPerm(const CERTCertificate* cert);
+
+/*
+ * Release the temp/perm lock
+ */
+void CERT_UnlockCertTempPerm(const 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(const CERTCertificate* cert);
+
+/*
+ * Release the cert trust lock
+ */
+void CERT_UnlockCertTrust(const CERTCertificate* cert);
+
+#endif /* _CERTI_H_ */
diff --git a/security/nss/lib/certdb/certt.h b/security/nss/lib/certdb/certt.h
new file mode 100644
index 0000000000..aae1184a84
--- /dev/null
+++ b/security/nss/lib/certdb/certt.h
@@ -0,0 +1,1359 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * certt.h - public data structures for the certificate library
+ */
+#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;
+typedef struct NSSTrustDomainStr CERTCertDBHandle;
+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 CERTCertDistrustStr CERTCertDistrust;
+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 CERTPrivKeyUsagePeriodStr CERTPrivKeyUsagePeriod;
+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 {
+ PLArenaPool *arena;
+ CERTRDN **rdns;
+};
+
+/*
+** An X.509 validity object
+*/
+struct CERTValidityStr {
+ PLArenaPool *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 {
+ PLArenaPool *arena;
+ SECAlgorithmID algorithm;
+ SECItem subjectPublicKey;
+};
+
+struct CERTPublicKeyAndChallengeStr {
+ SECItem spki;
+ SECItem challenge;
+};
+
+struct CERTCertTrustStr {
+ unsigned int sslFlags;
+ unsigned int emailFlags;
+ unsigned int objectSigningFlags;
+};
+
+/*
+ * Distrust dates for specific certificate usages.
+ * These dates are hardcoded in nssckbi/builtins. They are DER encoded to be
+ * compatible with the format of certdata.txt, other date fields in certs and
+ * existing functions to read these dates. Clients should check the distrust
+ * date in certificates to avoid trusting a CA for service they have ceased to
+ * support */
+struct CERTCertDistrustStr {
+ SECItem serverDistrustAfter;
+ SECItem emailDistrustAfter;
+};
+
+/*
+ * 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 {
+ PLArenaPool *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. It is used when the
+ * cert is decoded, destroyed, and at some times when it changes
+ * state
+ */
+ PLArenaPool *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 */
+ PRUint32 nsCertType; /* value of the ns cert type extension */
+ /* must be 32-bit for PR_ATOMIC_SET */
+
+ /* 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.
+ */
+ union {
+ void *apointer; /* was struct SECSocketNode* authsocketlist */
+ struct {
+ unsigned int hasUnsupportedCriticalExt : 1;
+ /* add any new option bits needed here */
+ } bits;
+ } options;
+ 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 */
+ /* These fields are used in nssckbi/builtins CAs. */
+ CERTCertDistrust *distrust;
+};
+#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 {
+ PLArenaPool *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 {
+ PLArenaPool *arena;
+ SECItem version;
+ CERTName subject;
+ CERTSubjectPublicKeyInfo subjectPublicKeyInfo;
+ CERTAttribute **attributes;
+};
+#define SEC_CERTIFICATE_REQUEST_VERSION 0 /* what we *create* */
+
+/*
+** A certificate list object.
+*/
+struct CERTCertificateListStr {
+ SECItem *certs;
+ int len; /* number of certs */
+ PLArenaPool *arena;
+};
+
+struct CERTCertListNodeStr {
+ PRCList links;
+ CERTCertificate *cert;
+ void *appData;
+};
+
+struct CERTCertListStr {
+ PRCList list;
+ PLArenaPool *arena;
+};
+
+#define CERT_LIST_HEAD(l) ((CERTCertListNode *)PR_LIST_HEAD(&l->list))
+#define CERT_LIST_TAIL(l) ((CERTCertListNode *)PR_LIST_TAIL(&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 {
+ PLArenaPool *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 {
+ PLArenaPool *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 {
+ PLArenaPool *arena;
+ CERTCertDBHandle *dbhandle;
+ CERTCrlNode *first;
+ CERTCrlNode *last;
+};
+
+struct CERTCrlNodeStr {
+ CERTCrlNode *next;
+ int type;
+ CERTSignedCrl *crl;
+};
+
+/*
+ * Array of X.500 Distinguished Names
+ */
+struct CERTDistNamesStr {
+ PLArenaPool *arena;
+ int nnames;
+ SECItem *names;
+ void *head; /* private */
+};
+
+/*
+ * NS_CERT_TYPE defines are used in two areas:
+ * 1) The old NSS Cert Type Extension, which is a certificate extension in the
+ * actual cert. It was created before the x509 Extended Key Usage Extension,
+ * which has now taken over it's function. This field is only 8 bits wide
+ * 2) The nsCertType entry in the CERTCertificate structure. This field is
+ * 32 bits wide.
+ * Any entries in this table greater than 0x80 will not be able to be encoded
+ * in an NSS Cert Type Extension, but can still be represented internally in
+ * the nsCertType field.
+ */
+#define NS_CERT_TYPE_IPSEC_CA (0x200) /* outside the NS Cert Type Extenstion */
+#define NS_CERT_TYPE_IPSEC (0x100) /* outside the NS Cert Type Extenstion */
+#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_IPSEC | 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 | \
+ NS_CERT_TYPE_IPSEC_CA)
+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,
+ certUsageIPsec = 12
+} SECCertUsage;
+
+typedef PRInt64 SECCertificateUsage;
+
+#define certificateUsageCheckAllUsages (0x0000)
+#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 certificateUsageIPsec (0x1000)
+
+#define certificateUsageHighest certificateUsageIPsec
+
+/*
+ * 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,
+ secCertTimeUndetermined = 3 /* validity could not be decoded from the
+ cert, most likely because it was NULL */
+} SECCertTimeValidity;
+
+/*
+ * This is used as return status in functions that compare the validity
+ * periods of two certificates A and B, currently only
+ * CERT_CompareValidityTimes.
+ */
+
+typedef enum CERTCompareValidityStatusEnum {
+ certValidityUndetermined = 0, /* the function is unable to select one cert
+ over another */
+ certValidityChooseB = 1, /* cert B should be preferred */
+ certValidityEqual = 2, /* both certs have the same validity period */
+ certValidityChooseA = 3 /* cert A should be preferred */
+} CERTCompareValidityStatus;
+
+/*
+ * 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 {
+ PLArenaPool *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_ENCIPHER_ONLY (0x01) /* bit 7 */
+#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 | KU_ENCIPHER_ONLY)
+
+/* This value will not occur in certs. It is used internally for the case
+ * when either digital signature or non-repudiation is the correct value.
+ */
+#define KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION (0x2000)
+
+/* 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
+
+#define CERT_MAX_SERIAL_NUMBER_BYTES 20 /* from RFC 3280 */
+#define CERT_MAX_DN_BYTES 4096 /* arbitrary */
+
+/* x.509 v3 Reason Flags, 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 */
+
+/* enum for CRL Entry Reason Code */
+typedef enum CERTCRLEntryReasonCodeEnum {
+ crlEntryReasonUnspecified = 0,
+ crlEntryReasonKeyCompromise = 1,
+ crlEntryReasonCaCompromise = 2,
+ crlEntryReasonAffiliationChanged = 3,
+ crlEntryReasonSuperseded = 4,
+ crlEntryReasonCessationOfOperation = 5,
+ crlEntryReasoncertificatedHold = 6,
+ crlEntryReasonRemoveFromCRL = 8,
+ crlEntryReasonPrivilegeWithdrawn = 9,
+ crlEntryReasonAaCompromise = 10
+} CERTCRLEntryReasonCode;
+
+/* 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 {
+ PLArenaPool *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;
+};
+
+/* Private Key Usage Period extension struct. */
+struct CERTPrivKeyUsagePeriodStr {
+ SECItem notBefore;
+ SECItem notAfter;
+ PLArenaPool *arena;
+};
+
+/* 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 {
+ PLArenaPool *arena;
+ unsigned int count;
+ struct CERTVerifyLogNodeStr *head;
+ struct CERTVerifyLogNodeStr *tail;
+};
+
+struct CERTOKDomainNameStr {
+ CERTOKDomainName *next;
+ char *name;
+};
+
+typedef SECStatus(PR_CALLBACK *CERTStatusChecker)(CERTCertDBHandle *handle,
+ CERTCertificate *cert,
+ PRTime 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 {
+ PLArenaPool *arena;
+ CERTPolicyInfo **policyInfos;
+} CERTCertificatePolicies;
+
+typedef struct {
+ SECItem organization;
+ SECItem **noticeNumbers;
+} CERTNoticeReference;
+
+typedef struct {
+ PLArenaPool *arena;
+ CERTNoticeReference noticeReference;
+ SECItem derNoticeReference;
+ SECItem displayText;
+} CERTUserNotice;
+
+typedef struct {
+ PLArenaPool *arena;
+ SECItem **oids;
+} CERTOidSequence;
+
+/*
+ * these types are for the PKIX Policy Mappings extension
+ */
+typedef struct {
+ SECItem issuerDomainPolicy;
+ SECItem subjectDomainPolicy;
+} CERTPolicyMap;
+
+typedef struct {
+ PLArenaPool *arena;
+ CERTPolicyMap **policyMaps;
+} CERTCertificatePolicyMappings;
+
+/*
+ * these types are for the PKIX inhibitAnyPolicy extension
+ */
+typedef struct {
+ SECItem inhibitAnySkipCerts;
+} CERTCertificateInhibitAny;
+
+/*
+ * these types are for the PKIX Policy Constraints extension
+ */
+typedef struct {
+ SECItem explicitPolicySkipCerts;
+ SECItem inhibitMappingSkipCerts;
+} CERTCertificatePolicyConstraints;
+
+/*
+ * These types are for the validate chain callback param.
+ *
+ * CERTChainVerifyCallback is an application-supplied callback that can be used
+ * to augment libpkix's certificate chain validation with additional
+ * application-specific checks. It may be called multiple times if there are
+ * multiple potentially-valid paths for the certificate being validated. This
+ * callback is called before revocation checking is done on the certificates in
+ * the given chain.
+ *
+ * - isValidChainArg contains the application-provided opaque argument
+ * - currentChain is the currently validated chain. It is ordered with the leaf
+ * certificate at the head and the trust anchor at the tail.
+ *
+ * The callback should set *chainOK = PR_TRUE and return SECSuccess if the
+ * certificate chain is acceptable. It should set *chainOK = PR_FALSE and
+ * return SECSuccess if the chain is unacceptable, to indicate that the given
+ * chain is bad and path building should continue. It should return SECFailure
+ * to indicate an fatal error that will cause path validation to fail
+ * immediately.
+ */
+typedef SECStatus (*CERTChainVerifyCallbackFunc)(
+ void *isChainValidArg, const CERTCertList *currentChain, PRBool *chainOK);
+
+/*
+ * Note: If extending this structure, it will be necessary to change the
+ * associated CERTValParamInType
+ */
+typedef struct {
+ CERTChainVerifyCallbackFunc isChainValid;
+ void *isChainValidArg;
+} CERTChainVerifyCallback;
+
+/*
+ * these types are for the CERT_PKIX* Verification functions
+ * These are all optional parameters.
+ */
+
+typedef enum {
+ cert_pi_end = 0, /* SPECIAL: signifies end of array of
+ * CERTValParam* */
+ cert_pi_nbioContext = 1, /* specify a non-blocking IO context used to
+ * resume a session. If this argument is
+ * specified, no other arguments should be.
+ * Specified in value.pointer.p. If the
+ * operation completes the context will be
+ * freed. */
+ cert_pi_nbioAbort = 2, /* specify a non-blocking IO context for an
+ * existing operation which the caller wants
+ * to abort. If this argument is
+ * specified, no other arguments should be.
+ * Specified in value.pointer.p. If the
+ * operation succeeds the context will be
+ * freed. */
+ cert_pi_certList = 3, /* specify the chain to validate against. If
+ * this value is given, then the path
+ * construction step in the validation is
+ * skipped. Specified in value.pointer.chain */
+ cert_pi_policyOID = 4, /* validate certificate for policy OID.
+ * Specified in value.array.oids. Cert must
+ * be good for at least one OID in order
+ * to validate. Default is that the user is not
+ * concerned about certificate policy. */
+ cert_pi_policyFlags = 5, /* flags for each policy specified in policyOID.
+ * Specified in value.scalar.ul. Policy flags
+ * apply to all specified oids.
+ * Use CERT_POLICY_FLAG_* macros below. If not
+ * specified policy flags default to 0 */
+ cert_pi_keyusage = 6, /* specify what the keyusages the certificate
+ * will be evaluated against, specified in
+ * value.scalar.ui. The cert must validate for
+ * at least one of the specified key usages.
+ * Values match the KU_ bit flags defined
+ * in this file. Default is derived from
+ * the 'usages' function argument */
+ cert_pi_extendedKeyusage = 7, /* specify what the required extended key
+ * usage of the certificate. Specified as
+ * an array of oidTags in value.array.oids.
+ * The cert must validate for at least one
+ * of the specified extended key usages.
+ * If not specified, no extended key usages
+ * will be checked. */
+ cert_pi_date = 8, /* validate certificate is valid as of date
+ * specified in value.scalar.time. A special
+ * value '0' indicates 'now'. default is '0' */
+ cert_pi_revocationFlags = 9, /* Specify what revocation checking to do.
+ * See CERT_REV_FLAG_* macros below
+ * Set in value.pointer.revocation */
+ cert_pi_certStores = 10, /* Bitmask of Cert Store flags (see below)
+ * Set in value.scalar.ui */
+ cert_pi_trustAnchors =
+ 11, /* Specify the list of trusted roots to
+ * validate against.
+ * The default set of trusted roots, these are
+ * root CA certs from libnssckbi.so or CA
+ * certs trusted by user, are used in any of
+ * the following cases:
+ * * when the parameter is not set.
+ * * when the list of trust anchors is
+ * empty.
+ * Note that this handling can be further
+ * altered by altering the
+ * cert_pi_useOnlyTrustAnchors flag
+ * Specified in value.pointer.chain */
+ cert_pi_useAIACertFetch = 12, /* Enables cert fetching using AIA extension.
+ * In NSS 3.12.1 or later. Default is off.
+ * Value is in value.scalar.b */
+ cert_pi_chainVerifyCallback = 13,
+ /* The callback container for doing extra
+ * validation on the currently calculated chain.
+ * Value is in value.pointer.chainVerifyCallback */
+ cert_pi_useOnlyTrustAnchors = 14,
+ /* If true, disables trusting any
+ * certificates other than the ones passed in via cert_pi_trustAnchors.
+ * If false, then the certificates specified via cert_pi_trustAnchors
+ * will be combined with the pre-existing trusted roots, but only
+ * for the certificate validation being performed.
+ * If no value has been supplied via cert_pi_trustAnchors, this has
+ * no effect.
+ * The default value is true, meaning if this is not supplied, only
+ * trust anchors supplied via cert_pi_trustAnchors are trusted.
+ * Specified in value.scalar.b */
+ cert_pi_max /* SPECIAL: signifies maximum allowed value,
+ * can increase in future releases */
+} CERTValParamInType;
+
+/*
+ * for all out parameters:
+ * out parameters are only returned if the caller asks for them in
+ * the CERTValOutParam array. Caller is responsible for the CERTValOutParam
+ * array itself. The pkix verify function will allocate and other arrays
+ * pointers, or objects. The Caller is responsible for freeing those results.
+ * If SECWouldBlock is returned, only cert_pi_nbioContext is returned.
+ */
+typedef enum {
+ cert_po_end = 0, /* SPECIAL: signifies end of array of
+ * CERTValParam* */
+ cert_po_nbioContext = 1, /* Return a nonblocking context. If no
+ * non-blocking context is specified, then
+ * blocking IO will be used.
+ * Returned in value.pointer.p. The context is
+ * freed after an abort or a complete operation.
+ * This value is only returned on SECWouldBlock.
+ */
+ cert_po_trustAnchor = 2, /* Return the trust anchor for the chain that
+ * was validated. Returned in
+ * value.pointer.cert, this value is only
+ * returned on SECSuccess. */
+ cert_po_certList = 3, /* Return the entire chain that was validated.
+ * Returned in value.pointer.certList. If no
+ * chain could be constructed, this value
+ * would be NULL. */
+ cert_po_policyOID = 4, /* Return the policies that were found to be
+ * valid. Returned in value.array.oids as an
+ * array. This is only returned on
+ * SECSuccess. */
+ cert_po_errorLog = 5, /* Return a log of problems with the chain.
+ * Returned in value.pointer.log */
+ cert_po_usages = 6, /* Return what usages the certificate is valid
+ for. Returned in value.scalar.usages */
+ cert_po_keyUsage = 7, /* Return what key usages the certificate
+ * is valid for.
+ * Returned in value.scalar.usage */
+ cert_po_extendedKeyusage = 8, /* Return what extended key usages the
+ * certificate is valid for.
+ * Returned in value.array.oids */
+ cert_po_max /* SPECIAL: signifies maximum allowed value,
+ * can increase in future releases */
+
+} CERTValParamOutType;
+
+typedef enum {
+ cert_revocation_method_crl = 0,
+ cert_revocation_method_ocsp,
+ cert_revocation_method_count
+} CERTRevocationMethodIndex;
+
+/*
+ * The following flags are supposed to be used to control bits in
+ * each integer contained in the array pointed to be:
+ * CERTRevocationTests.cert_rev_flags_per_method
+ * All Flags are prefixed by CERT_REV_M_, where _M_ indicates
+ * this is a method dependent flag.
+ */
+
+/*
+ * Whether or not to use a method for revocation testing.
+ * If set to "do not test", then all other flags are ignored.
+ */
+#define CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD 0UL
+#define CERT_REV_M_TEST_USING_THIS_METHOD 1UL
+
+/*
+ * Whether or not NSS is allowed to attempt to fetch fresh information
+ * from the network.
+ * (Although fetching will never happen if fresh information for the
+ * method is already locally available.)
+ */
+#define CERT_REV_M_ALLOW_NETWORK_FETCHING 0UL
+#define CERT_REV_M_FORBID_NETWORK_FETCHING 2UL
+
+/*
+ * Example for an implicit default source:
+ * The globally configured default OCSP responder.
+ * IGNORE means:
+ * ignore the implicit default source, whether it's configured or not.
+ * ALLOW means:
+ * if an implicit default source is configured,
+ * then it overrides any available or missing source in the cert.
+ * if no implicit default source is configured,
+ * then we continue to use what's available (or not available)
+ * in the certs.
+ */
+#define CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE 0UL
+#define CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE 4UL
+
+/*
+ * Defines the behavior if no fresh information is available,
+ * fetching from the network is allowed, but the source of revocation
+ * information is unknown (even after considering implicit sources,
+ * if allowed by other flags).
+ * SKIPT_TEST means:
+ * We ignore that no fresh information is available and
+ * skip this test.
+ * REQUIRE_INFO means:
+ * We still require that fresh information is available.
+ * Other flags define what happens on missing fresh info.
+ */
+#define CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE 0UL
+#define CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE 8UL
+
+/*
+ * Defines the behavior if we are unable to obtain fresh information.
+ * INGORE means:
+ * Return "cert status unknown"
+ * FAIL means:
+ * Return "cert revoked".
+ */
+#define CERT_REV_M_IGNORE_MISSING_FRESH_INFO 0UL
+#define CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO 16UL
+
+/*
+ * What should happen if we were able to find fresh information using
+ * this method, and the data indicated the cert is good?
+ * STOP_TESTING means:
+ * Our success is sufficient, do not continue testing
+ * other methods.
+ * CONTINUE_TESTING means:
+ * We will continue and test the next allowed
+ * specified method.
+ */
+#define CERT_REV_M_STOP_TESTING_ON_FRESH_INFO 0UL
+#define CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO 32UL
+
+/* When this flag is used, libpkix will never attempt to use the GET HTTP
+ * method for OCSP requests; it will always use POST.
+ */
+#define CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP 64UL
+
+/*
+ * The following flags are supposed to be used to control bits in
+ * CERTRevocationTests.cert_rev_method_independent_flags
+ * All Flags are prefixed by CERT_REV_M_, where _M_ indicates
+ * this is a method independent flag.
+ */
+
+/*
+ * This defines the order to checking.
+ * EACH_METHOD_SEPARATELY means:
+ * Do all tests related to a particular allowed method
+ * (both local information and network fetching) in a single step.
+ * Only after testing for a particular method is done,
+ * then switching to the next method will happen.
+ * ALL_LOCAL_INFORMATION_FIRST means:
+ * Start by testing the information for all allowed methods
+ * which are already locally available. Only after that is done
+ * consider to fetch from the network (as allowed by other flags).
+ */
+#define CERT_REV_MI_TEST_EACH_METHOD_SEPARATELY 0UL
+#define CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST 1UL
+
+/*
+ * Use this flag to specify that it's necessary that fresh information
+ * is available for at least one of the allowed methods, but it's
+ * irrelevant which of the mechanisms succeeded.
+ * NO_OVERALL_INFO_REQUIREMENT means:
+ * We strictly follow the requirements for each individual method.
+ * REQUIRE_SOME_FRESH_INFO_AVAILABLE means:
+ * After the individual tests have been executed, we must have
+ * been able to find fresh information using at least one method.
+ * If we were unable to find fresh info, it's a failure.
+ * This setting overrides the CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO
+ * flag on all methods.
+ */
+#define CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT 0UL
+#define CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE 2UL
+
+typedef struct {
+ /*
+ * The size of the array that cert_rev_flags_per_method points to,
+ * meaning, the number of methods that are known and defined
+ * by the caller.
+ */
+ PRUint32 number_of_defined_methods;
+
+ /*
+ * A pointer to an array of integers.
+ * Each integer defines revocation checking for a single method,
+ * by having individual CERT_REV_M_* bits set or not set.
+ * The meaning of index numbers into this array are defined by
+ * enum CERTRevocationMethodIndex
+ * The size of the array must be specified by the caller in the separate
+ * variable number_of_defined_methods.
+ * The size of the array may be smaller than
+ * cert_revocation_method_count, it can happen if a caller
+ * is not yet aware of the latest revocation methods
+ * (or does not want to use them).
+ */
+ PRUint64 *cert_rev_flags_per_method;
+
+ /*
+ * How many preferred methods are specified?
+ * This is equivalent to the size of the array that
+ * preferred_methods points to.
+ * It's allowed to set this value to zero,
+ * then NSS will decide which methods to prefer.
+ */
+ PRUint32 number_of_preferred_methods;
+
+ /* Array that may specify an optional order of preferred methods.
+ * Each array entry shall contain a method identifier as defined
+ * by CERTRevocationMethodIndex.
+ * The entry at index [0] specifies the method with highest preference.
+ * These methods will be tested first for locally available information.
+ * Methods allowed for downloading will be attempted in the same order.
+ */
+ CERTRevocationMethodIndex *preferred_methods;
+
+ /*
+ * An integer which defines certain aspects of revocation checking
+ * (independent of individual methods) by having individual
+ * CERT_REV_MI_* bits set or not set.
+ */
+ PRUint64 cert_rev_method_independent_flags;
+} CERTRevocationTests;
+
+typedef struct {
+ CERTRevocationTests leafTests;
+ CERTRevocationTests chainTests;
+} CERTRevocationFlags;
+
+typedef struct CERTValParamInValueStr {
+ union {
+ PRBool b;
+ PRInt32 i;
+ PRUint32 ui;
+ PRInt64 l;
+ PRUint64 ul;
+ PRTime time;
+ } scalar;
+ union {
+ const void *p;
+ const char *s;
+ const CERTCertificate *cert;
+ const CERTCertList *chain;
+ const CERTRevocationFlags *revocation;
+ const CERTChainVerifyCallback *chainVerifyCallback;
+ } pointer;
+ union {
+ const PRInt32 *pi;
+ const PRUint32 *pui;
+ const PRInt64 *pl;
+ const PRUint64 *pul;
+ const SECOidTag *oids;
+ } array;
+ int arraySize;
+} CERTValParamInValue;
+
+typedef struct CERTValParamOutValueStr {
+ union {
+ PRBool b;
+ PRInt32 i;
+ PRUint32 ui;
+ PRInt64 l;
+ PRUint64 ul;
+ SECCertificateUsage usages;
+ } scalar;
+ union {
+ void *p;
+ char *s;
+ CERTVerifyLog *log;
+ CERTCertificate *cert;
+ CERTCertList *chain;
+ } pointer;
+ union {
+ void *p;
+ SECOidTag *oids;
+ } array;
+ int arraySize;
+} CERTValParamOutValue;
+
+typedef struct {
+ CERTValParamInType type;
+ CERTValParamInValue value;
+} CERTValInParam;
+
+typedef struct {
+ CERTValParamOutType type;
+ CERTValParamOutValue value;
+} CERTValOutParam;
+
+/*
+ * Levels of standards conformance strictness for CERT_NameToAsciiInvertible
+ */
+typedef enum CertStrictnessLevels {
+ CERT_N2A_READABLE = 0, /* maximum human readability */
+ CERT_N2A_STRICT = 10, /* strict RFC compliance */
+ CERT_N2A_INVERTIBLE = 20 /* maximum invertibility,
+ all DirectoryStrings encoded in hex */
+} CertStrictnessLevel;
+
+/*
+ * policy flag defines
+ */
+#define CERT_POLICY_FLAG_NO_MAPPING 1
+#define CERT_POLICY_FLAG_EXPLICIT 2
+#define CERT_POLICY_FLAG_NO_ANY 4
+
+/*
+ * CertStore flags
+ */
+#define CERT_ENABLE_LDAP_FETCH 1
+#define CERT_ENABLE_HTTP_FETCH 2
+
+/* This functin pointer type may be used for any function that takes
+ * a CERTCertificate * and returns an allocated string, which must be
+ * freed by a call to PORT_Free.
+ */
+typedef char *(*CERT_StringFromCertFcn)(CERTCertificate *cert);
+
+/* 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_TimeChoiceTemplate[];
+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[];
+extern const SEC_ASN1Template CERT_SignedCrlTemplate[];
+
+/*
+** 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_SequenceOfCertExtensionTemplate)
+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_ASN1_CHOOSER_DECLARE(CERT_SignedCrlTemplate)
+SEC_ASN1_CHOOSER_DECLARE(CERT_TimeChoiceTemplate)
+
+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 0000000000..f00b88f1d7
--- /dev/null
+++ b/security/nss/lib/certdb/certv3.c
@@ -0,0 +1,222 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Code for dealing with X509.V3 extensions.
+ */
+
+#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(const 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));
+}
+
+/*
+ * 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;
+ PLArenaPool *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_ASN1_GET(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)
+{
+
+ SECStatus rv;
+ SECItem encodedValue = { siBuffer, NULL, 0 };
+ SECItem decodedValue = { siBuffer, NULL, 0 };
+
+ rv = cert_FindExtension(cert->extensions, SEC_OID_X509_SUBJECT_KEY_ID,
+ &encodedValue);
+ if (rv == SECSuccess) {
+ PORTCheapArenaPool tmpArena;
+ PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+ rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &decodedValue,
+ SEC_ASN1_GET(SEC_OctetStringTemplate),
+ &encodedValue);
+ if (rv == SECSuccess) {
+ rv = SECITEM_CopyItem(NULL, retItem, &decodedValue);
+ }
+ PORT_DestroyCheapArena(&tmpArena);
+ }
+ SECITEM_FreeItem(&encodedValue, PR_FALSE);
+ 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(PLArenaPool *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)
+{
+ SECItem keyUsage;
+ SECStatus rv;
+
+ /* There is no extension, v1 or v2 certificate */
+ if (cert->extensions == NULL) {
+ return (SECSuccess);
+ }
+
+ keyUsage.data = NULL;
+
+ /* This code formerly ignored the Key Usage extension if it was
+ ** marked non-critical. That was wrong. Since we do understand it,
+ ** we are obligated to honor it, whether or not it is critical.
+ */
+ rv = CERT_FindKeyUsageExtension(cert, &keyUsage);
+ if (rv == SECFailure) {
+ rv = (PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) ? SECSuccess
+ : SECFailure;
+ } else if (!keyUsage.data || !keyUsage.len || !(keyUsage.data[0] & usage)) {
+ PORT_SetError(SEC_ERROR_CERT_USAGES_INVALID);
+ rv = SECFailure;
+ }
+ 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 0000000000..5db2eb9ef0
--- /dev/null
+++ b/security/nss/lib/certdb/certxutl.c
@@ -0,0 +1,496 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * 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;
+ PLArenaPool *ownerArena;
+ PLArenaPool *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, PLArenaPool *ownerArena,
+ void (*setExts)(void *object, CERTCertExtension **exts))
+{
+ PLArenaPool *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;
+
+ /* set critical field */
+ if (critical) {
+ ext->critical.data = (unsigned char *)&hextrue;
+ ext->critical.len = 1;
+ }
+
+ /* set object ID of the extension and its value */
+ if (copyData) {
+ rv = SECITEM_CopyItem(handle->ownerArena, &ext->id, oid);
+ if (rv) {
+ return (SECFailure);
+ }
+
+ rv = SECITEM_CopyItem(handle->ownerArena, &ext->value, value);
+ if (rv) {
+ return (SECFailure);
+ }
+ } else {
+ ext->id = *oid;
+ 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_ASN1_GET(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;
+}
+
+SECStatus
+CERT_MergeExtensions(void *exthandle, CERTCertExtension **extensions)
+{
+ CERTCertExtension *ext;
+ SECStatus rv = SECSuccess;
+ SECOidTag tag;
+ extNode *node;
+ extRec *handle = exthandle;
+
+ if (!exthandle || !extensions) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ while ((ext = *extensions++) != NULL) {
+ tag = SECOID_FindOIDTag(&ext->id);
+ for (node = handle->head; node != NULL; node = node->next) {
+ if (tag == 0) {
+ if (SECITEM_ItemsAreEqual(&ext->id, &node->ext->id))
+ break;
+ } else {
+ if (SECOID_FindOIDTag(&node->ext->id) == tag) {
+ break;
+ }
+ }
+ }
+ if (node == NULL) {
+ PRBool critical = (ext->critical.len != 0 &&
+ ext->critical.data[ext->critical.len - 1] != 0);
+ if (critical && tag == SEC_OID_UNKNOWN) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
+ rv = SECFailure;
+ break;
+ }
+ /* add to list */
+ rv = CERT_AddExtensionByOID(exthandle, &ext->id, &ext->value,
+ critical, PR_TRUE);
+ if (rv != SECSuccess)
+ break;
+ }
+ }
+ 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;
+ PORTCheapArenaPool tmpArena;
+
+ wrapperItem.data = NULL;
+ tmpItem.data = NULL;
+
+ PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+
+ rv = cert_FindExtension(extensions, tag, &wrapperItem);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &tmpItem,
+ SEC_ASN1_GET(SEC_BitStringTemplate),
+ &wrapperItem);
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ retItem->data = (unsigned char *)PORT_ZAlloc((tmpItem.len + 7) >> 3);
+ if (retItem->data == NULL) {
+ goto loser;
+ }
+
+ if (tmpItem.len > 0) {
+ PORT_Memcpy(retItem->data, tmpItem.data, (tmpItem.len + 7) >> 3);
+ }
+
+ retItem->len = tmpItem.len;
+
+ rv = SECSuccess;
+ goto done;
+
+loser:
+ rv = SECFailure;
+
+done:
+ PORT_DestroyCheapArena(&tmpArena);
+
+ 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 0000000000..a8c76b5cfa
--- /dev/null
+++ b/security/nss/lib/certdb/certxutl.h
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * 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/crl.c b/security/nss/lib/certdb/crl.c
new file mode 100644
index 0000000000..d110841241
--- /dev/null
+++ b/security/nss/lib/certdb/crl.c
@@ -0,0 +1,3046 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Moved from secpkcs7.c
+ */
+
+#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"
+#if defined(DPC_RWLOCK) || defined(GLOBAL_RWLOCK)
+#include "nssrwlk.h"
+#endif
+#include "pk11priv.h"
+
+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 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 }
+};
+
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+SEC_ASN1_MKSUB(CERT_TimeChoiceTemplate)
+
+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_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrlEntry, revocationDate),
+ SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
+ { 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 | SEC_ASN1_XTRN, offsetof(CERTCrl, signatureAlg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_SAVE, offsetof(CERTCrl, derName) },
+ { SEC_ASN1_INLINE, offsetof(CERTCrl, name), CERT_NameTemplate },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrl, lastUpdate),
+ SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
+ { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL | SEC_ASN1_XTRN,
+ offsetof(CERTCrl, nextUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
+ { 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 | SEC_ASN1_XTRN, offsetof(CERTCrl, signatureAlg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_SAVE, offsetof(CERTCrl, derName) },
+ { SEC_ASN1_INLINE, offsetof(CERTCrl, name), CERT_NameTemplate },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrl, lastUpdate),
+ SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
+ { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL | SEC_ASN1_XTRN,
+ offsetof(CERTCrl, nextUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
+ { 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_INLINE | SEC_ASN1_XTRN,
+ offsetof(CERTCrl, lastUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
+ { SEC_ASN1_SKIP | SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL | SEC_ASN1_XTRN,
+ offsetof(CERTCrl, nextUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF, offsetof(CERTCrl, entries),
+ cert_CrlEntryTemplate }, /* decode entries */
+ { SEC_ASN1_SKIP_REST },
+ { 0 }
+};
+
+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 | SEC_ASN1_XTRN,
+ offsetof(CERTSignedCrl, signatureWrap.signatureAlgorithm),
+ SEC_ASN1_SUB(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 | SEC_ASN1_XTRN,
+ offsetof(CERTSignedCrl, signatureWrap.signatureAlgorithm),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_BIT_STRING, offsetof(CERTSignedCrl, signatureWrap.signature) },
+ { 0 }
+};
+
+const SEC_ASN1Template CERT_SetOfSignedCrlTemplate[] = {
+ { SEC_ASN1_SET_OF, 0, CERT_SignedCrlTemplate },
+};
+
+/* get CRL version */
+int
+cert_get_crl_version(CERTCrl* crl)
+{
+ /* CRL version is defaulted to v1 */
+ int version = SEC_CRL_VERSION_1;
+ if (crl && crl->version.data != 0) {
+ version = (int)DER_GetUInteger(&crl->version);
+ }
+ return version;
+}
+
+/* check the entries in the CRL */
+SECStatus
+cert_check_crl_entries(CERTCrl* crl)
+{
+ CERTCrlEntry** entries;
+ CERTCrlEntry* entry;
+ PRBool hasCriticalExten = PR_FALSE;
+ SECStatus rv = SECSuccess;
+
+ if (!crl) {
+ return SECFailure;
+ }
+
+ if (crl->entries == NULL) {
+ /* CRLs with no entries are valid */
+ 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) {
+ if (cert_get_crl_version(crl) != SEC_CRL_VERSION_2) {
+ /* only CRL v2 critical extensions are supported */
+ PORT_SetError(SEC_ERROR_CRL_V1_CRITICAL_EXTENSION);
+ 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_CRL_UNKNOWN_CRITICAL_EXTENSION);
+ rv = SECFailure;
+ break;
+ }
+ }
+ ++entries;
+ }
+ return (rv);
+}
+
+/* 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)
+{
+ PRBool hasCriticalExten = PR_FALSE;
+ int version = cert_get_crl_version(crl);
+
+ if (version > SEC_CRL_VERSION_2) {
+ PORT_SetError(SEC_ERROR_CRL_INVALID_VERSION);
+ 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) {
+ /* only CRL v2 critical extensions are supported */
+ PORT_SetError(SEC_ERROR_CRL_V1_CRITICAL_EXTENSION);
+ return (SECFailure);
+ }
+ /* make sure that there is no unknown critical extension */
+ if (cert_HasUnknownCriticalExten(crl->extensions) == PR_TRUE) {
+ PORT_SetError(SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION);
+ return (SECFailure);
+ }
+ }
+ }
+
+ return (SECSuccess);
+}
+
+/*
+ * Generate a database key, based on the issuer name from a
+ * DER crl.
+ */
+SECStatus
+CERT_KeyFromDERCrl(PLArenaPool* arena, SECItem* derCrl, SECItem* key)
+{
+ SECStatus rv;
+ CERTSignedData sd;
+ CERTCrlKey crlkey;
+ PLArenaPool* myArena;
+
+ if (!arena) {
+ /* arena needed for QuickDER */
+ myArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ } else {
+ myArena = arena;
+ }
+ PORT_Memset(&sd, 0, sizeof(sd));
+ rv = SEC_QuickDERDecodeItem(myArena, &sd, CERT_SignedDataTemplate, derCrl);
+ if (SECSuccess == rv) {
+ PORT_Memset(&crlkey, 0, sizeof(crlkey));
+ rv = SEC_QuickDERDecodeItem(myArena, &crlkey, cert_CrlKeyTemplate,
+ &sd.data);
+ }
+
+ /* make a copy so the data doesn't point to memory inside derCrl, which
+ may be temporary */
+ if (SECSuccess == rv) {
+ rv = SECITEM_CopyItem(arena, key, &crlkey.derName);
+ }
+
+ if (myArena != arena) {
+ PORT_FreeArena(myArena, PR_FALSE);
+ }
+
+ return rv;
+}
+
+#define GetOpaqueCRLFields(x) ((OpaqueCRLFields*)x->opaque)
+
+SECStatus
+CERT_CompleteCRLDecodeEntries(CERTSignedCrl* crl)
+{
+ SECStatus rv = SECSuccess;
+ SECItem* crldata = NULL;
+ OpaqueCRLFields* extended = NULL;
+
+ if ((!crl) || (!(extended = (OpaqueCRLFields*)crl->opaque)) ||
+ (PR_TRUE == extended->decodingError)) {
+ 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->decodingError = 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 */
+ }
+ rv = cert_check_crl_entries(&crl->crl);
+ if (rv != SECSuccess) {
+ extended->badExtensions = PR_TRUE;
+ }
+ }
+ return rv;
+}
+
+/*
+ * take a DER CRL and decode it into a CRL structure
+ * allow reusing the input DER without making a copy
+ */
+CERTSignedCrl*
+CERT_DecodeDERCrlWithFlags(PLArenaPool* narena, SECItem* derSignedCrl, int type,
+ PRInt32 options)
+{
+ PLArenaPool* arena;
+ CERTSignedCrl* crl;
+ SECStatus rv;
+ OpaqueCRLFields* extended = NULL;
+ const SEC_ASN1Template* crlTemplate = CERT_SignedCrlTemplate;
+ PRInt32 testOptions = options;
+
+ PORT_Assert(derSignedCrl);
+ if (!derSignedCrl) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ /* Adopting DER requires not copying it. Code that sets ADOPT flag
+ * but doesn't set DONT_COPY probably doesn't know What it is doing.
+ * That condition is a programming error in the caller.
+ */
+ testOptions &= (CRL_DECODE_ADOPT_HEAP_DER | CRL_DECODE_DONT_COPY_DER);
+ PORT_Assert(testOptions != CRL_DECODE_ADOPT_HEAP_DER);
+ if (testOptions == CRL_DECODE_ADOPT_HEAP_DER) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ /* make a new arena if needed */
+ 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_ADOPT_HEAP_DER) {
+ extended->heapDER = PR_TRUE;
+ }
+ 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 extensions */
+ rv = cert_check_crl_version(&crl->crl);
+ if (rv != SECSuccess) {
+ extended->badExtensions = PR_TRUE;
+ break;
+ }
+
+ if (PR_TRUE == extended->partial) {
+ /* partial decoding, don't verify entries */
+ break;
+ }
+
+ rv = cert_check_crl_entries(&crl->crl);
+ if (rv != SECSuccess) {
+ extended->badExtensions = PR_TRUE;
+ }
+
+ break;
+
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ rv = SECFailure;
+ break;
+ }
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ crl->referenceCount = 1;
+
+ return (crl);
+
+loser:
+ if (options & CRL_DECODE_KEEP_BAD_CRL) {
+ if (extended) {
+ extended->decodingError = PR_TRUE;
+ }
+ if (crl) {
+ crl->referenceCount = 1;
+ return (crl);
+ }
+ }
+
+ if ((narena == NULL) && arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return (0);
+}
+
+/*
+ * take a DER CRL and decode it into a CRL structure
+ */
+CERTSignedCrl*
+CERT_DecodeDERCrl(PLArenaPool* 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 decodable DER CRL, or no CRL at all.
+ * Caller may distinguish those cases by the value returned in "decoded".
+ * When DER CRL is not found, error code will be SEC_ERROR_CRL_NOT_FOUND.
+ *
+ * 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;
+
+ PORT_Assert(decoded);
+ if (!decoded) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ 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 */
+ int nsserror = PORT_GetError();
+ if (nsserror != SEC_ERROR_CRL_NOT_FOUND) {
+ rv = SECFailure;
+ }
+ goto loser;
+ }
+ PORT_Assert(crlHandle != CK_INVALID_HANDLE);
+ /* PK11_FindCrlByName obtained a slot reference. */
+
+ /* derCRL is a fresh HEAP copy made for us by PK11_FindCrlByName.
+ Force adoption of the DER CRL from the heap - this will cause it
+ to be automatically freed when SEC_DestroyCrl is invoked */
+ decodeoptions |= (CRL_DECODE_ADOPT_HEAP_DER | CRL_DECODE_DONT_COPY_DER);
+
+ crl = CERT_DecodeDERCrlWithFlags(NULL, derCrl, type, decodeoptions);
+ if (crl) {
+ crl->slot = slot;
+ slot = NULL; /* adopt it */
+ derCrl = NULL; /* adopted by the crl struct */
+ crl->pkcs11ID = crlHandle;
+ if (url) {
+ crl->url = PORT_ArenaStrdup(crl->arena, url);
+ }
+ } else {
+ rv = SECFailure;
+ }
+
+ if (url) {
+ PORT_Free(url);
+ }
+
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+
+loser:
+ if (derCrl) {
+ SECITEM_FreeItem(derCrl, PR_TRUE);
+ }
+
+ *decoded = crl;
+
+ return rv;
+}
+
+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 = CK_INVALID_HANDLE;
+
+ PORT_Assert(newCrl);
+ PORT_Assert(derCrl);
+ PORT_Assert(type == SEC_CRL_TYPE);
+
+ if (type != SEC_CRL_TYPE) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ /* we can't use the cache here because we must look in the same
+ token */
+ (void)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;
+ if (oldCrl->url && !url)
+ url = oldCrl->url;
+ if (url)
+ crl->url = PORT_ArenaStrdup(crl->arena, url);
+ goto done;
+ }
+ if (!SEC_CrlIsNewer(&newCrl->crl, &oldCrl->crl)) {
+ PORT_SetError(SEC_ERROR_OLD_CRL);
+ goto done;
+ }
+
+ /* if we have a url in the database, use that one */
+ if (oldCrl->url && !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)
+{
+ PLArenaPool* 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_ATOMIC_INCREMENT(&acrl->referenceCount);
+ return acrl;
+ }
+ return NULL;
+}
+
+SECStatus
+SEC_DestroyCrl(CERTSignedCrl* crl)
+{
+ if (crl) {
+ if (PR_ATOMIC_DECREMENT(&crl->referenceCount) < 1) {
+ if (crl->slot) {
+ PK11_FreeSlot(crl->slot);
+ }
+ if (GetOpaqueCRLFields(crl) &&
+ PR_TRUE == GetOpaqueCRLFields(crl)->heapDER) {
+ SECITEM_FreeItem(crl->derCrl, PR_TRUE);
+ }
+ if (crl->arena) {
+ PORT_FreeArena(crl->arena, PR_FALSE);
+ }
+ }
+ return SECSuccess;
+ } else {
+ return SECFailure;
+ }
+}
+
+SECStatus
+SEC_LookupCrls(CERTCertDBHandle* handle, CERTCrlHeadNode** nodes, int type)
+{
+ CERTCrlHeadNode* head;
+ PLArenaPool* 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_SignedCrlTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SetOfSignedCrlTemplate)
+
+/* CRL cache code starts here */
+
+/* constructor */
+static SECStatus CachedCrl_Create(CachedCrl** returned, CERTSignedCrl* crl,
+ CRLOrigin origin);
+/* destructor */
+static SECStatus CachedCrl_Destroy(CachedCrl* crl);
+
+/* create hash table of CRL entries */
+static SECStatus CachedCrl_Populate(CachedCrl* crlobject);
+
+/* empty the cache content */
+static SECStatus CachedCrl_Depopulate(CachedCrl* crl);
+
+/* are these CRLs the same, as far as the cache is concerned ?
+ Or are they the same token object, but with different DER ? */
+
+static SECStatus CachedCrl_Compare(CachedCrl* a, CachedCrl* b, PRBool* isDupe,
+ PRBool* isUpdated);
+
+/* create a DPCache object */
+static SECStatus DPCache_Create(CRLDPCache** returned, CERTCertificate* issuer,
+ const SECItem* subject, SECItem* dp);
+
+/* destructor for CRL DPCache object */
+static SECStatus DPCache_Destroy(CRLDPCache* cache);
+
+/* add a new CRL object to the dynamic array of CRLs of the DPCache, and
+ returns the cached CRL object . Needs write access to DPCache. */
+static SECStatus DPCache_AddCRL(CRLDPCache* cache, CachedCrl* crl,
+ PRBool* added);
+
+/* fetch the CRL for this DP from the PKCS#11 tokens */
+static SECStatus DPCache_FetchFromTokens(CRLDPCache* cache, PRTime vfdate,
+ void* wincx);
+
+/* update the content of the CRL cache, including fetching of CRLs, and
+ reprocessing with specified issuer and date */
+static SECStatus DPCache_GetUpToDate(CRLDPCache* cache, CERTCertificate* issuer,
+ PRBool readlocked, PRTime vfdate,
+ void* wincx);
+
+/* returns true if there are CRLs from PKCS#11 slots */
+static PRBool DPCache_HasTokenCRLs(CRLDPCache* cache);
+
+/* remove CRL at offset specified */
+static SECStatus DPCache_RemoveCRL(CRLDPCache* cache, PRUint32 offset);
+
+/* Pick best CRL to use . needs write access */
+static SECStatus DPCache_SelectCRL(CRLDPCache* cache);
+
+/* create an issuer cache object (per CA subject ) */
+static SECStatus IssuerCache_Create(CRLIssuerCache** returned,
+ CERTCertificate* issuer,
+ const SECItem* subject, const SECItem* dp);
+
+/* destructor for CRL IssuerCache object */
+SECStatus IssuerCache_Destroy(CRLIssuerCache* cache);
+
+/* add a DPCache to the issuer cache */
+static SECStatus IssuerCache_AddDP(CRLIssuerCache* cache,
+ CERTCertificate* issuer,
+ const SECItem* subject, const SECItem* dp,
+ CRLDPCache** newdpc);
+
+/* get a particular DPCache object from an IssuerCache */
+static CRLDPCache* IssuerCache_GetDPCache(CRLIssuerCache* cache,
+ const SECItem* dp);
+
+/*
+** Pre-allocator hash allocator ops.
+*/
+
+/* allocate memory for hash table */
+static void* PR_CALLBACK
+PreAllocTable(void* pool, PRSize size)
+{
+ PreAllocator* alloc = (PreAllocator*)pool;
+ PORT_Assert(alloc);
+ if (!alloc) {
+ /* no allocator, or buffer full */
+ return NULL;
+ }
+ if (size > (alloc->len - alloc->used)) {
+ /* initial buffer full, let's use the arena */
+ alloc->extra += size;
+ return PORT_ArenaAlloc(alloc->arena, size);
+ }
+ /* use the initial buffer */
+ alloc->used += size;
+ return (char*)alloc->data + alloc->used - size;
+}
+
+/* free hash table memory.
+ Individual PreAllocator elements cannot be freed, so this is a no-op. */
+static void PR_CALLBACK
+PreFreeTable(void* pool, void* item)
+{
+}
+
+/* allocate memory for hash table */
+static PLHashEntry* PR_CALLBACK
+PreAllocEntry(void* pool, const void* key)
+{
+ return PreAllocTable(pool, sizeof(PLHashEntry));
+}
+
+/* free hash table entry.
+ Individual PreAllocator elements cannot be freed, so this is a no-op. */
+static void PR_CALLBACK
+PreFreeEntry(void* pool, PLHashEntry* he, PRUintn flag)
+{
+}
+
+/* methods required for PL hash table functions */
+static PLHashAllocOps preAllocOps = { PreAllocTable, PreFreeTable,
+ PreAllocEntry, PreFreeEntry };
+
+/* destructor for PreAllocator object */
+void
+PreAllocator_Destroy(PreAllocator* allocator)
+{
+ if (!allocator) {
+ return;
+ }
+ if (allocator->arena) {
+ PORT_FreeArena(allocator->arena, PR_TRUE);
+ }
+}
+
+/* constructor for PreAllocator object */
+PreAllocator*
+PreAllocator_Create(PRSize size)
+{
+ PLArenaPool* arena = NULL;
+ PreAllocator* prebuffer = NULL;
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena) {
+ return NULL;
+ }
+ prebuffer = (PreAllocator*)PORT_ArenaZAlloc(arena, sizeof(PreAllocator));
+ if (!prebuffer) {
+ PORT_FreeArena(arena, PR_TRUE);
+ return NULL;
+ }
+ prebuffer->arena = arena;
+
+ if (size) {
+ prebuffer->len = size;
+ prebuffer->data = PORT_ArenaAlloc(arena, size);
+ if (!prebuffer->data) {
+ PORT_FreeArena(arena, PR_TRUE);
+ return NULL;
+ }
+ }
+ return prebuffer;
+}
+
+/* global Named CRL cache object */
+static NamedCRLCache namedCRLCache = { NULL, NULL };
+
+/* global CRL cache object */
+static CRLCache crlcache = { NULL, NULL };
+
+/* initial state is off */
+static PRBool crlcache_initialized = PR_FALSE;
+
+PRTime CRLCache_Empty_TokenFetch_Interval = 60 * 1000000; /* how often
+ to query the tokens for CRL objects, in order to discover new objects, if
+ the cache does not contain any token CRLs . In microseconds */
+
+PRTime CRLCache_TokenRefetch_Interval = 600 * 1000000; /* how often
+ to query the tokens for CRL objects, in order to discover new objects, if
+ the cache already contains token CRLs In microseconds */
+
+PRTime CRLCache_ExistenceCheck_Interval = 60 * 1000000; /* how often to check
+ if a token CRL object still exists. In microseconds */
+
+/* this function is called at NSS initialization time */
+SECStatus
+InitCRLCache(void)
+{
+ if (PR_FALSE == crlcache_initialized) {
+ PORT_Assert(NULL == crlcache.lock);
+ PORT_Assert(NULL == crlcache.issuers);
+ PORT_Assert(NULL == namedCRLCache.lock);
+ PORT_Assert(NULL == namedCRLCache.entries);
+ if (crlcache.lock || crlcache.issuers || namedCRLCache.lock ||
+ namedCRLCache.entries) {
+ /* CRL cache already partially initialized */
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+#ifdef GLOBAL_RWLOCK
+ crlcache.lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
+#else
+ crlcache.lock = PR_NewLock();
+#endif
+ namedCRLCache.lock = PR_NewLock();
+ crlcache.issuers = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
+ PL_CompareValues, NULL, NULL);
+ namedCRLCache.entries = PL_NewHashTable(
+ 0, SECITEM_Hash, SECITEM_HashCompare, PL_CompareValues, NULL, NULL);
+ if (!crlcache.lock || !namedCRLCache.lock || !crlcache.issuers ||
+ !namedCRLCache.entries) {
+ if (crlcache.lock) {
+#ifdef GLOBAL_RWLOCK
+ NSSRWLock_Destroy(crlcache.lock);
+#else
+ PR_DestroyLock(crlcache.lock);
+#endif
+ crlcache.lock = NULL;
+ }
+ if (namedCRLCache.lock) {
+ PR_DestroyLock(namedCRLCache.lock);
+ namedCRLCache.lock = NULL;
+ }
+ if (crlcache.issuers) {
+ PL_HashTableDestroy(crlcache.issuers);
+ crlcache.issuers = NULL;
+ }
+ if (namedCRLCache.entries) {
+ PL_HashTableDestroy(namedCRLCache.entries);
+ namedCRLCache.entries = NULL;
+ }
+
+ return SECFailure;
+ }
+ crlcache_initialized = PR_TRUE;
+ return SECSuccess;
+ } else {
+ PORT_Assert(crlcache.lock);
+ PORT_Assert(crlcache.issuers);
+ if ((NULL == crlcache.lock) || (NULL == crlcache.issuers)) {
+ /* CRL cache not fully initialized */
+ return SECFailure;
+ } else {
+ /* CRL cache already initialized */
+ return SECSuccess;
+ }
+ }
+}
+
+/* destructor for CRL DPCache object */
+static SECStatus
+DPCache_Destroy(CRLDPCache* cache)
+{
+ PRUint32 i = 0;
+ PORT_Assert(cache);
+ if (!cache) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (cache->lock) {
+#ifdef DPC_RWLOCK
+ NSSRWLock_Destroy(cache->lock);
+#else
+ PR_DestroyLock(cache->lock);
+#endif
+ } else {
+ PORT_Assert(0);
+ return SECFailure;
+ }
+ /* destroy all our CRL objects */
+ for (i = 0; i < cache->ncrls; i++) {
+ if (!cache->crls || !cache->crls[i] ||
+ SECSuccess != CachedCrl_Destroy(cache->crls[i])) {
+ return SECFailure;
+ }
+ }
+ /* free the array of CRLs */
+ if (cache->crls) {
+ PORT_Free(cache->crls);
+ }
+ /* destroy the cert */
+ if (cache->issuerDERCert) {
+ SECITEM_FreeItem(cache->issuerDERCert, PR_TRUE);
+ }
+ /* 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);
+ }
+ PORT_Free(cache);
+ return SECSuccess;
+}
+
+/* destructor for CRL IssuerCache object */
+SECStatus
+IssuerCache_Destroy(CRLIssuerCache* cache)
+{
+ PORT_Assert(cache);
+ if (!cache) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+#ifdef XCRL
+ if (cache->lock) {
+ NSSRWLock_Destroy(cache->lock);
+ } else {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (cache->issuer) {
+ CERT_DestroyCertificate(cache->issuer);
+ }
+#endif
+ /* free the subject */
+ if (cache->subject) {
+ SECITEM_FreeItem(cache->subject, PR_TRUE);
+ }
+ if (SECSuccess != DPCache_Destroy(cache->dpp)) {
+ PORT_Assert(0);
+ return SECFailure;
+ }
+ PORT_Free(cache);
+ return SECSuccess;
+}
+
+/* create a named CRL entry object */
+static SECStatus
+NamedCRLCacheEntry_Create(NamedCRLCacheEntry** returned)
+{
+ NamedCRLCacheEntry* entry = NULL;
+ if (!returned) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ *returned = NULL;
+ entry = (NamedCRLCacheEntry*)PORT_ZAlloc(sizeof(NamedCRLCacheEntry));
+ if (!entry) {
+ return SECFailure;
+ }
+ *returned = entry;
+ return SECSuccess;
+}
+
+/* destroy a named CRL entry object */
+static SECStatus
+NamedCRLCacheEntry_Destroy(NamedCRLCacheEntry* entry)
+{
+ if (!entry) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (entry->crl) {
+ /* named CRL cache owns DER memory */
+ SECITEM_ZfreeItem(entry->crl, PR_TRUE);
+ }
+ if (entry->canonicalizedName) {
+ SECITEM_FreeItem(entry->canonicalizedName, PR_TRUE);
+ }
+ PORT_Free(entry);
+ return SECSuccess;
+}
+
+/* callback function used in hash table destructor */
+static PRIntn PR_CALLBACK
+FreeIssuer(PLHashEntry* he, PRIntn i, void* arg)
+{
+ CRLIssuerCache* issuer = NULL;
+ SECStatus* rv = (SECStatus*)arg;
+
+ PORT_Assert(he);
+ if (!he) {
+ return HT_ENUMERATE_NEXT;
+ }
+ issuer = (CRLIssuerCache*)he->value;
+ PORT_Assert(issuer);
+ if (issuer) {
+ if (SECSuccess != IssuerCache_Destroy(issuer)) {
+ PORT_Assert(rv);
+ if (rv) {
+ *rv = SECFailure;
+ }
+ return HT_ENUMERATE_NEXT;
+ }
+ }
+ return HT_ENUMERATE_NEXT;
+}
+
+/* callback function used in hash table destructor */
+static PRIntn PR_CALLBACK
+FreeNamedEntries(PLHashEntry* he, PRIntn i, void* arg)
+{
+ NamedCRLCacheEntry* entry = NULL;
+ SECStatus* rv = (SECStatus*)arg;
+
+ PORT_Assert(he);
+ if (!he) {
+ return HT_ENUMERATE_NEXT;
+ }
+ entry = (NamedCRLCacheEntry*)he->value;
+ PORT_Assert(entry);
+ if (entry) {
+ if (SECSuccess != NamedCRLCacheEntry_Destroy(entry)) {
+ PORT_Assert(rv);
+ if (rv) {
+ *rv = SECFailure;
+ }
+ return HT_ENUMERATE_NEXT;
+ }
+ }
+ return HT_ENUMERATE_NEXT;
+}
+
+/* needs to be called at NSS shutdown time
+ This will destroy the global CRL cache, including
+ - the hash table of issuer cache objects
+ - the issuer cache objects
+ - DPCache objects in issuer cache objects */
+SECStatus
+ShutdownCRLCache(void)
+{
+ SECStatus rv = SECSuccess;
+ if (PR_FALSE == crlcache_initialized && !crlcache.lock &&
+ !crlcache.issuers) {
+ /* CRL cache has already been shut down */
+ return SECSuccess;
+ }
+ if (PR_TRUE == crlcache_initialized &&
+ (!crlcache.lock || !crlcache.issuers || !namedCRLCache.lock ||
+ !namedCRLCache.entries)) {
+ /* CRL cache has partially been shut down */
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ /* empty the CRL cache */
+ /* free the issuers */
+ PL_HashTableEnumerateEntries(crlcache.issuers, &FreeIssuer, &rv);
+ /* free the hash table of issuers */
+ PL_HashTableDestroy(crlcache.issuers);
+ crlcache.issuers = NULL;
+/* free the global lock */
+#ifdef GLOBAL_RWLOCK
+ NSSRWLock_Destroy(crlcache.lock);
+#else
+ PR_DestroyLock(crlcache.lock);
+#endif
+ crlcache.lock = NULL;
+
+ /* empty the named CRL cache. This must be done after freeing the CRL
+ * cache, since some CRLs in this cache are in the memory for the other */
+ /* free the entries */
+ PL_HashTableEnumerateEntries(namedCRLCache.entries, &FreeNamedEntries, &rv);
+ /* free the hash table of issuers */
+ PL_HashTableDestroy(namedCRLCache.entries);
+ namedCRLCache.entries = NULL;
+ /* free the global lock */
+ PR_DestroyLock(namedCRLCache.lock);
+ namedCRLCache.lock = NULL;
+
+ crlcache_initialized = PR_FALSE;
+ return rv;
+}
+
+/* add a new CRL object to the dynamic array of CRLs of the DPCache, and
+ returns the cached CRL object . Needs write access to DPCache. */
+static SECStatus
+DPCache_AddCRL(CRLDPCache* cache, CachedCrl* newcrl, PRBool* added)
+{
+ CachedCrl** newcrls = NULL;
+ PRUint32 i = 0;
+ PORT_Assert(cache);
+ PORT_Assert(newcrl);
+ PORT_Assert(added);
+ if (!cache || !newcrl || !added) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ *added = PR_FALSE;
+ /* before adding a new CRL, check if it is a duplicate */
+ for (i = 0; i < cache->ncrls; i++) {
+ CachedCrl* existing = NULL;
+ SECStatus rv = SECSuccess;
+ PRBool dupe = PR_FALSE, updated = PR_FALSE;
+ if (!cache->crls) {
+ PORT_Assert(0);
+ return SECFailure;
+ }
+ existing = cache->crls[i];
+ if (!existing) {
+ PORT_Assert(0);
+ return SECFailure;
+ }
+ rv = CachedCrl_Compare(existing, newcrl, &dupe, &updated);
+ if (SECSuccess != rv) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (PR_TRUE == dupe) {
+ /* dupe */
+ PORT_SetError(SEC_ERROR_CRL_ALREADY_EXISTS);
+ return SECSuccess;
+ }
+ if (PR_TRUE == updated) {
+ /* this token CRL is in the same slot and has the same object ID,
+ but different content. We need to remove the old object */
+ if (SECSuccess != DPCache_RemoveCRL(cache, i)) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ newcrls = (CachedCrl**)PORT_Realloc(cache->crls, (cache->ncrls + 1) * sizeof(CachedCrl*));
+ if (!newcrls) {
+ return SECFailure;
+ }
+ cache->crls = newcrls;
+ cache->ncrls++;
+ cache->crls[cache->ncrls - 1] = newcrl;
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+/* remove CRL at offset specified */
+static SECStatus
+DPCache_RemoveCRL(CRLDPCache* cache, PRUint32 offset)
+{
+ CachedCrl* acrl = NULL;
+ PORT_Assert(cache);
+ if (!cache || (!cache->crls) || (!(offset < cache->ncrls))) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ acrl = cache->crls[offset];
+ PORT_Assert(acrl);
+ if (!acrl) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ cache->crls[offset] = cache->crls[cache->ncrls - 1];
+ cache->crls[cache->ncrls - 1] = NULL;
+ cache->ncrls--;
+ if (cache->selected == acrl) {
+ cache->selected = NULL;
+ }
+ if (SECSuccess != CachedCrl_Destroy(acrl)) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/* check whether a CRL object stored in a PKCS#11 token still exists in
+ that token . This has to be efficient (the entire CRL value cannot be
+ transferred accross the token boundaries), so this is accomplished by
+ simply fetching the subject attribute and making sure it hasn't changed .
+ Note that technically, the CRL object could have been replaced with a new
+ PKCS#11 object of the same ID and subject (which actually happens in
+ softoken), but this function has no way of knowing that the object
+ value changed, since CKA_VALUE isn't checked. */
+static PRBool
+TokenCRLStillExists(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(crl->slot);
+ if (!slot) {
+ return PR_FALSE;
+ }
+ oldSubject = &crl->crl.derName;
+ PORT_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) {
+ (void)nssToken_Destroy(instance.token);
+ 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_NSS_CRL != crl_class) {
+ xstatus = PR_FALSE;
+ }
+ } else {
+ xstatus = PR_FALSE;
+ }
+ NSSArena_Destroy(arena);
+ (void)nssToken_Destroy(instance.token);
+ return xstatus;
+}
+
+/* verify the signature of a CRL against its issuer at a given date */
+static SECStatus
+CERT_VerifyCRL(CERTSignedCrl* crlobject, CERTCertificate* issuer, PRTime vfdate,
+ void* wincx)
+{
+ return CERT_VerifySignedData(&crlobject->signatureWrap, issuer, vfdate,
+ wincx);
+}
+
+/* verify a CRL and update cache state */
+static SECStatus
+CachedCrl_Verify(CRLDPCache* cache, CachedCrl* crlobject, PRTime vfdate,
+ void* wincx)
+{
+ /* 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 to have unknown status 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 (!cache || !crlobject) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (PR_TRUE == GetOpaqueCRLFields(crlobject->crl)->decodingError) {
+ crlobject->sigChecked = PR_TRUE; /* we can never verify a CRL
+ with bogus DER. Mark it checked so we won't try again */
+ PORT_SetError(SEC_ERROR_BAD_DER);
+ return SECSuccess;
+ } else {
+ SECStatus signstatus = SECFailure;
+ if (cache->issuerDERCert) {
+ CERTCertificate* issuer = CERT_NewTempCertificate(
+ cache->dbHandle, cache->issuerDERCert, NULL, PR_FALSE, PR_TRUE);
+
+ if (issuer) {
+ signstatus =
+ CERT_VerifyCRL(crlobject->crl, issuer, vfdate, wincx);
+ CERT_DestroyCertificate(issuer);
+ }
+ }
+ if (SECSuccess != signstatus) {
+ if (!cache->issuerDERCert) {
+ /* 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 */
+ } else {
+ crlobject->sigChecked = PR_TRUE;
+ }
+ PORT_SetError(SEC_ERROR_CRL_BAD_SIGNATURE);
+ return SECSuccess;
+ } else {
+ crlobject->sigChecked = PR_TRUE;
+ crlobject->sigValid = PR_TRUE;
+ }
+ }
+
+ return SECSuccess;
+}
+
+/* fetch the CRLs for this DP from the PKCS#11 tokens */
+static SECStatus
+DPCache_FetchFromTokens(CRLDPCache* cache, PRTime vfdate, void* wincx)
+{
+ SECStatus rv = SECSuccess;
+ CERTCrlHeadNode head;
+ if (!cache) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ /* first, initialize list */
+ memset(&head, 0, sizeof(head));
+ head.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ rv = pk11_RetrieveCrls(&head, cache->subject, wincx);
+
+ /* 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) {
+ /* fetch failed, add error bit */
+ cache->invalid |= CRL_CACHE_LAST_FETCH_FAILED;
+ } else {
+ /* fetch was successful, clear this error bit */
+ cache->invalid &= (~CRL_CACHE_LAST_FETCH_FAILED);
+ }
+
+ /* add any CRLs found to our array */
+ if (SECSuccess == rv) {
+ CERTCrlNode* crlNode = NULL;
+
+ for (crlNode = head.first; crlNode; crlNode = crlNode->next) {
+ CachedCrl* returned = NULL;
+ CERTSignedCrl* crlobject = crlNode->crl;
+ if (!crlobject) {
+ PORT_Assert(0);
+ continue;
+ }
+ rv = CachedCrl_Create(&returned, crlobject, CRL_OriginToken);
+ if (SECSuccess == rv) {
+ PRBool added = PR_FALSE;
+ rv = DPCache_AddCRL(cache, returned, &added);
+ if (PR_TRUE != added) {
+ rv = CachedCrl_Destroy(returned);
+ returned = NULL;
+ } else if (vfdate) {
+ rv = CachedCrl_Verify(cache, returned, vfdate, wincx);
+ }
+ } else {
+ /* not enough memory to add the CRL to the cache. mark it
+ invalid so we will try again . */
+ cache->invalid |= CRL_CACHE_LAST_FETCH_FAILED;
+ }
+ if (SECFailure == rv) {
+ break;
+ }
+ }
+ }
+
+ if (head.arena) {
+ CERTCrlNode* crlNode = NULL;
+ /* clean up the CRL list in case we got a partial one
+ during a failed fetch */
+ for (crlNode = head.first; crlNode; crlNode = crlNode->next) {
+ if (crlNode->crl) {
+ SEC_DestroyCrl(crlNode->crl); /* free the CRL. Either it got
+ added to the cache and the refcount got bumped, or not, and
+ thus we need to free its RAM */
+ }
+ }
+ PORT_FreeArena(head.arena, PR_FALSE); /* destroy CRL list */
+ }
+
+ return rv;
+}
+
+static SECStatus
+CachedCrl_GetEntry(CachedCrl* crl, const SECItem* sn, CERTCrlEntry** returned)
+{
+ CERTCrlEntry* acrlEntry;
+
+ PORT_Assert(crl);
+ PORT_Assert(crl->entries);
+ PORT_Assert(sn);
+ PORT_Assert(returned);
+ if (!crl || !sn || !returned || !crl->entries) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ acrlEntry = PL_HashTableLookup(crl->entries, (void*)sn);
+ if (acrlEntry) {
+ *returned = acrlEntry;
+ } else {
+ *returned = NULL;
+ }
+ return SECSuccess;
+}
+
+/* check if a particular SN is in the CRL cache and return its entry */
+dpcacheStatus
+DPCache_Lookup(CRLDPCache* cache, const SECItem* sn, CERTCrlEntry** returned)
+{
+ SECStatus rv;
+ if (!cache || !sn || !returned) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ /* no cache or SN to look up, or no way to return entry */
+ return dpcacheCallerError;
+ }
+ *returned = NULL;
+ if (0 != cache->invalid) {
+ /* the cache contains a bad CRL, or there was a CRL fetching error. */
+ PORT_SetError(SEC_ERROR_CRL_INVALID);
+ return dpcacheInvalidCacheError;
+ }
+ if (!cache->selected) {
+ /* no CRL means no entry to return. This is OK, except for
+ * NIST policy */
+ return dpcacheEmpty;
+ }
+ rv = CachedCrl_GetEntry(cache->selected, sn, returned);
+ if (SECSuccess != rv) {
+ return dpcacheLookupError;
+ } else {
+ if (*returned) {
+ return dpcacheFoundEntry;
+ } else {
+ return dpcacheNoEntry;
+ }
+ }
+}
+
+#if defined(DPC_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
+
+/* with a global lock, we are always locked for read before we need write
+ access, so do nothing */
+
+#define DPCache_LockWrite() \
+ { \
+ }
+
+#define DPCache_UnlockWrite() \
+ { \
+ }
+
+#endif
+
+/* update the content of the CRL cache, including fetching of CRLs, and
+ reprocessing with specified issuer and date . We are always holding
+ either the read or write lock on DPCache upon entry. */
+static SECStatus
+DPCache_GetUpToDate(CRLDPCache* cache, CERTCertificate* issuer,
+ PRBool readlocked, PRTime vfdate, void* wincx)
+{
+ /* 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 forcedrefresh = PR_FALSE;
+ PRBool dirty = PR_FALSE; /* whether something was changed in the
+ cache state during this update cycle */
+ PRBool hastokenCRLs = PR_FALSE;
+ PRTime now = 0;
+ PRTime lastfetch = 0;
+ PRBool mustunlock = PR_FALSE;
+
+ if (!cache) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ /* first, make sure we have obtained all the CRLs we need.
+ We do an expensive token fetch in the following cases :
+ 1) cache is empty because no fetch was ever performed yet
+ 2) cache is explicitly set to refresh state
+ 3) cache is in invalid state because last fetch failed
+ 4) cache contains no token CRLs, and it's been more than one minute
+ since the last fetch
+ 5) cache contains token CRLs, and it's been more than 10 minutes since
+ the last fetch
+ */
+ forcedrefresh = cache->refresh;
+ lastfetch = cache->lastfetch;
+ if (PR_TRUE != forcedrefresh &&
+ (!(cache->invalid & CRL_CACHE_LAST_FETCH_FAILED))) {
+ now = PR_Now();
+ hastokenCRLs = DPCache_HasTokenCRLs(cache);
+ }
+ if ((0 == lastfetch) ||
+
+ (PR_TRUE == forcedrefresh) ||
+
+ (cache->invalid & CRL_CACHE_LAST_FETCH_FAILED) ||
+
+ ((PR_FALSE == hastokenCRLs) &&
+ ((now - cache->lastfetch > CRLCache_Empty_TokenFetch_Interval) ||
+ (now < cache->lastfetch))) ||
+
+ ((PR_TRUE == hastokenCRLs) &&
+ ((now - cache->lastfetch > CRLCache_TokenRefetch_Interval) ||
+ (now < cache->lastfetch)))) {
+ /* the cache needs to be refreshed, and/or we had zero CRL for this
+ DP. Try to get one from PKCS#11 tokens */
+ DPCache_LockWrite();
+ /* check if another thread updated before us, and skip update if so */
+ if (lastfetch == cache->lastfetch) {
+ /* we are the first */
+ rv = DPCache_FetchFromTokens(cache, vfdate, wincx);
+ if (PR_TRUE == cache->refresh) {
+ cache->refresh = PR_FALSE; /* clear refresh state */
+ }
+ dirty = PR_TRUE;
+ cache->lastfetch = PR_Now();
+ }
+ DPCache_UnlockWrite();
+ }
+
+ /* now, make sure we have no extraneous CRLs (deleted token objects)
+ we'll do this inexpensive existence check either
+ 1) if there was a token object fetch
+ 2) every minute */
+ if ((PR_TRUE != dirty) && (!now)) {
+ now = PR_Now();
+ }
+ if ((PR_TRUE == dirty) ||
+ ((now - cache->lastcheck > CRLCache_ExistenceCheck_Interval) ||
+ (now < cache->lastcheck))) {
+ PRTime lastcheck = cache->lastcheck;
+ mustunlock = PR_FALSE;
+ /* check if all CRLs still exist */
+ for (i = 0; (i < cache->ncrls); i++) {
+ CachedCrl* savcrl = cache->crls[i];
+ if ((!savcrl) || (savcrl && CRL_OriginToken != savcrl->origin)) {
+ /* we only want to check token CRLs */
+ continue;
+ }
+ if ((PR_TRUE != TokenCRLStillExists(savcrl->crl))) {
+
+ /* this CRL is gone */
+ if (PR_TRUE != mustunlock) {
+ DPCache_LockWrite();
+ mustunlock = PR_TRUE;
+ }
+ /* first, we need to check if another thread did an update
+ before we did */
+ if (lastcheck == cache->lastcheck) {
+ /* the CRL is gone. And we are the one to do the update */
+ DPCache_RemoveCRL(cache, i);
+ dirty = PR_TRUE;
+ }
+ /* stay locked here intentionally so we do all the other
+ updates in this thread for the remaining CRLs */
+ }
+ }
+ if (PR_TRUE == mustunlock) {
+ cache->lastcheck = PR_Now();
+ DPCache_UnlockWrite();
+ mustunlock = PR_FALSE;
+ }
+ }
+
+ /* add issuer certificate if it was previously unavailable */
+ if (issuer && (NULL == cache->issuerDERCert) &&
+ (SECSuccess == CERT_CheckCertUsage(issuer, KU_CRL_SIGN))) {
+ /* if we didn't have a valid issuer cert yet, but we do now. add it */
+ DPCache_LockWrite();
+ if (!cache->issuerDERCert) {
+ dirty = PR_TRUE;
+ cache->dbHandle = issuer->dbhandle;
+ cache->issuerDERCert = SECITEM_DupItem(&issuer->derCert);
+ }
+ DPCache_UnlockWrite();
+ }
+
+ /* verify CRLs that couldn't be checked when inserted into the cache
+ because the issuer cert or a verification date was unavailable.
+ These are CRLs that were inserted into the cache through
+ SEC_FindCrlByName, or through manual insertion, rather than through a
+ certificate verification (CERT_CheckCRL) */
+
+ if (cache->issuerDERCert && vfdate) {
+ mustunlock = PR_FALSE;
+ /* re-process all unverified CRLs */
+ for (i = 0; i < cache->ncrls; i++) {
+ CachedCrl* savcrl = cache->crls[i];
+ if (!savcrl) {
+ continue;
+ }
+ if (PR_TRUE != savcrl->sigChecked) {
+ if (!mustunlock) {
+ DPCache_LockWrite();
+ mustunlock = PR_TRUE;
+ }
+ /* 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. Make sure first that the CRL is still
+ in the array at the same position */
+ if ((i < cache->ncrls) && (savcrl == cache->crls[i]) &&
+ (PR_TRUE != savcrl->sigChecked)) {
+ /* the CRL is still there, unverified. Do it */
+ CachedCrl_Verify(cache, savcrl, vfdate, wincx);
+ dirty = PR_TRUE;
+ }
+ /* stay locked here intentionally so we do all the other
+ updates in this thread for the remaining CRLs */
+ }
+ if (mustunlock && !dirty) {
+ DPCache_UnlockWrite();
+ mustunlock = PR_FALSE;
+ }
+ }
+ }
+
+ if (dirty || cache->mustchoose) {
+ /* changes to the content of the CRL cache necessitate examining all
+ CRLs for selection of the most appropriate one to cache */
+ if (!mustunlock) {
+ DPCache_LockWrite();
+ mustunlock = PR_TRUE;
+ }
+ DPCache_SelectCRL(cache);
+ cache->mustchoose = PR_FALSE;
+ }
+ if (mustunlock)
+ DPCache_UnlockWrite();
+
+ return rv;
+}
+
+/* callback for qsort to sort by thisUpdate */
+static int
+SortCRLsByThisUpdate(const void* arg1, const void* arg2)
+{
+ PRTime timea, timeb;
+ SECStatus rv = SECSuccess;
+ CachedCrl *a, *b;
+
+ a = *(CachedCrl**)arg1;
+ b = *(CachedCrl**)arg2;
+
+ if (!a || !b) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ }
+
+ if (SECSuccess == rv) {
+ rv = DER_DecodeTimeChoice(&timea, &a->crl->crl.lastUpdate);
+ }
+ if (SECSuccess == rv) {
+ rv = DER_DecodeTimeChoice(&timeb, &b->crl->crl.lastUpdate);
+ }
+ if (SECSuccess == rv) {
+ if (timea > timeb) {
+ return 1; /* a is better than b */
+ }
+ if (timea < timeb) {
+ return -1; /* a is not as good as b */
+ }
+ }
+
+ /* if they are equal, or if all else fails, use pointer differences */
+ PORT_Assert(a != b); /* they should never be equal */
+ return a > b ? 1 : -1;
+}
+
+/* callback for qsort to sort a set of disparate CRLs, some of which are
+ invalid DER or failed signature check.
+
+ Validated CRLs are differentiated by thisUpdate .
+ Validated CRLs are preferred over non-validated CRLs .
+ Proper DER CRLs are preferred over non-DER data .
+*/
+static int
+SortImperfectCRLs(const void* arg1, const void* arg2)
+{
+ CachedCrl *a, *b;
+
+ a = *(CachedCrl**)arg1;
+ b = *(CachedCrl**)arg2;
+
+ if (!a || !b) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ PORT_Assert(0);
+ } else {
+ PRBool aDecoded = PR_FALSE, bDecoded = PR_FALSE;
+ if ((PR_TRUE == a->sigValid) && (PR_TRUE == b->sigValid)) {
+ /* both CRLs have been validated, choose the latest one */
+ return SortCRLsByThisUpdate(arg1, arg2);
+ }
+ if (PR_TRUE == a->sigValid) {
+ return 1; /* a is greater than b */
+ }
+ if (PR_TRUE == b->sigValid) {
+ return -1; /* a is not as good as b */
+ }
+ aDecoded = GetOpaqueCRLFields(a->crl)->decodingError;
+ bDecoded = GetOpaqueCRLFields(b->crl)->decodingError;
+ /* neither CRL had its signature check pass */
+ if ((PR_FALSE == aDecoded) && (PR_FALSE == bDecoded)) {
+ /* both CRLs are proper DER, choose the latest one */
+ return SortCRLsByThisUpdate(arg1, arg2);
+ }
+ if (PR_FALSE == aDecoded) {
+ return 1; /* a is better than b */
+ }
+ if (PR_FALSE == bDecoded) {
+ return -1; /* a is not as good as b */
+ }
+ /* both are invalid DER. sigh. */
+ }
+ /* if they are equal, or if all else fails, use pointer differences */
+ PORT_Assert(a != b); /* they should never be equal */
+ return a > b ? 1 : -1;
+}
+
+/* Pick best CRL to use . needs write access */
+static SECStatus
+DPCache_SelectCRL(CRLDPCache* cache)
+{
+ PRUint32 i;
+ PRBool valid = PR_TRUE;
+ CachedCrl* selected = NULL;
+
+ PORT_Assert(cache);
+ if (!cache) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ /* if any invalid CRL is present, then the CRL cache is
+ considered invalid, for security reasons */
+ for (i = 0; i < cache->ncrls; i++) {
+ if (!cache->crls[i] || !cache->crls[i]->sigChecked ||
+ !cache->crls[i]->sigValid) {
+ valid = PR_FALSE;
+ break;
+ }
+ }
+ if (PR_TRUE == valid) {
+ /* all CRLs are valid, clear this error */
+ cache->invalid &= (~CRL_CACHE_INVALID_CRLS);
+ } else {
+ /* some CRLs are invalid, set this error */
+ cache->invalid |= CRL_CACHE_INVALID_CRLS;
+ }
+
+ if (cache->invalid) {
+ /* cache is in an invalid state, so reset it */
+ if (cache->selected) {
+ cache->selected = NULL;
+ }
+ /* also sort the CRLs imperfectly */
+ qsort(cache->crls, cache->ncrls, sizeof(CachedCrl*), SortImperfectCRLs);
+ return SECSuccess;
+ }
+
+ if (cache->ncrls) {
+ /* all CRLs are good, sort them by thisUpdate */
+ qsort(cache->crls, cache->ncrls, sizeof(CachedCrl*), SortCRLsByThisUpdate);
+
+ /* pick the newest CRL */
+ selected = cache->crls[cache->ncrls - 1];
+
+ /* and populate the cache */
+ if (SECSuccess != CachedCrl_Populate(selected)) {
+ return SECFailure;
+ }
+ }
+
+ cache->selected = selected;
+
+ return SECSuccess;
+}
+
+/* initialize a DPCache object */
+static SECStatus
+DPCache_Create(CRLDPCache** returned, CERTCertificate* issuer,
+ const SECItem* subject, SECItem* dp)
+{
+ CRLDPCache* cache = NULL;
+ PORT_Assert(returned);
+ /* issuer and dp are allowed to be NULL */
+ if (!returned || !subject) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ *returned = NULL;
+ cache = PORT_ZAlloc(sizeof(CRLDPCache));
+ if (!cache) {
+ return SECFailure;
+ }
+#ifdef DPC_RWLOCK
+ cache->lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
+#else
+ cache->lock = PR_NewLock();
+#endif
+ if (!cache->lock) {
+ PORT_Free(cache);
+ return SECFailure;
+ }
+ if (issuer) {
+ cache->dbHandle = issuer->dbhandle;
+ cache->issuerDERCert = SECITEM_DupItem(&issuer->derCert);
+ }
+ cache->distributionPoint = SECITEM_DupItem(dp);
+ cache->subject = SECITEM_DupItem(subject);
+ cache->lastfetch = 0;
+ cache->lastcheck = 0;
+ *returned = cache;
+ return SECSuccess;
+}
+
+/* create an issuer cache object (per CA subject ) */
+static SECStatus
+IssuerCache_Create(CRLIssuerCache** returned, CERTCertificate* issuer,
+ const SECItem* subject, const SECItem* dp)
+{
+ SECStatus rv = SECSuccess;
+ CRLIssuerCache* cache = NULL;
+ PORT_Assert(returned);
+ PORT_Assert(subject);
+ /* issuer and dp are allowed to be NULL */
+ if (!returned || !subject) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ *returned = NULL;
+ cache = (CRLIssuerCache*)PORT_ZAlloc(sizeof(CRLIssuerCache));
+ if (!cache) {
+ return SECFailure;
+ }
+ cache->subject = SECITEM_DupItem(subject);
+#ifdef 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) {
+ PORT_Assert(SECSuccess == IssuerCache_Destroy(cache));
+ return SECFailure;
+ }
+ *returned = cache;
+ return SECSuccess;
+}
+
+/* add a DPCache to the issuer cache */
+static SECStatus
+IssuerCache_AddDP(CRLIssuerCache* cache, CERTCertificate* issuer,
+ const SECItem* subject, const SECItem* dp,
+ CRLDPCache** newdpc)
+{
+ /* now create the required DP cache object */
+ if (!cache || !subject || !newdpc) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (!dp) {
+ /* default distribution point */
+ SECStatus rv = DPCache_Create(&cache->dpp, issuer, subject, NULL);
+ if (SECSuccess == rv) {
+ *newdpc = cache->dpp;
+ return SECSuccess;
+ }
+ } else {
+ /* we should never hit this until we support multiple DPs */
+ PORT_Assert(dp);
+ /* XCRL allocate a new distribution point cache object, initialize it,
+ and add it to the hash table of DPs */
+ }
+ return SECFailure;
+}
+
+/* add an IssuerCache to the global hash table of issuers */
+static SECStatus
+CRLCache_AddIssuer(CRLIssuerCache* issuer)
+{
+ PORT_Assert(issuer);
+ PORT_Assert(crlcache.issuers);
+ if (!issuer || !crlcache.issuers) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (NULL == PL_HashTableAdd(crlcache.issuers, (void*)issuer->subject,
+ (void*)issuer)) {
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/* retrieve the issuer cache object for a given issuer subject */
+static SECStatus
+CRLCache_GetIssuerCache(CRLCache* cache, const 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) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ }
+
+ if (SECSuccess == rv) {
+ *returned = (CRLIssuerCache*)PL_HashTableLookup(crlcache.issuers,
+ (void*)subject);
+ }
+
+ return rv;
+}
+
+/* retrieve the full CRL object that best matches the content of a DPCache */
+static CERTSignedCrl*
+GetBestCRL(CRLDPCache* cache, PRBool entries)
+{
+ CachedCrl* acrl = NULL;
+
+ PORT_Assert(cache);
+ if (!cache) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ if (0 == cache->ncrls) {
+ /* empty cache*/
+ PORT_SetError(SEC_ERROR_CRL_NOT_FOUND);
+ return NULL;
+ }
+
+ /* if we have a valid full CRL selected, return it */
+ if (cache->selected) {
+ return SEC_DupCrl(cache->selected->crl);
+ }
+
+ /* otherwise, use latest valid DER CRL */
+ acrl = cache->crls[cache->ncrls - 1];
+
+ if (acrl && (PR_FALSE == GetOpaqueCRLFields(acrl->crl)->decodingError)) {
+ SECStatus rv = SECSuccess;
+ if (PR_TRUE == entries) {
+ rv = CERT_CompleteCRLDecodeEntries(acrl->crl);
+ }
+ if (SECSuccess == rv) {
+ return SEC_DupCrl(acrl->crl);
+ }
+ }
+
+ PORT_SetError(SEC_ERROR_CRL_NOT_FOUND);
+ return NULL;
+}
+
+/* get a particular DPCache object from an IssuerCache */
+static CRLDPCache*
+IssuerCache_GetDPCache(CRLIssuerCache* cache, const 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) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+#ifdef XCRL
+ NSSRWLock_LockRead(cache->lock);
+#endif
+ dpp = cache->dpp;
+#ifdef XCRL
+ NSSRWLock_UnlockRead(cache->lock);
+#endif
+ return dpp;
+}
+
+/* get a DPCache object for the given issuer subject and dp
+ Automatically creates the cache object if it doesn't exist yet.
+ */
+SECStatus
+AcquireDPCache(CERTCertificate* issuer, const SECItem* subject,
+ const SECItem* dp, PRTime t, void* wincx, CRLDPCache** dpcache,
+ PRBool* writeLocked)
+{
+ SECStatus rv = SECSuccess;
+ CRLIssuerCache* issuercache = NULL;
+#ifdef GLOBAL_RWLOCK
+ PRBool globalwrite = PR_FALSE;
+#endif
+ PORT_Assert(crlcache.lock);
+ if (!crlcache.lock) {
+ /* CRL cache is not initialized */
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+#ifdef GLOBAL_RWLOCK
+ NSSRWLock_LockRead(crlcache.lock);
+#else
+ PR_Lock(crlcache.lock);
+#endif
+ rv = CRLCache_GetIssuerCache(&crlcache, subject, &issuercache);
+ if (SECSuccess != rv) {
+#ifdef GLOBAL_RWLOCK
+ NSSRWLock_UnlockRead(crlcache.lock);
+#else
+ PR_Unlock(crlcache.lock);
+#endif
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ 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. */
+
+ 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 DPC_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 */
+#ifdef GLOBAL_RWLOCK
+ CRLIssuerCache* existing = NULL;
+ NSSRWLock_UnlockRead(crlcache.lock);
+ /* when using a r/w lock for the global cache, check if the issuer
+ already exists before adding to the hash table */
+ NSSRWLock_LockWrite(crlcache.lock);
+ globalwrite = PR_TRUE;
+ rv = CRLCache_GetIssuerCache(&crlcache, subject, &existing);
+ if (!existing) {
+#endif
+ rv = CRLCache_AddIssuer(issuercache);
+ if (SECSuccess != rv) {
+ /* failure */
+ rv = SECFailure;
+ }
+#ifdef GLOBAL_RWLOCK
+ } else {
+ /* somebody else updated before we did */
+ IssuerCache_Destroy(issuercache); /* destroy the new object */
+ issuercache = existing; /* use the existing one */
+ *dpcache = IssuerCache_GetDPCache(issuercache, dp);
+ }
+#endif
+ }
+
+/* now unlock the global cache. We only want to lock the issuer hash
+ table addition. Holding it longer would hurt scalability */
+#ifdef GLOBAL_RWLOCK
+ if (PR_TRUE == globalwrite) {
+ NSSRWLock_UnlockWrite(crlcache.lock);
+ globalwrite = PR_FALSE;
+ } else {
+ NSSRWLock_UnlockRead(crlcache.lock);
+ }
+#else
+ PR_Unlock(crlcache.lock);
+#endif
+
+ /* if there was a failure adding an issuer cache object, destroy it */
+ if (SECSuccess != rv && issuercache) {
+ if (PR_TRUE == *writeLocked) {
+#ifdef DPC_RWLOCK
+ NSSRWLock_UnlockWrite((*dpcache)->lock);
+#else
+ PR_Unlock((*dpcache)->lock);
+#endif
+ }
+ IssuerCache_Destroy(issuercache);
+ issuercache = NULL;
+ }
+
+ if (SECSuccess != rv) {
+ return SECFailure;
+ }
+ } else {
+#ifdef GLOBAL_RWLOCK
+ NSSRWLock_UnlockRead(crlcache.lock);
+#else
+ PR_Unlock(crlcache.lock);
+#endif
+ *dpcache = IssuerCache_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 DPC_RWLOCK
+ NSSRWLock_LockRead((*dpcache)->lock);
+#else
+ PR_Lock((*dpcache)->lock);
+#endif
+ }
+
+ if (SECSuccess == rv) {
+ /* currently there is always one and only one DPCache per issuer */
+ PORT_Assert(*dpcache);
+ if (*dpcache) {
+ /* make sure the DP cache is up to date before using it */
+ rv = DPCache_GetUpToDate(*dpcache, issuer, PR_FALSE == *writeLocked,
+ t, wincx);
+ } else {
+ rv = SECFailure;
+ }
+ }
+ return rv;
+}
+
+/* unlock access to the DPCache */
+void
+ReleaseDPCache(CRLDPCache* dpcache, PRBool writeLocked)
+{
+ if (!dpcache) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return;
+ }
+#ifdef DPC_RWLOCK
+ if (PR_TRUE == writeLocked) {
+ NSSRWLock_UnlockWrite(dpcache->lock);
+ } else {
+ NSSRWLock_UnlockRead(dpcache->lock);
+ }
+#else
+ PR_Unlock(dpcache->lock);
+#endif
+}
+
+SECStatus
+cert_CheckCertRevocationStatus(CERTCertificate* cert, CERTCertificate* issuer,
+ const SECItem* dp, PRTime t, void* wincx,
+ CERTRevocationStatus* revStatus,
+ CERTCRLEntryReasonCode* revReason)
+{
+ PRBool lockedwrite = PR_FALSE;
+ SECStatus rv = SECSuccess;
+ CRLDPCache* dpcache = NULL;
+ CERTRevocationStatus status = certRevocationStatusRevoked;
+ CERTCRLEntryReasonCode reason = crlEntryReasonUnspecified;
+ CERTCrlEntry* entry = NULL;
+ dpcacheStatus ds;
+
+ if (!cert || !issuer) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ if (revStatus) {
+ *revStatus = status;
+ }
+ if (revReason) {
+ *revReason = reason;
+ }
+
+ if (t &&
+ secCertTimeValid != CERT_CheckCertValidTimes(issuer, t, PR_FALSE)) {
+ /* we won't be able to check the CRL's signature if the issuer cert
+ is expired as of the time we are verifying. This may cause a valid
+ CRL to be cached as bad. short-circuit to avoid this case. */
+ PORT_SetError(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE);
+ return SECFailure;
+ }
+
+ rv = AcquireDPCache(issuer, &issuer->derSubject, dp, t, wincx, &dpcache,
+ &lockedwrite);
+ PORT_Assert(SECSuccess == rv);
+ if (SECSuccess != rv) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ /* now look up the certificate SN in the DP cache's CRL */
+ ds = DPCache_Lookup(dpcache, &cert->serialNumber, &entry);
+ switch (ds) {
+ case dpcacheFoundEntry:
+ PORT_Assert(entry);
+ /* check the time if we have one */
+ if (entry->revocationDate.data && entry->revocationDate.len) {
+ PRTime revocationDate = 0;
+ if (SECSuccess ==
+ DER_DecodeTimeChoice(&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 {
+ status = certRevocationStatusValid;
+ }
+ } else {
+ /* invalid revocation date, consider the certificate
+ permanently revoked */
+ rv = SECFailure;
+ }
+ } else {
+ /* no revocation date, certificate is permanently revoked */
+ rv = SECFailure;
+ }
+ if (SECFailure == rv) {
+ (void)CERT_FindCRLEntryReasonExten(entry, &reason);
+ PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
+ }
+ break;
+
+ case dpcacheEmpty:
+ /* useful for NIST policy */
+ status = certRevocationStatusUnknown;
+ break;
+
+ case dpcacheNoEntry:
+ status = certRevocationStatusValid;
+ break;
+
+ case dpcacheInvalidCacheError:
+ /* treat it as unknown and let the caller decide based on
+ the policy */
+ status = certRevocationStatusUnknown;
+ break;
+
+ default:
+ /* leave status as revoked */
+ break;
+ }
+
+ ReleaseDPCache(dpcache, lockedwrite);
+ if (revStatus) {
+ *revStatus = status;
+ }
+ if (revReason) {
+ *revReason = reason;
+ }
+ return rv;
+}
+
+/* check CRL revocation status of given certificate and issuer */
+SECStatus
+CERT_CheckCRL(CERTCertificate* cert, CERTCertificate* issuer, const SECItem* dp,
+ PRTime t, void* wincx)
+{
+ return cert_CheckCertRevocationStatus(cert, issuer, dp, t, wincx, NULL,
+ NULL);
+}
+
+/* retrieve full CRL object that best matches the cache status */
+CERTSignedCrl*
+SEC_FindCrlByName(CERTCertDBHandle* handle, SECItem* crlKey, int type)
+{
+ CERTSignedCrl* acrl = NULL;
+ CRLDPCache* dpcache = NULL;
+ SECStatus rv = SECSuccess;
+ PRBool writeLocked = PR_FALSE;
+
+ if (!crlKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ rv = AcquireDPCache(NULL, crlKey, NULL, 0, NULL, &dpcache, &writeLocked);
+ if (SECSuccess == rv) {
+ acrl = GetBestCRL(dpcache, PR_TRUE); /* decode entries, because
+ SEC_FindCrlByName always returned fully decoded CRLs in the past */
+ ReleaseDPCache(dpcache, writeLocked);
+ }
+ return acrl;
+}
+
+/* invalidate the CRL cache for a given issuer, which forces a refetch of
+ CRL objects from PKCS#11 tokens */
+void
+CERT_CRLCacheRefreshIssuer(CERTCertDBHandle* dbhandle, SECItem* crlKey)
+{
+ CRLDPCache* cache = NULL;
+ SECStatus rv = SECSuccess;
+ PRBool writeLocked = PR_FALSE;
+ PRBool readlocked;
+
+ (void)dbhandle; /* silence compiler warnings */
+
+ /* XCRL we will need to refresh all the DPs of the issuer in the future,
+ not just the default one */
+ rv = AcquireDPCache(NULL, crlKey, NULL, 0, NULL, &cache, &writeLocked);
+ if (SECSuccess != rv) {
+ return;
+ }
+ /* we need to invalidate the DPCache here */
+ readlocked = (writeLocked == PR_TRUE ? PR_FALSE : PR_TRUE);
+ DPCache_LockWrite();
+ cache->refresh = PR_TRUE;
+ DPCache_UnlockWrite();
+ ReleaseDPCache(cache, writeLocked);
+ return;
+}
+
+/* add the specified RAM CRL object to the cache */
+SECStatus
+CERT_CacheCRL(CERTCertDBHandle* dbhandle, SECItem* newdercrl)
+{
+ CRLDPCache* cache = NULL;
+ SECStatus rv = SECSuccess;
+ PRBool writeLocked = PR_FALSE;
+ PRBool readlocked;
+ CachedCrl* returned = NULL;
+ PRBool added = PR_FALSE;
+ CERTSignedCrl* newcrl = NULL;
+ int realerror = 0;
+
+ if (!dbhandle || !newdercrl) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* first decode the DER CRL to make sure it's OK */
+ newcrl = CERT_DecodeDERCrlWithFlags(NULL, newdercrl, SEC_CRL_TYPE,
+ CRL_DECODE_DONT_COPY_DER |
+ CRL_DECODE_SKIP_ENTRIES);
+
+ if (!newcrl) {
+ return SECFailure;
+ }
+
+ /* XXX check if it has IDP extension. If so, do not proceed and set error */
+
+ rv = AcquireDPCache(NULL, &newcrl->crl.derName, NULL, 0, NULL, &cache,
+ &writeLocked);
+ if (SECSuccess == rv) {
+ readlocked = (writeLocked == PR_TRUE ? PR_FALSE : PR_TRUE);
+
+ rv = CachedCrl_Create(&returned, newcrl, CRL_OriginExplicit);
+ if (SECSuccess == rv && returned) {
+ DPCache_LockWrite();
+ rv = DPCache_AddCRL(cache, returned, &added);
+ if (PR_TRUE != added) {
+ realerror = PORT_GetError();
+ CachedCrl_Destroy(returned);
+ returned = NULL;
+ }
+ DPCache_UnlockWrite();
+ }
+
+ ReleaseDPCache(cache, writeLocked);
+
+ if (!added) {
+ rv = SECFailure;
+ }
+ }
+ SEC_DestroyCrl(newcrl); /* free the CRL. Either it got added to the cache
+ and the refcount got bumped, or not, and thus we need to free its
+ RAM */
+ if (realerror) {
+ PORT_SetError(realerror);
+ }
+ return rv;
+}
+
+/* remove the specified RAM CRL object from the cache */
+SECStatus
+CERT_UncacheCRL(CERTCertDBHandle* dbhandle, SECItem* olddercrl)
+{
+ CRLDPCache* cache = NULL;
+ SECStatus rv = SECSuccess;
+ PRBool writeLocked = PR_FALSE;
+ PRBool readlocked;
+ PRBool removed = PR_FALSE;
+ PRUint32 i;
+ CERTSignedCrl* oldcrl = NULL;
+
+ if (!dbhandle || !olddercrl) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* first decode the DER CRL to make sure it's OK */
+ oldcrl = CERT_DecodeDERCrlWithFlags(NULL, olddercrl, SEC_CRL_TYPE,
+ CRL_DECODE_DONT_COPY_DER |
+ CRL_DECODE_SKIP_ENTRIES);
+
+ if (!oldcrl) {
+ /* if this DER CRL can't decode, it can't be in the cache */
+ return SECFailure;
+ }
+
+ rv = AcquireDPCache(NULL, &oldcrl->crl.derName, NULL, 0, NULL, &cache,
+ &writeLocked);
+ if (SECSuccess == rv) {
+ CachedCrl* returned = NULL;
+
+ readlocked = (writeLocked == PR_TRUE ? PR_FALSE : PR_TRUE);
+
+ rv = CachedCrl_Create(&returned, oldcrl, CRL_OriginExplicit);
+ if (SECSuccess == rv && returned) {
+ DPCache_LockWrite();
+ for (i = 0; i < cache->ncrls; i++) {
+ PRBool dupe = PR_FALSE, updated = PR_FALSE;
+ rv = CachedCrl_Compare(returned, cache->crls[i], &dupe,
+ &updated);
+ if (SECSuccess != rv) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ break;
+ }
+ if (PR_TRUE == dupe) {
+ rv = DPCache_RemoveCRL(cache, i); /* got a match */
+ if (SECSuccess == rv) {
+ cache->mustchoose = PR_TRUE;
+ removed = PR_TRUE;
+ }
+ break;
+ }
+ }
+
+ DPCache_UnlockWrite();
+
+ if (SECSuccess != CachedCrl_Destroy(returned)) {
+ rv = SECFailure;
+ }
+ }
+
+ ReleaseDPCache(cache, writeLocked);
+ }
+ if (SECSuccess != SEC_DestroyCrl(oldcrl)) {
+ /* need to do this because object is refcounted */
+ rv = SECFailure;
+ }
+ if (SECSuccess == rv && PR_TRUE != removed) {
+ PORT_SetError(SEC_ERROR_CRL_NOT_FOUND);
+ }
+ return rv;
+}
+
+SECStatus
+cert_AcquireNamedCRLCache(NamedCRLCache** returned)
+{
+ PORT_Assert(returned);
+ if (!namedCRLCache.lock) {
+ PORT_Assert(0);
+ return SECFailure;
+ }
+ PR_Lock(namedCRLCache.lock);
+ *returned = &namedCRLCache;
+ return SECSuccess;
+}
+
+/* This must be called only while cache is acquired, and the entry is only
+ * valid until cache is released.
+ */
+SECStatus
+cert_FindCRLByGeneralName(NamedCRLCache* ncc, const SECItem* canonicalizedName,
+ NamedCRLCacheEntry** retEntry)
+{
+ if (!ncc || !canonicalizedName || !retEntry) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ *retEntry = (NamedCRLCacheEntry*)PL_HashTableLookup(
+ namedCRLCache.entries, (void*)canonicalizedName);
+ return SECSuccess;
+}
+
+SECStatus
+cert_ReleaseNamedCRLCache(NamedCRLCache* ncc)
+{
+ if (!ncc) {
+ return SECFailure;
+ }
+ if (!ncc->lock) {
+ PORT_Assert(0);
+ return SECFailure;
+ }
+ PR_Unlock(namedCRLCache.lock);
+ return SECSuccess;
+}
+
+/* creates new named cache entry from CRL, and tries to add it to CRL cache */
+static SECStatus
+addCRLToCache(CERTCertDBHandle* dbhandle, SECItem* crl,
+ const SECItem* canonicalizedName, NamedCRLCacheEntry** newEntry)
+{
+ SECStatus rv = SECSuccess;
+ NamedCRLCacheEntry* entry = NULL;
+
+ /* create new named entry */
+ if (SECSuccess != NamedCRLCacheEntry_Create(newEntry) || !*newEntry) {
+ /* no need to keep unused CRL around */
+ SECITEM_ZfreeItem(crl, PR_TRUE);
+ return SECFailure;
+ }
+ entry = *newEntry;
+ entry->crl = crl; /* named CRL cache owns DER */
+ entry->lastAttemptTime = PR_Now();
+ entry->canonicalizedName = SECITEM_DupItem(canonicalizedName);
+ if (!entry->canonicalizedName) {
+ rv = NamedCRLCacheEntry_Destroy(entry); /* destroys CRL too */
+ PORT_Assert(SECSuccess == rv);
+ return SECFailure;
+ }
+ /* now, attempt to insert CRL into CRL cache */
+ if (SECSuccess == CERT_CacheCRL(dbhandle, entry->crl)) {
+ entry->inCRLCache = PR_TRUE;
+ entry->successfulInsertionTime = entry->lastAttemptTime;
+ } else {
+ switch (PR_GetError()) {
+ case SEC_ERROR_CRL_ALREADY_EXISTS:
+ entry->dupe = PR_TRUE;
+ break;
+
+ case SEC_ERROR_BAD_DER:
+ entry->badDER = PR_TRUE;
+ break;
+
+ /* all other reasons */
+ default:
+ entry->unsupported = PR_TRUE;
+ break;
+ }
+ rv = SECFailure;
+ /* no need to keep unused CRL around */
+ SECITEM_ZfreeItem(entry->crl, PR_TRUE);
+ entry->crl = NULL;
+ }
+ return rv;
+}
+
+/* take ownership of CRL, and insert it into the named CRL cache
+ * and indexed CRL cache
+ */
+SECStatus
+cert_CacheCRLByGeneralName(CERTCertDBHandle* dbhandle, SECItem* crl,
+ const SECItem* canonicalizedName)
+{
+ NamedCRLCacheEntry *oldEntry, *newEntry = NULL;
+ NamedCRLCache* ncc = NULL;
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(namedCRLCache.lock);
+ PORT_Assert(namedCRLCache.entries);
+
+ if (!crl || !canonicalizedName) {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ rv = cert_AcquireNamedCRLCache(&ncc);
+ PORT_Assert(SECSuccess == rv);
+ if (SECSuccess != rv) {
+ SECITEM_ZfreeItem(crl, PR_TRUE);
+ return SECFailure;
+ }
+ rv = cert_FindCRLByGeneralName(ncc, canonicalizedName, &oldEntry);
+ PORT_Assert(SECSuccess == rv);
+ if (SECSuccess != rv) {
+ (void)cert_ReleaseNamedCRLCache(ncc);
+ SECITEM_ZfreeItem(crl, PR_TRUE);
+ return SECFailure;
+ }
+ if (SECSuccess ==
+ addCRLToCache(dbhandle, crl, canonicalizedName, &newEntry)) {
+ if (!oldEntry) {
+ /* add new good entry to the hash table */
+ if (NULL == PL_HashTableAdd(namedCRLCache.entries,
+ (void*)newEntry->canonicalizedName,
+ (void*)newEntry)) {
+ PORT_Assert(0);
+ NamedCRLCacheEntry_Destroy(newEntry);
+ rv = SECFailure;
+ }
+ } else {
+ PRBool removed;
+ /* remove the old CRL from the cache if needed */
+ if (oldEntry->inCRLCache) {
+ rv = CERT_UncacheCRL(dbhandle, oldEntry->crl);
+ PORT_Assert(SECSuccess == rv);
+ }
+ removed = PL_HashTableRemove(namedCRLCache.entries,
+ (void*)oldEntry->canonicalizedName);
+ PORT_Assert(removed);
+ if (!removed) {
+ rv = SECFailure;
+ /* leak old entry since we couldn't remove it from the hash
+ * table */
+ } else {
+ PORT_CheckSuccess(NamedCRLCacheEntry_Destroy(oldEntry));
+ }
+ if (NULL == PL_HashTableAdd(namedCRLCache.entries,
+ (void*)newEntry->canonicalizedName,
+ (void*)newEntry)) {
+ PORT_Assert(0);
+ rv = SECFailure;
+ }
+ }
+ } else {
+ /* error adding new CRL to cache */
+ if (!oldEntry) {
+ /* no old cache entry, use the new one even though it's bad */
+ if (NULL == PL_HashTableAdd(namedCRLCache.entries,
+ (void*)newEntry->canonicalizedName,
+ (void*)newEntry)) {
+ PORT_Assert(0);
+ rv = SECFailure;
+ }
+ } else {
+ if (oldEntry->inCRLCache) {
+ /* previous cache entry was good, keep it and update time */
+ oldEntry->lastAttemptTime = newEntry->lastAttemptTime;
+ /* throw away new bad entry */
+ rv = NamedCRLCacheEntry_Destroy(newEntry);
+ PORT_Assert(SECSuccess == rv);
+ } else {
+ /* previous cache entry was bad, just replace it */
+ PRBool removed = PL_HashTableRemove(
+ namedCRLCache.entries, (void*)oldEntry->canonicalizedName);
+ PORT_Assert(removed);
+ if (!removed) {
+ /* leak old entry since we couldn't remove it from the hash
+ * table */
+ rv = SECFailure;
+ } else {
+ PORT_CheckSuccess(NamedCRLCacheEntry_Destroy(oldEntry));
+ }
+ if (NULL == PL_HashTableAdd(namedCRLCache.entries,
+ (void*)newEntry->canonicalizedName,
+ (void*)newEntry)) {
+ PORT_Assert(0);
+ rv = SECFailure;
+ }
+ }
+ }
+ }
+ PORT_CheckSuccess(cert_ReleaseNamedCRLCache(ncc));
+
+ return rv;
+}
+
+static SECStatus
+CachedCrl_Create(CachedCrl** returned, CERTSignedCrl* crl, CRLOrigin origin)
+{
+ CachedCrl* newcrl = NULL;
+ if (!returned) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ newcrl = PORT_ZAlloc(sizeof(CachedCrl));
+ if (!newcrl) {
+ return SECFailure;
+ }
+ newcrl->crl = SEC_DupCrl(crl);
+ newcrl->origin = origin;
+ *returned = newcrl;
+ return SECSuccess;
+}
+
+/* empty the cache content */
+static SECStatus
+CachedCrl_Depopulate(CachedCrl* crl)
+{
+ if (!crl) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ /* destroy the hash table */
+ if (crl->entries) {
+ PL_HashTableDestroy(crl->entries);
+ crl->entries = NULL;
+ }
+
+ /* free the pre buffer */
+ if (crl->prebuffer) {
+ PreAllocator_Destroy(crl->prebuffer);
+ crl->prebuffer = NULL;
+ }
+ return SECSuccess;
+}
+
+static SECStatus
+CachedCrl_Destroy(CachedCrl* crl)
+{
+ if (!crl) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ CachedCrl_Depopulate(crl);
+ SEC_DestroyCrl(crl->crl);
+ PORT_Free(crl);
+ return SECSuccess;
+}
+
+/* create hash table of CRL entries */
+static SECStatus
+CachedCrl_Populate(CachedCrl* crlobject)
+{
+ SECStatus rv = SECFailure;
+ CERTCrlEntry** crlEntry = NULL;
+ PRUint32 numEntries = 0;
+
+ if (!crlobject) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ /* complete the entry decoding . XXX thread-safety of CRL object */
+ rv = CERT_CompleteCRLDecodeEntries(crlobject->crl);
+ if (SECSuccess != rv) {
+ crlobject->unbuildable = PR_TRUE; /* don't try to build this again */
+ return SECFailure;
+ }
+
+ if (crlobject->entries && crlobject->prebuffer) {
+ /* cache is already built */
+ return SECSuccess;
+ }
+
+ /* build the hash table from the full CRL */
+ /* count CRL entries so we can pre-allocate space for hash table entries */
+ for (crlEntry = crlobject->crl->crl.entries; crlEntry && *crlEntry;
+ crlEntry++) {
+ numEntries++;
+ }
+ crlobject->prebuffer =
+ PreAllocator_Create(numEntries * sizeof(PLHashEntry));
+ PORT_Assert(crlobject->prebuffer);
+ if (!crlobject->prebuffer) {
+ return SECFailure;
+ }
+ /* create a new hash table */
+ crlobject->entries =
+ PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare, PL_CompareValues,
+ &preAllocOps, crlobject->prebuffer);
+ PORT_Assert(crlobject->entries);
+ if (!crlobject->entries) {
+ return SECFailure;
+ }
+ /* add all serial numbers to the hash table */
+ for (crlEntry = crlobject->crl->crl.entries; crlEntry && *crlEntry;
+ crlEntry++) {
+ PL_HashTableAdd(crlobject->entries, &(*crlEntry)->serialNumber,
+ *crlEntry);
+ }
+
+ return SECSuccess;
+}
+
+/* returns true if there are CRLs from PKCS#11 slots */
+static PRBool
+DPCache_HasTokenCRLs(CRLDPCache* cache)
+{
+ PRBool answer = PR_FALSE;
+ PRUint32 i;
+ for (i = 0; i < cache->ncrls; i++) {
+ if (cache->crls[i] && (CRL_OriginToken == cache->crls[i]->origin)) {
+ answer = PR_TRUE;
+ break;
+ }
+ }
+ return answer;
+}
+
+/* are these CRLs the same, as far as the cache is concerned ? */
+/* are these CRLs the same token object but with different DER ?
+ This can happen if the DER CRL got updated in the token, but the PKCS#11
+ object ID did not change. NSS softoken has the unfortunate property to
+ never change the object ID for CRL objects. */
+static SECStatus
+CachedCrl_Compare(CachedCrl* a, CachedCrl* b, PRBool* isDupe, PRBool* isUpdated)
+{
+ PORT_Assert(a);
+ PORT_Assert(b);
+ PORT_Assert(isDupe);
+ PORT_Assert(isUpdated);
+ if (!a || !b || !isDupe || !isUpdated || !a->crl || !b->crl) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ *isDupe = *isUpdated = PR_FALSE;
+
+ if (a == b) {
+ /* dupe */
+ *isDupe = PR_TRUE;
+ *isUpdated = PR_FALSE;
+ return SECSuccess;
+ }
+ if (b->origin != a->origin) {
+ /* CRLs of different origins are not considered dupes,
+ and can't be updated either */
+ return SECSuccess;
+ }
+ if (CRL_OriginToken == b->origin) {
+ /* for token CRLs, slot and PKCS#11 object handle must match for CRL
+ to truly be a dupe */
+ if ((b->crl->slot == a->crl->slot) &&
+ (b->crl->pkcs11ID == a->crl->pkcs11ID)) {
+ /* ASN.1 DER needs to match for dupe check */
+ /* could optimize by just checking a few fields like thisUpdate */
+ if (SECEqual ==
+ SECITEM_CompareItem(b->crl->derCrl, a->crl->derCrl)) {
+ *isDupe = PR_TRUE;
+ } else {
+ *isUpdated = PR_TRUE;
+ }
+ }
+ return SECSuccess;
+ }
+ if (CRL_OriginExplicit == b->origin) {
+ /* We need to make sure this is the same object that the user provided
+ to CERT_CacheCRL previously. That API takes a SECItem*, thus, we
+ just do a pointer comparison here.
+ */
+ if (b->crl->derCrl == a->crl->derCrl) {
+ *isDupe = PR_TRUE;
+ }
+ }
+ return SECSuccess;
+}
diff --git a/security/nss/lib/certdb/exports.gyp b/security/nss/lib/certdb/exports.gyp
new file mode 100644
index 0000000000..359c93041f
--- /dev/null
+++ b/security/nss/lib/certdb/exports.gyp
@@ -0,0 +1,36 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'lib_certdb_exports',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'files': [
+ 'cert.h',
+ 'certdb.h',
+ 'certt.h'
+ ],
+ 'destination': '<(nss_public_dist_dir)/<(module)'
+ },
+ {
+ 'files': [
+ 'certi.h',
+ 'certxutl.h',
+ 'genname.h',
+ 'xconst.h'
+ ],
+ 'destination': '<(nss_private_dist_dir)/<(module)'
+ }
+ ]
+ }
+ ],
+ 'variables': {
+ 'module': 'nss'
+ }
+}
diff --git a/security/nss/lib/certdb/genname.c b/security/nss/lib/certdb/genname.c
new file mode 100644
index 0000000000..764a5c1cc5
--- /dev/null
+++ b/security/nss/lib/certdb/genname.c
@@ -0,0 +1,2011 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "plarena.h"
+#include "seccomon.h"
+#include "secitem.h"
+#include "secoidt.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "certt.h"
+#include "cert.h"
+#include "certi.h"
+#include "xconst.h"
+#include "secerr.h"
+#include "secoid.h"
+#include "prprf.h"
+#include "genname.h"
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+SEC_ASN1_MKSUB(SEC_IntegerTemplate)
+SEC_ASN1_MKSUB(SEC_IA5StringTemplate)
+SEC_ASN1_MKSUB(SEC_ObjectIDTemplate)
+SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
+
+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 | SEC_ASN1_XTRN | 0,
+ offsetof(CERTNameConstraint, min), SEC_ASN1_SUB(SEC_IntegerTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
+ offsetof(CERTNameConstraint, max), SEC_ASN1_SUB(SEC_IntegerTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template CERT_NameConstraintSubtreeSubTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) }
+};
+
+static const SEC_ASN1Template CERTNameConstraintsTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNameConstraints) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(CERTNameConstraints, DERPermited),
+ CERT_NameConstraintSubtreeSubTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | 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 |
+ SEC_ASN1_XTRN | 0,
+ offsetof(OtherName, name), SEC_ASN1_SUB(SEC_AnyTemplate) },
+ { 0 }
+};
+
+static const SEC_ASN1Template CERTOtherNameTemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0,
+ offsetof(CERTGeneralName, name.OthName), CERTOthNameTemplate,
+ sizeof(CERTGeneralName) }
+};
+
+static const SEC_ASN1Template CERT_RFC822NameTemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
+ offsetof(CERTGeneralName, name.other),
+ SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) }
+};
+
+static const SEC_ASN1Template CERT_DNSNameTemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2,
+ offsetof(CERTGeneralName, name.other),
+ SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) }
+};
+
+static const SEC_ASN1Template CERT_X400AddressTemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_XTRN | 3,
+ offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_AnyTemplate),
+ sizeof(CERTGeneralName) }
+};
+
+static const SEC_ASN1Template CERT_DirectoryNameTemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_XTRN | 4,
+ offsetof(CERTGeneralName, derDirectoryName),
+ SEC_ASN1_SUB(SEC_AnyTemplate), sizeof(CERTGeneralName) }
+};
+
+static const SEC_ASN1Template CERT_EDIPartyNameTemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_XTRN | 5,
+ offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_AnyTemplate),
+ sizeof(CERTGeneralName) }
+};
+
+static const SEC_ASN1Template CERT_URITemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 6,
+ offsetof(CERTGeneralName, name.other),
+ SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) }
+};
+
+static const SEC_ASN1Template CERT_IPAddressTemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 7,
+ offsetof(CERTGeneralName, name.other),
+ SEC_ASN1_SUB(SEC_OctetStringTemplate), sizeof(CERTGeneralName) }
+};
+
+static const SEC_ASN1Template CERT_RegisteredIDTemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 8,
+ offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_ObjectIDTemplate),
+ sizeof(CERTGeneralName) }
+};
+
+const SEC_ASN1Template CERT_GeneralNamesTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) }
+};
+
+static struct {
+ CERTGeneralNameType type;
+ char *name;
+} typesArray[] = { { certOtherName, "other" },
+ { certRFC822Name, "email" },
+ { certRFC822Name, "rfc822" },
+ { certDNSName, "dns" },
+ { certX400Address, "x400" },
+ { certX400Address, "x400addr" },
+ { certDirectoryName, "directory" },
+ { certDirectoryName, "dn" },
+ { certEDIPartyName, "edi" },
+ { certEDIPartyName, "ediparty" },
+ { certURI, "uri" },
+ { certIPAddress, "ip" },
+ { certIPAddress, "ipaddr" },
+ { certRegisterID, "registerid" } };
+
+CERTGeneralNameType
+CERT_GetGeneralNameTypeFromString(const char *string)
+{
+ int types_count = sizeof(typesArray) / sizeof(typesArray[0]);
+ int i;
+
+ for (i = 0; i < types_count; i++) {
+ if (PORT_Strcasecmp(string, typesArray[i].name) == 0) {
+ return typesArray[i].type;
+ }
+ }
+ return 0;
+}
+
+CERTGeneralName *
+CERT_NewGeneralName(PLArenaPool *arena, CERTGeneralNameType type)
+{
+ CERTGeneralName *name = arena ? PORT_ArenaZNew(arena, CERTGeneralName)
+ : PORT_ZNew(CERTGeneralName);
+ if (name) {
+ name->type = type;
+ name->l.prev = name->l.next = &name->l;
+ }
+ return name;
+}
+
+/* Copy content of one General Name to another.
+** Caller has allocated destination general name.
+** This function does not change the destinate's GeneralName's list linkage.
+*/
+SECStatus
+cert_CopyOneGeneralName(PLArenaPool *arena, CERTGeneralName *dest,
+ CERTGeneralName *src)
+{
+ SECStatus rv;
+ void *mark = NULL;
+
+ PORT_Assert(dest != NULL);
+ dest->type = src->type;
+
+ mark = PORT_ArenaMark(arena);
+
+ switch (src->type) {
+ case certDirectoryName:
+ rv = SECITEM_CopyItem(arena, &dest->derDirectoryName,
+ &src->derDirectoryName);
+ if (rv == SECSuccess)
+ 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)
+ rv = SECITEM_CopyItem(arena, &dest->name.OthName.oid,
+ &src->name.OthName.oid);
+ break;
+
+ default:
+ rv = SECITEM_CopyItem(arena, &dest->name.other, &src->name.other);
+ break;
+ }
+ if (rv != SECSuccess) {
+ PORT_ArenaRelease(arena, mark);
+ } else {
+ PORT_ArenaUnmark(arena, mark);
+ }
+ return rv;
+}
+
+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)
+{
+ PLArenaPool *arena;
+ CERTGeneralNameList *list = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ goto done;
+ }
+ list = PORT_ArenaZNew(arena, CERTGeneralNameList);
+ if (!list)
+ goto loser;
+ if (name != NULL) {
+ SECStatus rv;
+ list->name = CERT_NewGeneralName(arena, (CERTGeneralNameType)0);
+ if (!list->name)
+ goto loser;
+ rv = CERT_CopyGeneralName(arena, list->name, name);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+ list->lock = PZ_NewLock(nssILockList);
+ if (!list->lock)
+ goto loser;
+ list->arena = arena;
+ list->refCount = 1;
+done:
+ return list;
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+}
+
+CERTGeneralName *
+CERT_GetNextGeneralName(CERTGeneralName *current)
+{
+ PRCList *next;
+
+ next = current->l.next;
+ return (CERTGeneralName *)(((char *)next) - offsetof(CERTGeneralName, l));
+}
+
+CERTGeneralName *
+CERT_GetPrevGeneralName(CERTGeneralName *current)
+{
+ PRCList *prev;
+ prev = current->l.prev;
+ return (CERTGeneralName *)(((char *)prev) - offsetof(CERTGeneralName, l));
+}
+
+CERTNameConstraint *
+CERT_GetNextNameConstraint(CERTNameConstraint *current)
+{
+ PRCList *next;
+
+ next = current->l.next;
+ return (CERTNameConstraint *)(((char *)next) -
+ offsetof(CERTNameConstraint, l));
+}
+
+CERTNameConstraint *
+CERT_GetPrevNameConstraint(CERTNameConstraint *current)
+{
+ PRCList *prev;
+ prev = current->l.prev;
+ return (CERTNameConstraint *)(((char *)prev) -
+ offsetof(CERTNameConstraint, l));
+}
+
+SECItem *
+CERT_EncodeGeneralName(CERTGeneralName *genName, SECItem *dest,
+ PLArenaPool *arena)
+{
+
+ const SEC_ASN1Template *template;
+
+ PORT_Assert(arena);
+ if (arena == NULL || !genName) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ /* TODO: mark arena */
+ if (dest == NULL) {
+ dest = PORT_ArenaZNew(arena, SECItem);
+ if (!dest)
+ goto loser;
+ }
+ if (genName->type == certDirectoryName) {
+ if (genName->derDirectoryName.data == NULL) {
+ /* The field hasn't been encoded yet. */
+ SECItem *pre_dest = SEC_ASN1EncodeItem(
+ arena, &(genName->derDirectoryName),
+ &(genName->name.directoryName), CERT_NameTemplate);
+ if (!pre_dest)
+ goto loser;
+ }
+ if (genName->derDirectoryName.data == NULL) {
+ goto loser;
+ }
+ }
+ switch (genName->type) {
+ case certURI:
+ template = CERT_URITemplate;
+ break;
+ case certRFC822Name:
+ template = CERT_RFC822NameTemplate;
+ break;
+ case certDNSName:
+ template = CERT_DNSNameTemplate;
+ break;
+ case certIPAddress:
+ template = CERT_IPAddressTemplate;
+ break;
+ case certOtherName:
+ template = CERTOtherNameTemplate;
+ break;
+ case certRegisterID:
+ template = CERT_RegisteredIDTemplate;
+ break;
+ /* for this type, we expect the value is already encoded */
+ case certEDIPartyName:
+ template = CERT_EDIPartyNameTemplate;
+ break;
+ /* for this type, we expect the value is already encoded */
+ case certX400Address:
+ template = CERT_X400AddressTemplate;
+ break;
+ case certDirectoryName:
+ template = CERT_DirectoryNameTemplate;
+ break;
+ default:
+ PORT_Assert(0);
+ goto loser;
+ }
+ dest = SEC_ASN1EncodeItem(arena, dest, genName, template);
+ if (!dest) {
+ goto loser;
+ }
+ /* TODO: unmark arena */
+ return dest;
+loser:
+ /* TODO: release arena back to mark */
+ return NULL;
+}
+
+SECItem **
+cert_EncodeGeneralNames(PLArenaPool *arena, CERTGeneralName *names)
+{
+ CERTGeneralName *current_name;
+ SECItem **items = NULL;
+ int count = 1;
+ int i;
+ PRCList *head;
+
+ if (!names) {
+ return NULL;
+ }
+
+ PORT_Assert(arena);
+ /* TODO: mark arena */
+ current_name = names;
+ head = &(names->l);
+ while (current_name->l.next != head) {
+ current_name = CERT_GetNextGeneralName(current_name);
+ ++count;
+ }
+ current_name = CERT_GetNextGeneralName(current_name);
+ items = PORT_ArenaNewArray(arena, 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_GetNextGeneralName(current_name);
+ }
+ items[i] = NULL;
+ /* TODO: unmark arena */
+ return items;
+loser:
+ /* TODO: release arena to mark */
+ return NULL;
+}
+
+CERTGeneralName *
+CERT_DecodeGeneralName(PLArenaPool *reqArena, SECItem *encodedName,
+ CERTGeneralName *genName)
+{
+ const SEC_ASN1Template *template;
+ CERTGeneralNameType genNameType;
+ SECStatus rv = SECSuccess;
+ SECItem *newEncodedName;
+
+ if (!reqArena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ /* make a copy for decoding so the data decoded with QuickDER doesn't
+ point to temporary memory */
+ newEncodedName = SECITEM_ArenaDupItem(reqArena, encodedName);
+ if (!newEncodedName) {
+ return NULL;
+ }
+ /* TODO: mark arena */
+ genNameType = (CERTGeneralNameType)((*(newEncodedName->data) & 0x0f) + 1);
+ if (genName == NULL) {
+ genName = CERT_NewGeneralName(reqArena, genNameType);
+ if (!genName)
+ goto loser;
+ } else {
+ genName->type = genNameType;
+ genName->l.prev = genName->l.next = &genName->l;
+ }
+
+ switch (genNameType) {
+ case certURI:
+ template = CERT_URITemplate;
+ break;
+ case certRFC822Name:
+ template = CERT_RFC822NameTemplate;
+ break;
+ case certDNSName:
+ template = CERT_DNSNameTemplate;
+ break;
+ case certIPAddress:
+ template = CERT_IPAddressTemplate;
+ break;
+ case certOtherName:
+ template = CERTOtherNameTemplate;
+ break;
+ case certRegisterID:
+ template = CERT_RegisteredIDTemplate;
+ break;
+ case certEDIPartyName:
+ template = CERT_EDIPartyNameTemplate;
+ break;
+ case certX400Address:
+ template = CERT_X400AddressTemplate;
+ break;
+ case certDirectoryName:
+ template = CERT_DirectoryNameTemplate;
+ break;
+ default:
+ goto loser;
+ }
+ rv = SEC_QuickDERDecodeItem(reqArena, genName, template, newEncodedName);
+ if (rv != SECSuccess)
+ goto loser;
+ if (genNameType == certDirectoryName) {
+ rv = SEC_QuickDERDecodeItem(reqArena, &(genName->name.directoryName),
+ CERT_NameTemplate,
+ &(genName->derDirectoryName));
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ /* TODO: unmark arena */
+ return genName;
+loser:
+ /* TODO: release arena to mark */
+ return NULL;
+}
+
+CERTGeneralName *
+cert_DecodeGeneralNames(PLArenaPool *arena, SECItem **encodedGenName)
+{
+ PRCList *head = NULL;
+ PRCList *tail = NULL;
+ CERTGeneralName *currentName = NULL;
+
+ PORT_Assert(arena);
+ if (!encodedGenName || !arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ /* TODO: mark arena */
+ while (*encodedGenName != NULL) {
+ currentName = CERT_DecodeGeneralName(arena, *encodedGenName, NULL);
+ if (currentName == NULL)
+ break;
+ if (head == NULL) {
+ head = &(currentName->l);
+ tail = head;
+ }
+ currentName->l.next = head;
+ currentName->l.prev = tail;
+ tail = head->prev = tail->next = &(currentName->l);
+ encodedGenName++;
+ }
+ if (currentName) {
+ /* TODO: unmark arena */
+ return CERT_GetNextGeneralName(currentName);
+ }
+ /* TODO: release arena to mark */
+ 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_GetNextGeneralName(name);
+ PORT_Free(name);
+ name = next;
+ } while (name != first);
+ return SECSuccess;
+}
+
+static SECItem *
+cert_EncodeNameConstraint(CERTNameConstraint *constraint, SECItem *dest,
+ PLArenaPool *arena)
+{
+ PORT_Assert(arena);
+ if (dest == NULL) {
+ dest = PORT_ArenaZNew(arena, 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,
+ PLArenaPool *arena, SECItem ***dest,
+ PRBool permited)
+{
+ CERTNameConstraint *current_constraint = constraints;
+ SECItem **items = NULL;
+ int count = 0;
+ int i;
+ PRCList *head;
+
+ PORT_Assert(arena);
+ /* TODO: mark arena */
+ if (constraints != NULL) {
+ count = 1;
+ }
+ head = &constraints->l;
+ while (current_constraint->l.next != head) {
+ current_constraint = CERT_GetNextNameConstraint(current_constraint);
+ ++count;
+ }
+ current_constraint = CERT_GetNextNameConstraint(current_constraint);
+ items = PORT_ArenaZNewArray(arena, 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_GetNextNameConstraint(current_constraint);
+ }
+ *dest = items;
+ if (*dest == NULL) {
+ goto loser;
+ }
+ /* TODO: unmark arena */
+ return SECSuccess;
+loser:
+ /* TODO: release arena to mark */
+ return SECFailure;
+}
+
+SECStatus
+cert_EncodeNameConstraints(CERTNameConstraints *constraints, PLArenaPool *arena,
+ SECItem *dest)
+{
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(arena);
+ /* TODO: mark 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;
+ }
+ /* TODO: unmark arena */
+ return SECSuccess;
+loser:
+ /* TODO: release arena to mark */
+ return SECFailure;
+}
+
+CERTNameConstraint *
+cert_DecodeNameConstraint(PLArenaPool *reqArena, SECItem *encodedConstraint)
+{
+ CERTNameConstraint *constraint;
+ SECStatus rv = SECSuccess;
+ CERTGeneralName *temp;
+ SECItem *newEncodedConstraint;
+
+ if (!reqArena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ newEncodedConstraint = SECITEM_ArenaDupItem(reqArena, encodedConstraint);
+ if (!newEncodedConstraint) {
+ return NULL;
+ }
+ /* TODO: mark arena */
+ constraint = PORT_ArenaZNew(reqArena, CERTNameConstraint);
+ if (!constraint)
+ goto loser;
+ rv = SEC_QuickDERDecodeItem(
+ reqArena, constraint, CERTNameConstraintTemplate, newEncodedConstraint);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ temp = CERT_DecodeGeneralName(reqArena, &(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);
+ /* TODO: unmark arena */
+ return constraint;
+loser:
+ /* TODO: release arena back to mark */
+ return NULL;
+}
+
+static CERTNameConstraint *
+cert_DecodeNameConstraintSubTree(PLArenaPool *arena, SECItem **subTree,
+ PRBool permited)
+{
+ CERTNameConstraint *current = NULL;
+ CERTNameConstraint *first = NULL;
+ CERTNameConstraint *last = NULL;
+ int i = 0;
+
+ PORT_Assert(arena);
+ /* TODO: mark arena */
+ while (subTree[i] != NULL) {
+ current = cert_DecodeNameConstraint(arena, subTree[i]);
+ if (current == NULL) {
+ goto loser;
+ }
+ if (first == NULL) {
+ first = current;
+ } else {
+ current->l.prev = &(last->l);
+ last->l.next = &(current->l);
+ }
+ last = current;
+ i++;
+ }
+ if (first && last) {
+ first->l.prev = &(last->l);
+ last->l.next = &(first->l);
+ }
+ /* TODO: unmark arena */
+ return first;
+loser:
+ /* TODO: release arena back to mark */
+ return NULL;
+}
+
+CERTNameConstraints *
+cert_DecodeNameConstraints(PLArenaPool *reqArena,
+ const SECItem *encodedConstraints)
+{
+ CERTNameConstraints *constraints;
+ SECStatus rv;
+ SECItem *newEncodedConstraints;
+
+ if (!reqArena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ PORT_Assert(encodedConstraints);
+ newEncodedConstraints = SECITEM_ArenaDupItem(reqArena, encodedConstraints);
+
+ /* TODO: mark arena */
+ constraints = PORT_ArenaZNew(reqArena, CERTNameConstraints);
+ if (constraints == NULL) {
+ goto loser;
+ }
+ rv = SEC_QuickDERDecodeItem(reqArena, constraints,
+ CERTNameConstraintsTemplate,
+ newEncodedConstraints);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ if (constraints->DERPermited != NULL &&
+ constraints->DERPermited[0] != NULL) {
+ constraints->permited = cert_DecodeNameConstraintSubTree(
+ reqArena, constraints->DERPermited, PR_TRUE);
+ if (constraints->permited == NULL) {
+ goto loser;
+ }
+ }
+ if (constraints->DERExcluded != NULL &&
+ constraints->DERExcluded[0] != NULL) {
+ constraints->excluded = cert_DecodeNameConstraintSubTree(
+ reqArena, constraints->DERExcluded, PR_FALSE);
+ if (constraints->excluded == NULL) {
+ goto loser;
+ }
+ }
+ /* TODO: unmark arena */
+ return constraints;
+loser:
+ /* TODO: release arena back to mark */
+ return NULL;
+}
+
+/* Copy a chain of one or more general names to a destination chain.
+** Caller has allocated at least the first destination GeneralName struct.
+** Both source and destination chains are circular doubly-linked lists.
+** The first source struct is copied to the first destination struct.
+** If the source chain has more than one member, and the destination chain
+** has only one member, then this function allocates new structs for all but
+** the first copy from the arena and links them into the destination list.
+** If the destination struct is part of a list with more than one member,
+** then this function traverses both the source and destination lists,
+** copying each source struct to the corresponding dest struct.
+** In that case, the destination list MUST contain at least as many
+** structs as the source list or some dest entries will be overwritten.
+*/
+SECStatus
+CERT_CopyGeneralName(PLArenaPool *arena, CERTGeneralName *dest,
+ CERTGeneralName *src)
+{
+ SECStatus rv;
+ CERTGeneralName *destHead = dest;
+ CERTGeneralName *srcHead = src;
+
+ PORT_Assert(dest != NULL);
+ if (!dest) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ /* TODO: mark arena */
+ do {
+ rv = cert_CopyOneGeneralName(arena, dest, src);
+ if (rv != SECSuccess)
+ goto loser;
+ src = CERT_GetNextGeneralName(src);
+ /* if there is only one general name, we shouldn't do this */
+ if (src != srcHead) {
+ if (dest->l.next == &destHead->l) {
+ CERTGeneralName *temp;
+ temp = CERT_NewGeneralName(arena, (CERTGeneralNameType)0);
+ if (!temp)
+ goto loser;
+ 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_GetNextGeneralName(dest);
+ }
+ }
+ } while (src != srcHead && rv == SECSuccess);
+ /* TODO: unmark arena */
+ return rv;
+loser:
+ /* TODO: release back to mark */
+ return SECFailure;
+}
+
+CERTGeneralNameList *
+CERT_DupGeneralNameList(CERTGeneralNameList *list)
+{
+ if (list != NULL) {
+ PZ_Lock(list->lock);
+ list->refCount++;
+ PZ_Unlock(list->lock);
+ }
+ return list;
+}
+
+/* Allocate space and copy CERTNameConstraint from src to dest */
+CERTNameConstraint *
+CERT_CopyNameConstraint(PLArenaPool *arena, CERTNameConstraint *dest,
+ CERTNameConstraint *src)
+{
+ SECStatus rv;
+
+ /* TODO: mark arena */
+ if (dest == NULL) {
+ dest = PORT_ArenaZNew(arena, CERTNameConstraint);
+ if (!dest)
+ goto loser;
+ /* 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;
+ /* TODO: unmark arena */
+ return dest;
+loser:
+ /* TODO: release arena to mark */
+ 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;
+ }
+}
+
+/* Add a CERTNameConstraint to the CERTNameConstraint list */
+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_GetNameConstraintByType(CERTNameConstraint *constraints,
+ CERTGeneralNameType type,
+ CERTNameConstraint **returnList,
+ PLArenaPool *arena)
+{
+ CERTNameConstraint *current = NULL;
+ void *mark = NULL;
+
+ *returnList = NULL;
+ if (!constraints)
+ return SECSuccess;
+
+ mark = PORT_ArenaMark(arena);
+
+ current = constraints;
+ do {
+ PORT_Assert(current->name.type);
+ if (current->name.type == type) {
+ CERTNameConstraint *temp;
+ temp = CERT_CopyNameConstraint(arena, NULL, current);
+ if (temp == NULL)
+ goto loser;
+ *returnList = CERT_AddNameConstraint(*returnList, temp);
+ }
+ current = CERT_GetNextNameConstraint(current);
+ } while (current != constraints);
+ PORT_ArenaUnmark(arena, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(arena, mark);
+ 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 (void *)&current->name.other; /* SECItem * */
+
+ case certOtherName:
+ return (void *)&current->name.OthName; /* OthName * */
+
+ case certDirectoryName:
+ return derFormat
+ ? (void *)&current
+ ->derDirectoryName /* SECItem * */
+ : (void *)&current->name
+ .directoryName; /* CERTName * */
+ }
+ PORT_Assert(0);
+ return NULL;
+ }
+ current = CERT_GetNextGeneralName(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_GetNextGeneralName(names);
+ } while (names != first);
+ }
+ return length;
+}
+
+/* Creates new GeneralNames for any email addresses found in the
+** input DN, and links them onto the list for the DN.
+*/
+SECStatus
+cert_ExtractDNEmailAddrs(CERTGeneralName *name, PLArenaPool *arena)
+{
+ CERTGeneralName *nameList = NULL;
+ const CERTRDN **nRDNs = (const CERTRDN **)(name->name.directoryName.rdns);
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(name->type == certDirectoryName);
+ if (name->type != certDirectoryName) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ /* TODO: mark arena */
+ while (nRDNs && *nRDNs) { /* loop over RDNs */
+ const CERTRDN *nRDN = *nRDNs++;
+ CERTAVA **nAVAs = nRDN->avas;
+ while (nAVAs && *nAVAs) { /* loop over AVAs */
+ int tag;
+ CERTAVA *nAVA = *nAVAs++;
+ tag = CERT_GetAVATag(nAVA);
+ if (tag == SEC_OID_PKCS9_EMAIL_ADDRESS ||
+ tag == SEC_OID_RFC1274_MAIL) { /* email AVA */
+ CERTGeneralName *newName = NULL;
+ SECItem *avaValue = CERT_DecodeAVAValue(&nAVA->value);
+ if (!avaValue)
+ goto loser;
+ rv = SECFailure;
+ newName = CERT_NewGeneralName(arena, certRFC822Name);
+ if (newName) {
+ rv =
+ SECITEM_CopyItem(arena, &newName->name.other, avaValue);
+ }
+ SECITEM_FreeItem(avaValue, PR_TRUE);
+ if (rv != SECSuccess)
+ goto loser;
+ nameList = cert_CombineNamesLists(nameList, newName);
+ } /* handle one email AVA */
+ } /* loop over AVAs */
+ } /* loop over RDNs */
+ /* combine new names with old one. */
+ (void)cert_CombineNamesLists(name, nameList);
+ /* TODO: unmark arena */
+ return SECSuccess;
+
+loser:
+ /* TODO: release arena back to mark */
+ return SECFailure;
+}
+
+/* Extract all names except Subject Common Name from a cert
+** in preparation for a name constraints test.
+*/
+CERTGeneralName *
+CERT_GetCertificateNames(CERTCertificate *cert, PLArenaPool *arena)
+{
+ return CERT_GetConstrainedCertificateNames(cert, arena, PR_FALSE);
+}
+
+/* This function is called by CERT_VerifyCertChain to extract all
+** names from a cert in preparation for a name constraints test.
+*/
+CERTGeneralName *
+CERT_GetConstrainedCertificateNames(const CERTCertificate *cert,
+ PLArenaPool *arena,
+ PRBool includeSubjectCommonName)
+{
+ CERTGeneralName *DN;
+ CERTGeneralName *SAN;
+ PRUint32 numDNSNames = 0;
+ SECStatus rv;
+
+ if (!arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ /* TODO: mark arena */
+ DN = CERT_NewGeneralName(arena, certDirectoryName);
+ if (DN == NULL) {
+ goto loser;
+ }
+ rv = CERT_CopyName(arena, &DN->name.directoryName, &cert->subject);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = SECITEM_CopyItem(arena, &DN->derDirectoryName, &cert->derSubject);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ /* Extract email addresses from DN, construct CERTGeneralName structs
+ ** for them, add them to the name list
+ */
+ rv = cert_ExtractDNEmailAddrs(DN, arena);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* Now extract any GeneralNames from the subject name names extension. */
+ SAN = cert_GetSubjectAltNameList(cert, arena);
+ if (SAN) {
+ numDNSNames = cert_CountDNSPatterns(SAN);
+ DN = cert_CombineNamesLists(DN, SAN);
+ }
+ if (!numDNSNames && includeSubjectCommonName) {
+ char *cn = CERT_GetCommonName(&cert->subject);
+ if (cn) {
+ CERTGeneralName *CN = CERT_NewGeneralName(arena, certDNSName);
+ if (CN) {
+ SECItem cnItem = { siBuffer, NULL, 0 };
+ cnItem.data = (unsigned char *)cn;
+ cnItem.len = strlen(cn);
+ rv = SECITEM_CopyItem(arena, &CN->name.other, &cnItem);
+ if (rv == SECSuccess) {
+ DN = cert_CombineNamesLists(DN, CN);
+ }
+ }
+ PORT_Free(cn);
+ }
+ }
+ if (rv == SECSuccess) {
+ /* TODO: unmark arena */
+ return DN;
+ }
+loser:
+ /* TODO: release arena to mark */
+ return NULL;
+}
+
+/* Returns SECSuccess if name matches constraint per RFC 3280 rules for
+** URI name constraints. SECFailure otherwise.
+** If the constraint begins with a dot, it is a domain name, otherwise
+** It is a host name. Examples:
+** Constraint Name Result
+** ------------ --------------- --------
+** foo.bar.com foo.bar.com matches
+** foo.bar.com FoO.bAr.CoM matches
+** foo.bar.com www.foo.bar.com no match
+** foo.bar.com nofoo.bar.com no match
+** .foo.bar.com www.foo.bar.com matches
+** .foo.bar.com nofoo.bar.com no match
+** .foo.bar.com foo.bar.com no match
+** .foo.bar.com www..foo.bar.com no match
+*/
+static SECStatus
+compareURIN2C(const SECItem *name, const SECItem *constraint)
+{
+ int offset;
+ /* The spec is silent on intepreting zero-length constraints.
+ ** We interpret them as matching no URI names.
+ */
+ if (!constraint->len)
+ return SECFailure;
+ if (constraint->data[0] != '.') {
+ /* constraint is a host name. */
+ if (name->len != constraint->len ||
+ PL_strncasecmp((char *)name->data, (char *)constraint->data,
+ constraint->len))
+ return SECFailure;
+ return SECSuccess;
+ }
+ /* constraint is a domain name. */
+ if (name->len < constraint->len)
+ return SECFailure;
+ offset = name->len - constraint->len;
+ if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data,
+ constraint->len))
+ return SECFailure;
+ if (!offset ||
+ (name->data[offset - 1] == '.') + (constraint->data[0] == '.') == 1)
+ return SECSuccess;
+ return SECFailure;
+}
+
+/* for DNSname constraints, RFC 3280 says, (section 4.2.1.11, page 38)
+**
+** DNS name restrictions are expressed as foo.bar.com. Any DNS name
+** that can be constructed by simply adding to the left hand side of the
+** name satisfies the name constraint. For example, www.foo.bar.com
+** would satisfy the constraint but foo1.bar.com would not.
+**
+** But NIST's PKITS test suite requires that the constraint be treated
+** as a domain name, and requires that any name added to the left hand
+** side end in a dot ".". Sensible, but not strictly following the RFC.
+**
+** Constraint Name RFC 3280 NIST PKITS
+** ------------ --------------- -------- ----------
+** foo.bar.com foo.bar.com matches matches
+** foo.bar.com FoO.bAr.CoM matches matches
+** foo.bar.com www.foo.bar.com matches matches
+** foo.bar.com nofoo.bar.com MATCHES NO MATCH
+** .foo.bar.com www.foo.bar.com matches matches? disallowed?
+** .foo.bar.com foo.bar.com no match no match
+** .foo.bar.com www..foo.bar.com matches probably not
+**
+** We will try to conform to NIST's PKITS tests, and the unstated
+** rules they imply.
+*/
+static SECStatus
+compareDNSN2C(const SECItem *name, const SECItem *constraint)
+{
+ int offset;
+ /* The spec is silent on intepreting zero-length constraints.
+ ** We interpret them as matching all DNSnames.
+ */
+ if (!constraint->len)
+ return SECSuccess;
+ if (name->len < constraint->len)
+ return SECFailure;
+ offset = name->len - constraint->len;
+ if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data,
+ constraint->len))
+ return SECFailure;
+ if (!offset ||
+ (name->data[offset - 1] == '.') + (constraint->data[0] == '.') == 1)
+ return SECSuccess;
+ return SECFailure;
+}
+
+/* Returns SECSuccess if name matches constraint per RFC 3280 rules for
+** internet email addresses. SECFailure otherwise.
+** If constraint contains a '@' then the two strings much match exactly.
+** Else if constraint starts with a '.'. then it must match the right-most
+** substring of the name,
+** else constraint string must match entire name after the name's '@'.
+** Empty constraint string matches all names. All comparisons case insensitive.
+*/
+static SECStatus
+compareRFC822N2C(const SECItem *name, const SECItem *constraint)
+{
+ int offset;
+ if (!constraint->len)
+ return SECSuccess;
+ if (name->len < constraint->len)
+ return SECFailure;
+ if (constraint->len == 1 && constraint->data[0] == '.')
+ return SECSuccess;
+ for (offset = constraint->len - 1; offset >= 0; --offset) {
+ if (constraint->data[offset] == '@') {
+ return (name->len == constraint->len &&
+ !PL_strncasecmp((char *)name->data,
+ (char *)constraint->data, constraint->len))
+ ? SECSuccess
+ : SECFailure;
+ }
+ }
+ offset = name->len - constraint->len;
+ if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data,
+ constraint->len))
+ return SECFailure;
+ if (constraint->data[0] == '.')
+ return SECSuccess;
+ if (offset > 0 && name->data[offset - 1] == '@')
+ return SECSuccess;
+ return SECFailure;
+}
+
+/* name contains either a 4 byte IPv4 address or a 16 byte IPv6 address.
+** constraint contains an address of the same length, and a subnet mask
+** of the same length. Compare name's address to the constraint's
+** address, subject to the mask.
+** Return SECSuccess if they match, SECFailure if they don't.
+*/
+static SECStatus
+compareIPaddrN2C(const SECItem *name, const SECItem *constraint)
+{
+ int i;
+ if (name->len == 4 && constraint->len == 8) { /* ipv4 addr */
+ for (i = 0; i < 4; i++) {
+ if ((name->data[i] ^ constraint->data[i]) & constraint->data[i + 4])
+ goto loser;
+ }
+ return SECSuccess;
+ }
+ if (name->len == 16 && constraint->len == 32) { /* ipv6 addr */
+ for (i = 0; i < 16; i++) {
+ if ((name->data[i] ^ constraint->data[i]) &
+ constraint->data[i + 16])
+ goto loser;
+ }
+ return SECSuccess;
+ }
+loser:
+ return SECFailure;
+}
+
+/* start with a SECItem that points to a URI. Parse it lookingg for
+** a hostname. Modify item->data and item->len to define the hostname,
+** but do not modify and data at item->data.
+** If anything goes wrong, the contents of *item are undefined.
+*/
+static SECStatus
+parseUriHostname(SECItem *item)
+{
+ int i;
+ PRBool found = PR_FALSE;
+ for (i = 0; (unsigned)(i + 2) < item->len; ++i) {
+ if (item->data[i] == ':' && item->data[i + 1] == '/' &&
+ item->data[i + 2] == '/') {
+ i += 3;
+ item->data += i;
+ item->len -= i;
+ found = PR_TRUE;
+ break;
+ }
+ }
+ if (!found)
+ return SECFailure;
+ /* now look for a '/', which is an upper bound in the end of the name */
+ for (i = 0; (unsigned)i < item->len; ++i) {
+ if (item->data[i] == '/') {
+ item->len = i;
+ break;
+ }
+ }
+ /* now look for a ':', which marks the end of the name */
+ for (i = item->len; --i >= 0;) {
+ if (item->data[i] == ':') {
+ item->len = i;
+ break;
+ }
+ }
+ /* now look for an '@', which marks the beginning of the hostname */
+ for (i = 0; (unsigned)i < item->len; ++i) {
+ if (item->data[i] == '@') {
+ ++i;
+ item->data += i;
+ item->len -= i;
+ break;
+ }
+ }
+ return item->len ? SECSuccess : SECFailure;
+}
+
+/* This function takes one name, and a list of constraints.
+** It searches the constraints looking for a match.
+** It returns SECSuccess if the name satisfies the constraints, i.e.,
+** if excluded, then the name does not match any constraint,
+** if permitted, then the name matches at least one constraint.
+** It returns SECFailure if the name fails to satisfy the constraints,
+** or if some code fails (e.g. out of memory, or invalid constraint)
+*/
+SECStatus
+cert_CompareNameWithConstraints(const CERTGeneralName *name,
+ const CERTNameConstraint *constraints,
+ PRBool excluded)
+{
+ SECStatus rv = SECSuccess;
+ SECStatus matched = SECFailure;
+ const CERTNameConstraint *current;
+
+ PORT_Assert(constraints); /* caller should not call with NULL */
+ if (!constraints) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ current = constraints;
+ do {
+ rv = SECSuccess;
+ matched = SECFailure;
+ PORT_Assert(name->type == current->name.type);
+ switch (name->type) {
+
+ case certDNSName:
+ matched =
+ compareDNSN2C(&name->name.other, &current->name.name.other);
+ break;
+
+ case certRFC822Name:
+ matched = compareRFC822N2C(&name->name.other,
+ &current->name.name.other);
+ break;
+
+ case certURI: {
+ /* make a modifiable copy of the URI SECItem. */
+ SECItem uri = name->name.other;
+ /* find the hostname in the URI */
+ rv = parseUriHostname(&uri);
+ if (rv == SECSuccess) {
+ /* does our hostname meet the constraint? */
+ matched = compareURIN2C(&uri, &current->name.name.other);
+ }
+ } break;
+
+ case certDirectoryName:
+ /* Determine if the constraint directory name is a "prefix"
+ ** for the directory name being tested.
+ */
+ {
+ /* status defaults to SECEqual, so that a constraint with
+ ** no AVAs will be a wildcard, matching all directory names.
+ */
+ SECComparison status = SECEqual;
+ const CERTRDN **cRDNs =
+ (const CERTRDN **)current->name.name.directoryName.rdns;
+ const CERTRDN **nRDNs =
+ (const CERTRDN **)name->name.directoryName.rdns;
+ while (cRDNs && *cRDNs && nRDNs && *nRDNs) {
+ /* loop over name RDNs and constraint RDNs in lock step
+ */
+ const CERTRDN *cRDN = *cRDNs++;
+ const CERTRDN *nRDN = *nRDNs++;
+ CERTAVA **cAVAs = cRDN->avas;
+ while (cAVAs &&
+ *cAVAs) { /* loop over constraint AVAs */
+ CERTAVA *cAVA = *cAVAs++;
+ CERTAVA **nAVAs = nRDN->avas;
+ while (nAVAs && *nAVAs) { /* loop over name AVAs */
+ CERTAVA *nAVA = *nAVAs++;
+ status = CERT_CompareAVA(cAVA, nAVA);
+ if (status == SECEqual)
+ break;
+ } /* loop over name AVAs */
+ if (status != SECEqual)
+ break;
+ } /* loop over constraint AVAs */
+ if (status != SECEqual)
+ break;
+ } /* loop over name RDNs and constraint RDNs */
+ matched = (status == SECEqual) ? SECSuccess : SECFailure;
+ break;
+ }
+
+ case certIPAddress: /* type 8 */
+ matched = compareIPaddrN2C(&name->name.other,
+ &current->name.name.other);
+ break;
+
+ /* NSS does not know how to compare these "Other" type names with
+ ** their respective constraints. But it does know how to tell
+ ** if the constraint applies to the type of name (by comparing
+ ** the constraint OID to the name OID). NSS makes no use of "Other"
+ ** type names at all, so NSS errs on the side of leniency for these
+ ** types, provided that their OIDs match. So, when an "Other"
+ ** name constraint appears in an excluded subtree, it never causes
+ ** a name to fail. When an "Other" name constraint appears in a
+ ** permitted subtree, AND the constraint's OID matches the name's
+ ** OID, then name is treated as if it matches the constraint.
+ */
+ case certOtherName: /* type 1 */
+ matched =
+ (!excluded && name->type == current->name.type &&
+ SECITEM_ItemsAreEqual(&name->name.OthName.oid,
+ &current->name.name.OthName.oid))
+ ? SECSuccess
+ : SECFailure;
+ break;
+
+ /* NSS does not know how to compare these types of names with their
+ ** respective constraints. But NSS makes no use of these types of
+ ** names at all, so it errs on the side of leniency for these types.
+ ** Constraints for these types of names never cause the name to
+ ** fail the constraints test. NSS behaves as if the name matched
+ ** for permitted constraints, and did not match for excluded ones.
+ */
+ case certX400Address: /* type 4 */
+ case certEDIPartyName: /* type 6 */
+ case certRegisterID: /* type 9 */
+ matched = excluded ? SECFailure : SECSuccess;
+ break;
+
+ default: /* non-standard types are not supported */
+ rv = SECFailure;
+ break;
+ }
+ if (matched == SECSuccess || rv != SECSuccess)
+ break;
+ current = CERT_GetNextNameConstraint((CERTNameConstraint *)current);
+ } while (current != constraints);
+ if (rv == SECSuccess) {
+ if (matched == SECSuccess)
+ rv = excluded ? SECFailure : SECSuccess;
+ else
+ rv = excluded ? SECSuccess : SECFailure;
+ return rv;
+ }
+
+ return SECFailure;
+}
+
+/* Add and link a CERTGeneralName to a CERTNameConstraint list. Most
+** likely the CERTNameConstraint passed in is either the permitted
+** list or the excluded list of a CERTNameConstraints.
+*/
+SECStatus
+CERT_AddNameConstraintByGeneralName(PLArenaPool *arena,
+ CERTNameConstraint **constraints,
+ CERTGeneralName *name)
+{
+ SECStatus rv;
+ CERTNameConstraint *current = NULL;
+ CERTNameConstraint *first = *constraints;
+ void *mark = NULL;
+
+ mark = PORT_ArenaMark(arena);
+
+ current = PORT_ArenaZNew(arena, CERTNameConstraint);
+ if (current == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ rv = cert_CopyOneGeneralName(arena, &current->name, name);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+
+ current->name.l.prev = current->name.l.next = &(current->name.l);
+
+ if (first == NULL) {
+ *constraints = current;
+ PR_INIT_CLIST(&current->l);
+ } else {
+ PR_INSERT_BEFORE(&current->l, &first->l);
+ }
+
+done:
+ if (rv == SECFailure) {
+ PORT_ArenaRelease(arena, mark);
+ } else {
+ PORT_ArenaUnmark(arena, mark);
+ }
+ return rv;
+}
+
+/*
+ * Here we define a list of name constraints to be imposed on
+ * certain certificates, most importantly root certificates.
+ *
+ * Each entry in the name constraints list is constructed with this
+ * macro. An entry contains two SECItems, which have names in
+ * specific forms to make the macro work:
+ *
+ * * ${CA}_SUBJECT_DN - The subject DN for which the constraints
+ * should be applied
+ * * ${CA}_NAME_CONSTRAINTS - The name constraints extension
+ *
+ * Entities subject to name constraints are identified by subject name
+ * so that we can cover all certificates for that entity, including, e.g.,
+ * cross-certificates. We use subject rather than public key because
+ * calling methods often have easy access to that field (vs., say, a key ID),
+ * and in practice, subject names and public keys are usually in one-to-one
+ * correspondence anyway.
+ *
+ */
+
+#define STRING_TO_SECITEM(str) \
+ { \
+ siBuffer, (unsigned char *)str, sizeof(str) - 1 \
+ }
+
+#define NAME_CONSTRAINTS_ENTRY(CA) \
+ { \
+ STRING_TO_SECITEM(CA##_SUBJECT_DN) \
+ , \
+ STRING_TO_SECITEM(CA##_NAME_CONSTRAINTS) \
+ }
+
+/* clang-format off */
+
+/* Agence Nationale de la Securite des Systemes d'Information (ANSSI) */
+
+#define ANSSI_SUBJECT_DN \
+ "\x30\x81\x85" \
+ "\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02" "FR" /* C */ \
+ "\x31\x0F\x30\x0D\x06\x03\x55\x04\x08\x13\x06" "France" /* ST */ \
+ "\x31\x0E\x30\x0C\x06\x03\x55\x04\x07\x13\x05" "Paris" /* L */ \
+ "\x31\x10\x30\x0E\x06\x03\x55\x04\x0A\x13\x07" "PM/SGDN" /* O */ \
+ "\x31\x0E\x30\x0C\x06\x03\x55\x04\x0B\x13\x05" "DCSSI" /* OU */ \
+ "\x31\x0E\x30\x0C\x06\x03\x55\x04\x03\x13\x05" "IGC/A" /* CN */ \
+ "\x31\x23\x30\x21\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01" \
+ "\x16\x14" "igca@sgdn.pm.gouv.fr" /* emailAddress */ \
+
+#define ANSSI_NAME_CONSTRAINTS \
+ "\x30\x5D\xA0\x5B" \
+ "\x30\x05\x82\x03" ".fr" \
+ "\x30\x05\x82\x03" ".gp" \
+ "\x30\x05\x82\x03" ".gf" \
+ "\x30\x05\x82\x03" ".mq" \
+ "\x30\x05\x82\x03" ".re" \
+ "\x30\x05\x82\x03" ".yt" \
+ "\x30\x05\x82\x03" ".pm" \
+ "\x30\x05\x82\x03" ".bl" \
+ "\x30\x05\x82\x03" ".mf" \
+ "\x30\x05\x82\x03" ".wf" \
+ "\x30\x05\x82\x03" ".pf" \
+ "\x30\x05\x82\x03" ".nc" \
+ "\x30\x05\x82\x03" ".tf"
+
+/* TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 */
+
+#define TUBITAK1_SUBJECT_DN \
+ "\x30\x81\xd2" \
+ "\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02" \
+ /* C */ "TR" \
+ "\x31\x18\x30\x16\x06\x03\x55\x04\x07\x13\x0f" \
+ /* L */ "Gebze - Kocaeli" \
+ "\x31\x42\x30\x40\x06\x03\x55\x04\x0a\x13\x39" \
+ /* O */ "Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK" \
+ "\x31\x2d\x30\x2b\x06\x03\x55\x04\x0b\x13\x24" \
+ /* OU */ "Kamu Sertifikasyon Merkezi - Kamu SM" \
+ "\x31\x36\x30\x34\x06\x03\x55\x04\x03\x13\x2d" \
+ /* CN */ "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1"
+
+#define TUBITAK1_NAME_CONSTRAINTS \
+ "\x30\x09\xa0\x07" \
+ "\x30\x05\x82\x03" ".tr"
+
+/* clang-format on */
+
+static const SECItem builtInNameConstraints[][2] = {
+ NAME_CONSTRAINTS_ENTRY(ANSSI),
+ NAME_CONSTRAINTS_ENTRY(TUBITAK1)
+};
+
+SECStatus
+CERT_GetImposedNameConstraints(const SECItem *derSubject, SECItem *extensions)
+{
+ size_t i;
+
+ if (!extensions) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ for (i = 0; i < PR_ARRAY_SIZE(builtInNameConstraints); ++i) {
+ if (SECITEM_ItemsAreEqual(derSubject, &builtInNameConstraints[i][0])) {
+ return SECITEM_CopyItem(NULL, extensions,
+ &builtInNameConstraints[i][1]);
+ }
+ }
+
+ PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
+ return SECFailure;
+}
+
+/*
+ * Extract the name constraints extension from the CA cert.
+ * If the certificate contains no name constraints extension, but
+ * CERT_GetImposedNameConstraints returns a name constraints extension
+ * for the subject of the certificate, then that extension will be returned.
+ */
+SECStatus
+CERT_FindNameConstraintsExten(PLArenaPool *arena, CERTCertificate *cert,
+ CERTNameConstraints **constraints)
+{
+ SECStatus rv = SECSuccess;
+ SECItem constraintsExtension;
+ void *mark = NULL;
+
+ *constraints = NULL;
+
+ rv = CERT_FindCertExtension(cert, SEC_OID_X509_NAME_CONSTRAINTS,
+ &constraintsExtension);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
+ return rv;
+ }
+ rv = CERT_GetImposedNameConstraints(&cert->derSubject,
+ &constraintsExtension);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) {
+ return SECSuccess;
+ }
+ return rv;
+ }
+ }
+
+ mark = PORT_ArenaMark(arena);
+
+ *constraints = cert_DecodeNameConstraints(arena, &constraintsExtension);
+ if (*constraints == NULL) { /* decode failed */
+ rv = SECFailure;
+ }
+ PORT_Free(constraintsExtension.data);
+
+ if (rv == SECFailure) {
+ PORT_ArenaRelease(arena, mark);
+ } else {
+ PORT_ArenaUnmark(arena, mark);
+ }
+
+ return rv;
+}
+
+/* Verify name against all the constraints relevant to that type of
+** the name.
+*/
+SECStatus
+CERT_CheckNameSpace(PLArenaPool *arena, const CERTNameConstraints *constraints,
+ const CERTGeneralName *currentName)
+{
+ CERTNameConstraint *matchingConstraints;
+ SECStatus rv = SECSuccess;
+
+ if (constraints->excluded != NULL) {
+ rv = CERT_GetNameConstraintByType(constraints->excluded,
+ currentName->type,
+ &matchingConstraints, arena);
+ if (rv == SECSuccess && matchingConstraints != NULL) {
+ rv = cert_CompareNameWithConstraints(currentName,
+ matchingConstraints, PR_TRUE);
+ }
+ if (rv != SECSuccess) {
+ return (rv);
+ }
+ }
+
+ if (constraints->permited != NULL) {
+ rv = CERT_GetNameConstraintByType(constraints->permited,
+ currentName->type,
+ &matchingConstraints, arena);
+ if (rv == SECSuccess && matchingConstraints != NULL) {
+ rv = cert_CompareNameWithConstraints(currentName,
+ matchingConstraints, PR_FALSE);
+ }
+ if (rv != SECSuccess) {
+ return (rv);
+ }
+ }
+
+ return (SECSuccess);
+}
+
+/* Extract the name constraints extension from the CA cert.
+** Test each and every name in namesList against all the constraints
+** relevant to that type of name.
+** Returns NULL in pBadCert for success, if all names are acceptable.
+** If some name is not acceptable, returns a pointer to the cert that
+** contained that name.
+*/
+SECStatus
+CERT_CompareNameSpace(CERTCertificate *cert, CERTGeneralName *namesList,
+ CERTCertificate **certsList, PLArenaPool *reqArena,
+ CERTCertificate **pBadCert)
+{
+ SECStatus rv = SECSuccess;
+ CERTNameConstraints *constraints;
+ CERTGeneralName *currentName;
+ int count = 0;
+ CERTCertificate *badCert = NULL;
+
+ /* If no names to check, then no names can be bad. */
+ if (!namesList)
+ goto done;
+ rv = CERT_FindNameConstraintsExten(reqArena, cert, &constraints);
+ if (rv != SECSuccess) {
+ count = -1;
+ goto done;
+ }
+
+ currentName = namesList;
+ do {
+ if (constraints) {
+ rv = CERT_CheckNameSpace(reqArena, constraints, currentName);
+ if (rv != SECSuccess) {
+ break;
+ }
+ }
+ currentName = CERT_GetNextGeneralName(currentName);
+ count++;
+ } while (currentName != namesList);
+
+done:
+ if (rv != SECSuccess) {
+ badCert = (count >= 0) ? certsList[count] : cert;
+ }
+ if (pBadCert)
+ *pBadCert = badCert;
+
+ return rv;
+}
+
+#if 0
+/* not exported from shared libs, not used. Turn on if we ever need it. */
+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_GetNextGeneralName(currentB);
+ currentA = CERT_GetNextGeneralName(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(&currentA->name.other,
+ &currentB->name.other)
+ == SECEqual) {
+ found = PR_TRUE;
+ }
+ break;
+ case certOtherName:
+ if (SECITEM_CompareItem(&currentA->name.OthName.oid,
+ &currentB->name.OthName.oid)
+ == SECEqual &&
+ SECITEM_CompareItem(&currentA->name.OthName.name,
+ &currentB->name.OthName.name)
+ == SECEqual) {
+ found = PR_TRUE;
+ }
+ break;
+ case certDirectoryName:
+ if (CERT_CompareName(&currentA->name.directoryName,
+ &currentB->name.directoryName)
+ == SECEqual) {
+ found = PR_TRUE;
+ }
+ }
+
+ }
+ currentB = CERT_GetNextGeneralName(currentB);
+ } while (currentB != b && found != PR_TRUE);
+ if (found != PR_TRUE) {
+ return SECFailure;
+ }
+ currentA = CERT_GetNextGeneralName(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;
+}
+#endif
+
+#if 0
+/* This function is not exported from NSS shared libraries, and is not
+** used inside of NSS.
+** XXX it doesn't check for failed allocations. :-(
+*/
+void *
+CERT_GetGeneralNameFromListByType(CERTGeneralNameList *list,
+ CERTGeneralNameType type,
+ PLArenaPool *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 = PORT_ArenaNew(arena, SECItem);
+ if (item != NULL) {
+XXX 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 = PORT_ArenaNew(arena, OtherName);
+ } else {
+ tmpOther = PORT_New(OtherName);
+ }
+ if (tmpOther != NULL) {
+XXX SECITEM_CopyItem(arena, &tmpOther->oid, &other->oid);
+XXX SECITEM_CopyItem(arena, &tmpOther->name, &other->name);
+ }
+ PZ_Unlock(list->lock);
+ return tmpOther;
+ case certDirectoryName:
+ if (arena) {
+ name = PORT_ArenaZNew(list->arena, CERTName);
+ if (name) {
+XXX CERT_CopyName(arena, name, (CERTName *) data);
+ }
+ }
+ PZ_Unlock(list->lock);
+ return name;
+ }
+ }
+ PZ_Unlock(list->lock);
+ return NULL;
+}
+#endif
+
+#if 0
+/* This function is not exported from NSS shared libraries, and is not
+** used inside of NSS.
+** XXX it should NOT be a void function, since it does allocations
+** that can fail.
+*/
+void
+CERT_AddGeneralNameToList(CERTGeneralNameList *list,
+ CERTGeneralNameType type,
+ void *data, SECItem *oid)
+{
+ CERTGeneralName *name;
+
+ if (list != NULL && data != NULL) {
+ PZ_Lock(list->lock);
+ name = CERT_NewGeneralName(list->arena, type);
+ if (!name)
+ goto done;
+ switch (type) {
+ case certDNSName:
+ case certEDIPartyName:
+ case certIPAddress:
+ case certRegisterID:
+ case certRFC822Name:
+ case certX400Address:
+ case certURI:
+XXX SECITEM_CopyItem(list->arena, &name->name.other, (SECItem *)data);
+ break;
+ case certOtherName:
+XXX SECITEM_CopyItem(list->arena, &name->name.OthName.name,
+ (SECItem *) data);
+XXX SECITEM_CopyItem(list->arena, &name->name.OthName.oid,
+ oid);
+ break;
+ case certDirectoryName:
+XXX CERT_CopyName(list->arena, &name->name.directoryName,
+ (CERTName *) data);
+ break;
+ }
+ list->name = cert_CombineNamesLists(list->name, name);
+ list->len++;
+done:
+ PZ_Unlock(list->lock);
+ }
+ return;
+}
+#endif
diff --git a/security/nss/lib/certdb/genname.h b/security/nss/lib/certdb/genname.h
new file mode 100644
index 0000000000..5824157108
--- /dev/null
+++ b/security/nss/lib/certdb/genname.h
@@ -0,0 +1,93 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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 SECItem **cert_EncodeGeneralNames(PLArenaPool *arena,
+ CERTGeneralName *names);
+
+extern CERTGeneralName *cert_DecodeGeneralNames(PLArenaPool *arena,
+ SECItem **encodedGenName);
+
+extern SECStatus cert_DestroyGeneralNames(CERTGeneralName *name);
+
+extern SECStatus cert_EncodeNameConstraints(CERTNameConstraints *constraints,
+ PLArenaPool *arena, SECItem *dest);
+
+extern CERTNameConstraints *cert_DecodeNameConstraints(
+ PLArenaPool *arena, const SECItem *encodedConstraints);
+
+extern CERTGeneralName *cert_CombineNamesLists(CERTGeneralName *list1,
+ CERTGeneralName *list2);
+
+extern CERTNameConstraint *cert_CombineConstraintsLists(
+ CERTNameConstraint *list1, CERTNameConstraint *list2);
+
+/*********************************************************************/
+/* A thread safe implementation of General Names */
+/*********************************************************************/
+
+/* Destroy a Single CERTGeneralName */
+void CERT_DestroyGeneralName(CERTGeneralName *name);
+
+SECStatus CERT_CompareGeneralName(CERTGeneralName *a, CERTGeneralName *b);
+
+SECStatus CERT_CopyGeneralName(PLArenaPool *arena, CERTGeneralName *dest,
+ CERTGeneralName *src);
+
+/* General Name Lists are a thread safe, reference counting layer to
+ * general names */
+
+/* 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,
+ PLArenaPool *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 number of CERTGeneralName objects in the doubly linked
+** list of which *names is a member.
+*/
+extern int CERT_GetNamesLength(CERTGeneralName *names);
+
+/************************************************************************/
+
+SECStatus CERT_CompareNameSpace(CERTCertificate *cert,
+ CERTGeneralName *namesList,
+ CERTCertificate **certsList,
+ PLArenaPool *reqArena,
+ CERTCertificate **pBadCert);
+
+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 0000000000..4a8c5c40ad
--- /dev/null
+++ b/security/nss/lib/certdb/manifest.mn
@@ -0,0 +1,41 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+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)
+
+LIBRARY_NAME = certdb
+SHARED_LIBRARY = $(NULL)
+
+# This part of the code, including all sub-dirs, can be optimized for size
+export ALLOW_OPT_CODE_SIZE = 1
diff --git a/security/nss/lib/certdb/polcyxtn.c b/security/nss/lib/certdb/polcyxtn.c
new file mode 100644
index 0000000000..aae34e2433
--- /dev/null
+++ b/security/nss/lib/certdb/polcyxtn.c
@@ -0,0 +1,806 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Support for various policy related extensions
+ */
+
+#include "seccomon.h"
+#include "secport.h"
+#include "secder.h"
+#include "cert.h"
+#include "secoid.h"
+#include "secasn1.h"
+#include "secerr.h"
+#include "nspr.h"
+
+SEC_ASN1_MKSUB(SEC_IntegerTemplate)
+SEC_ASN1_MKSUB(SEC_ObjectIDTemplate)
+
+const SEC_ASN1Template CERT_DisplayTextTypeTemplate[] = {
+ { SEC_ASN1_CHOICE, offsetof(SECItem, type), 0, sizeof(SECItem) },
+ { SEC_ASN1_IA5_STRING, 0, 0, siAsciiString },
+ { SEC_ASN1_VISIBLE_STRING, 0, 0, siVisibleString },
+ { SEC_ASN1_BMP_STRING, 0, 0, siBMPString },
+ { SEC_ASN1_UTF8_STRING, 0, 0, siUTF8String },
+ { 0 }
+};
+
+const SEC_ASN1Template CERT_NoticeReferenceTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNoticeReference) },
+ { SEC_ASN1_INLINE, offsetof(CERTNoticeReference, organization),
+ CERT_DisplayTextTypeTemplate, 0 },
+ { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN,
+ offsetof(CERTNoticeReference, noticeNumbers),
+ SEC_ASN1_SUB(SEC_IntegerTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template CERT_UserNoticeTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTUserNotice) },
+ { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL,
+ offsetof(CERTUserNotice, noticeReference), CERT_NoticeReferenceTemplate,
+ 0 },
+ { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL,
+ offsetof(CERTUserNotice, displayText), CERT_DisplayTextTypeTemplate, 0 },
+ { 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 | SEC_ASN1_OPTIONAL,
+ offsetof(CERTPolicyInfo, policyQualifiers),
+ CERT_PolicyQualifierTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template CERT_CertificatePoliciesTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF, offsetof(CERTCertificatePolicies, policyInfos),
+ CERT_PolicyInfoTemplate, sizeof(CERTCertificatePolicies) }
+};
+
+const SEC_ASN1Template CERT_PolicyMapTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTPolicyMap) },
+ { SEC_ASN1_OBJECT_ID, offsetof(CERTPolicyMap, issuerDomainPolicy) },
+ { SEC_ASN1_OBJECT_ID, offsetof(CERTPolicyMap, subjectDomainPolicy) },
+ { 0 }
+};
+
+const SEC_ASN1Template CERT_PolicyMappingsTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF, offsetof(CERTCertificatePolicyMappings, policyMaps),
+ CERT_PolicyMapTemplate, sizeof(CERTPolicyMap) }
+};
+
+const SEC_ASN1Template CERT_PolicyConstraintsTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertificatePolicyConstraints) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(CERTCertificatePolicyConstraints, explicitPolicySkipCerts),
+ SEC_ASN1_SUB(SEC_IntegerTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
+ offsetof(CERTCertificatePolicyConstraints, inhibitMappingSkipCerts),
+ SEC_ASN1_SUB(SEC_IntegerTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template CERT_InhibitAnyTemplate[] = {
+ { SEC_ASN1_INTEGER,
+ offsetof(CERTCertificateInhibitAny, inhibitAnySkipCerts), NULL,
+ sizeof(CERTCertificateInhibitAny) }
+};
+
+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(const SECItem *extnValue)
+{
+ PLArenaPool *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 certificate 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 && *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;
+}
+
+CERTCertificatePolicyMappings *
+CERT_DecodePolicyMappingsExtension(SECItem *extnValue)
+{
+ PLArenaPool *arena = NULL;
+ SECStatus rv;
+ CERTCertificatePolicyMappings *mappings;
+ SECItem newExtnValue;
+
+ /* make a new arena */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena) {
+ goto loser;
+ }
+
+ /* allocate the policy mappings structure */
+ mappings = (CERTCertificatePolicyMappings *)PORT_ArenaZAlloc(
+ arena, sizeof(CERTCertificatePolicyMappings));
+ if (mappings == NULL) {
+ goto loser;
+ }
+ mappings->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 mappings */
+ rv = SEC_QuickDERDecodeItem(arena, mappings, CERT_PolicyMappingsTemplate,
+ &newExtnValue);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ return (mappings);
+
+loser:
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return (NULL);
+}
+
+SECStatus
+CERT_DestroyPolicyMappingsExtension(CERTCertificatePolicyMappings *mappings)
+{
+ if (mappings != NULL) {
+ PORT_FreeArena(mappings->arena, PR_FALSE);
+ }
+ return SECSuccess;
+}
+
+SECStatus
+CERT_DecodePolicyConstraintsExtension(
+ CERTCertificatePolicyConstraints *decodedValue, const SECItem *encodedValue)
+{
+ CERTCertificatePolicyConstraints decodeContext;
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECSuccess;
+
+ /* initialize so we can tell when an optional component is omitted */
+ PORT_Memset(&decodeContext, 0, sizeof(decodeContext));
+
+ /* make a new arena */
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (!arena) {
+ return SECFailure;
+ }
+
+ do {
+ /* decode the policy constraints */
+ rv = SEC_QuickDERDecodeItem(arena, &decodeContext,
+ CERT_PolicyConstraintsTemplate,
+ encodedValue);
+
+ if (rv != SECSuccess) {
+ break;
+ }
+
+ if (decodeContext.explicitPolicySkipCerts.len == 0) {
+ *(PRInt32 *)decodedValue->explicitPolicySkipCerts.data = -1;
+ } else {
+ *(PRInt32 *)decodedValue->explicitPolicySkipCerts.data =
+ DER_GetInteger(&decodeContext.explicitPolicySkipCerts);
+ }
+
+ if (decodeContext.inhibitMappingSkipCerts.len == 0) {
+ *(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data = -1;
+ } else {
+ *(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data =
+ DER_GetInteger(&decodeContext.inhibitMappingSkipCerts);
+ }
+
+ if ((*(PRInt32 *)decodedValue->explicitPolicySkipCerts.data ==
+ PR_INT32_MIN) ||
+ (*(PRInt32 *)decodedValue->explicitPolicySkipCerts.data ==
+ PR_INT32_MAX) ||
+ (*(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data ==
+ PR_INT32_MIN) ||
+ (*(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data ==
+ PR_INT32_MAX)) {
+ rv = SECFailure;
+ }
+
+ } while (0);
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return (rv);
+}
+
+SECStatus
+CERT_DecodeInhibitAnyExtension(CERTCertificateInhibitAny *decodedValue,
+ SECItem *encodedValue)
+{
+ CERTCertificateInhibitAny decodeContext;
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECSuccess;
+
+ /* make a new arena */
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (!arena) {
+ return SECFailure;
+ }
+
+ do {
+
+ /* decode the policy mappings */
+ decodeContext.inhibitAnySkipCerts.type = siUnsignedInteger;
+ rv = SEC_QuickDERDecodeItem(arena, &decodeContext,
+ CERT_InhibitAnyTemplate, encodedValue);
+
+ if (rv != SECSuccess) {
+ break;
+ }
+
+ *(PRInt32 *)decodedValue->inhibitAnySkipCerts.data =
+ DER_GetInteger(&decodeContext.inhibitAnySkipCerts);
+
+ } while (0);
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return (rv);
+}
+
+CERTUserNotice *
+CERT_DecodeUserNotice(SECItem *noticeItem)
+{
+ PLArenaPool *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) {
+
+ rv = SEC_QuickDERDecodeItem(arena, &userNotice->noticeReference,
+ CERT_NoticeReferenceTemplate,
+ &userNotice->derNoticeReference);
+ if (rv == SECFailure) {
+ 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 && *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 | SEC_ASN1_XTRN, offsetof(CERTOidSequence, oids),
+ SEC_ASN1_SUB(SEC_ObjectIDTemplate) }
+};
+
+CERTOidSequence *
+CERT_DecodeOidSequence(const SECItem *seqItem)
+{
+ PLArenaPool *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:
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ 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);
+}
+
+SECStatus
+CERT_EncodePolicyConstraintsExtension(PLArenaPool *arena,
+ CERTCertificatePolicyConstraints *constr,
+ SECItem *dest)
+{
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(constr != NULL && dest != NULL);
+ if (constr == NULL || dest == NULL) {
+ return SECFailure;
+ }
+
+ if (SEC_ASN1EncodeItem(arena, dest, constr,
+ CERT_PolicyConstraintsTemplate) == NULL) {
+ rv = SECFailure;
+ }
+ return (rv);
+}
+
+SECStatus
+CERT_EncodePolicyMappingExtension(PLArenaPool *arena,
+ CERTCertificatePolicyMappings *mapping,
+ SECItem *dest)
+{
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(mapping != NULL && dest != NULL);
+ if (mapping == NULL || dest == NULL) {
+ return SECFailure;
+ }
+
+ if (SEC_ASN1EncodeItem(arena, dest, mapping, CERT_PolicyMappingsTemplate) ==
+ NULL) {
+ rv = SECFailure;
+ }
+ return (rv);
+}
+
+SECStatus
+CERT_EncodeCertPoliciesExtension(PLArenaPool *arena, CERTPolicyInfo **info,
+ SECItem *dest)
+{
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(info != NULL && dest != NULL);
+ if (info == NULL || dest == NULL) {
+ return SECFailure;
+ }
+
+ if (SEC_ASN1EncodeItem(arena, dest, info,
+ CERT_CertificatePoliciesTemplate) == NULL) {
+ rv = SECFailure;
+ }
+ return (rv);
+}
+
+SECStatus
+CERT_EncodeUserNotice(PLArenaPool *arena, CERTUserNotice *notice, SECItem *dest)
+{
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(notice != NULL && dest != NULL);
+ if (notice == NULL || dest == NULL) {
+ return SECFailure;
+ }
+
+ if (SEC_ASN1EncodeItem(arena, dest, notice, CERT_UserNoticeTemplate) ==
+ NULL) {
+ rv = SECFailure;
+ }
+
+ return (rv);
+}
+
+SECStatus
+CERT_EncodeNoticeReference(PLArenaPool *arena, CERTNoticeReference *reference,
+ SECItem *dest)
+{
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(reference != NULL && dest != NULL);
+ if (reference == NULL || dest == NULL) {
+ return SECFailure;
+ }
+
+ if (SEC_ASN1EncodeItem(arena, dest, reference,
+ CERT_NoticeReferenceTemplate) == NULL) {
+ rv = SECFailure;
+ }
+
+ return (rv);
+}
+
+SECStatus
+CERT_EncodeInhibitAnyExtension(PLArenaPool *arena,
+ CERTCertificateInhibitAny *certInhibitAny,
+ SECItem *dest)
+{
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(certInhibitAny != NULL && dest != NULL);
+ if (certInhibitAny == NULL || dest == NULL) {
+ return SECFailure;
+ }
+
+ if (SEC_ASN1EncodeItem(arena, dest, certInhibitAny,
+ CERT_InhibitAnyTemplate) == NULL) {
+ rv = SECFailure;
+ }
+ return (rv);
+}
diff --git a/security/nss/lib/certdb/secname.c b/security/nss/lib/certdb/secname.c
new file mode 100644
index 0000000000..654dfdf3f0
--- /dev/null
+++ b/security/nss/lib/certdb/secname.c
@@ -0,0 +1,701 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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"
+#include "certi.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(PLArenaPool *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;
+}
+
+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(PLArenaPool *arena, SECOidTag type, SECItem *it, unsigned *maxLenp)
+{
+ unsigned char *oid;
+ unsigned oidLen;
+ unsigned char *cp;
+ int maxLen;
+ SECOidData *oidrec;
+
+ oidrec = SECOID_FindOIDByTag(type);
+ if (oidrec == NULL)
+ return SECFailure;
+
+ oid = oidrec->oid.data;
+ oidLen = oidrec->oid.len;
+
+ maxLen = cert_AVAOidTagToMaxLen(type);
+ if (maxLen < 0) {
+ 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 = (unsigned)maxLen;
+ return SECSuccess;
+}
+
+static SECStatus
+SetupAVAValue(PLArenaPool *arena, int valueType, const SECItem *in,
+ SECItem *out, unsigned maxLen)
+{
+ PRUint8 *value, *cp, *ucs4Val;
+ unsigned valueLen, valueLenLen, total;
+ unsigned ucs4Len = 0, ucs4MaxLen;
+
+ value = in->data;
+ valueLen = in->len;
+ switch (valueType) {
+ case SEC_ASN1_PRINTABLE_STRING:
+ case SEC_ASN1_IA5_STRING:
+ case SEC_ASN1_T61_STRING:
+ case SEC_ASN1_UTF8_STRING: /* no conversion required */
+ break;
+ case SEC_ASN1_UNIVERSAL_STRING:
+ ucs4MaxLen = valueLen * 6;
+ ucs4Val = (PRUint8 *)PORT_ArenaZAlloc(arena, ucs4MaxLen);
+ if (!ucs4Val ||
+ !PORT_UCS4_UTF8Conversion(PR_TRUE, value, valueLen, ucs4Val,
+ ucs4MaxLen, &ucs4Len)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ value = ucs4Val;
+ valueLen = ucs4Len;
+ maxLen *= 4;
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (valueLen > maxLen) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ valueLenLen = DER_LengthLength(valueLen);
+ total = 1 + valueLenLen + valueLen;
+ cp = (PRUint8 *)PORT_ArenaAlloc(arena, total);
+ if (!cp) {
+ return SECFailure;
+ }
+ out->data = cp;
+ out->len = total;
+ cp = (PRUint8 *)DER_StoreHeader(cp, valueType, valueLen);
+ PORT_Memcpy(cp, value, valueLen);
+ return SECSuccess;
+}
+
+CERTAVA *
+CERT_CreateAVAFromRaw(PLArenaPool *pool, const SECItem *OID,
+ const SECItem *value)
+{
+ CERTAVA *ava;
+ int rv;
+
+ ava = PORT_ArenaZNew(pool, CERTAVA);
+ if (ava) {
+ rv = SECITEM_CopyItem(pool, &ava->type, OID);
+ if (rv)
+ return NULL;
+
+ rv = SECITEM_CopyItem(pool, &ava->value, value);
+ if (rv)
+ return NULL;
+ }
+ return ava;
+}
+
+CERTAVA *
+CERT_CreateAVAFromSECItem(PLArenaPool *arena, SECOidTag kind, int valueType,
+ SECItem *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 NULL;
+ }
+ rv = SetupAVAValue(arena, valueType, value, &ava->value, maxLen);
+ if (rv) {
+ /* Illegal value type */
+ return NULL;
+ }
+ }
+ return ava;
+}
+
+CERTAVA *
+CERT_CreateAVA(PLArenaPool *arena, SECOidTag kind, int valueType, char *value)
+{
+ SECItem item = { siBuffer, NULL, 0 };
+
+ item.data = (PRUint8 *)value;
+ item.len = PORT_Strlen(value);
+
+ return CERT_CreateAVAFromSECItem(arena, kind, valueType, &item);
+}
+
+CERTAVA *
+CERT_CopyAVA(PLArenaPool *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;
+}
+
+CERTRDN *
+CERT_CreateRDN(PLArenaPool *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(PLArenaPool *arena, CERTRDN *rdn, CERTAVA *ava)
+{
+ rdn->avas = (CERTAVA **)AddToArray(arena, (void **)rdn->avas, ava);
+ return rdn->avas ? SECSuccess : SECFailure;
+}
+
+SECStatus
+CERT_CopyRDN(PLArenaPool *arena, CERTRDN *to, CERTRDN *from)
+{
+ CERTAVA **avas, *fava, *tava;
+ SECStatus rv = SECSuccess;
+
+ /* Copy each ava from from */
+ avas = from->avas;
+ if (avas) {
+ if (avas[0] == NULL) {
+ rv = CERT_AddAVA(arena, to, NULL);
+ return rv;
+ }
+ while ((fava = *avas++) != 0) {
+ tava = CERT_CopyAVA(arena, fava);
+ if (!tava) {
+ rv = SECFailure;
+ break;
+ }
+ rv = CERT_AddAVA(arena, to, tava);
+ if (rv != SECSuccess)
+ break;
+ }
+ }
+ return rv;
+}
+
+/************************************************************************/
+
+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;
+ PLArenaPool *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) {
+ PLArenaPool *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(PLArenaPool *arena, CERTName *to, const CERTName *from)
+{
+ CERTRDN **rdns, *frdn, *trdn;
+ SECStatus rv = SECSuccess;
+
+ if (!to || !from) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ CERT_DestroyName(to);
+ to->arena = arena;
+
+ /* Copy each rdn from from */
+ rdns = from->rdns;
+ if (rdns) {
+ if (rdns[0] == NULL) {
+ rv = CERT_AddRDN(to, NULL);
+ return rv;
+ }
+ while ((frdn = *rdns++) != NULL) {
+ trdn = CERT_CreateRDN(arena, NULL);
+ if (!trdn) {
+ rv = SECFailure;
+ break;
+ }
+ rv = CERT_CopyRDN(arena, trdn, frdn);
+ if (rv != SECSuccess)
+ break;
+ rv = CERT_AddRDN(to, trdn);
+ if (rv != SECSuccess)
+ break;
+ }
+ }
+ return rv;
+}
+
+/************************************************************************/
+
+static void
+canonicalize(SECItem *foo)
+{
+ int ch, lastch, len, src, dest;
+
+ /* strip trailing whitespace. */
+ len = foo->len;
+ while (len > 0 && ((ch = foo->data[len - 1]) == ' ' || ch == '\t' ||
+ ch == '\r' || ch == '\n')) {
+ len--;
+ }
+
+ src = 0;
+ /* strip leading whitespace. */
+ while (src < len && ((ch = foo->data[src]) == ' ' || ch == '\t' ||
+ ch == '\r' || ch == '\n')) {
+ src++;
+ }
+ dest = 0;
+ lastch = ' ';
+ while (src < len) {
+ ch = foo->data[src++];
+ if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
+ ch = ' ';
+ if (ch == lastch)
+ continue;
+ } else if (ch >= 'A' && ch <= 'Z') {
+ ch |= 0x20; /* downshift */
+ }
+ foo->data[dest++] = lastch = ch;
+ }
+ foo->len = dest;
+}
+
+/* SECItems a and b contain DER-encoded printable strings. */
+SECComparison
+CERT_CompareDERPrintableStrings(const SECItem *a, const SECItem *b)
+{
+ SECComparison rv = SECLessThan;
+ SECItem *aVal = CERT_DecodeAVAValue(a);
+ SECItem *bVal = CERT_DecodeAVAValue(b);
+
+ if (aVal && aVal->len && aVal->data && bVal && bVal->len && bVal->data) {
+ canonicalize(aVal);
+ canonicalize(bVal);
+ rv = SECITEM_CompareItem(aVal, bVal);
+ }
+ SECITEM_FreeItem(aVal, PR_TRUE);
+ SECITEM_FreeItem(bVal, PR_TRUE);
+ return rv;
+}
+
+SECComparison
+CERT_CompareAVA(const CERTAVA *a, const CERTAVA *b)
+{
+ SECComparison rv;
+
+ rv = SECITEM_CompareItem(&a->type, &b->type);
+ if (SECEqual != rv)
+ return rv; /* Attribute types don't match. */
+ /* Let's be optimistic. Maybe the values will just compare equal. */
+ rv = SECITEM_CompareItem(&a->value, &b->value);
+ if (SECEqual == rv)
+ return rv; /* values compared exactly. */
+ if (a->value.len && a->value.data && b->value.len && b->value.data) {
+ /* Here, the values did not match.
+ ** If the values had different encodings, convert them to the same
+ ** encoding and compare that way.
+ */
+ if (a->value.data[0] != b->value.data[0]) {
+ /* encodings differ. Convert both to UTF-8 and compare. */
+ SECItem *aVal = CERT_DecodeAVAValue(&a->value);
+ SECItem *bVal = CERT_DecodeAVAValue(&b->value);
+ if (aVal && aVal->len && aVal->data && bVal && bVal->len &&
+ bVal->data) {
+ rv = SECITEM_CompareItem(aVal, bVal);
+ }
+ SECITEM_FreeItem(aVal, PR_TRUE);
+ SECITEM_FreeItem(bVal, PR_TRUE);
+ } else if (a->value.data[0] == 0x13) { /* both are printable strings. */
+ /* printable strings */
+ rv = CERT_CompareDERPrintableStrings(&a->value, &b->value);
+ }
+ }
+ return rv;
+}
+
+SECComparison
+CERT_CompareRDN(const CERTRDN *a, const 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;
+
+ while (NULL != (aava = *aavas++)) {
+ for (bavas = b->avas; NULL != (bava = *bavas++);) {
+ rv = SECITEM_CompareItem(&aava->type, &bava->type);
+ if (SECEqual == rv) {
+ rv = CERT_CompareAVA(aava, bava);
+ if (SECEqual != rv)
+ return rv;
+ break;
+ }
+ }
+ if (!bava) /* didn't find a match */
+ return SECGreaterThan;
+ }
+ return rv;
+}
+
+SECComparison
+CERT_CompareName(const CERTName *a, const CERTName *b)
+{
+ CERTRDN **ardns;
+ CERTRDN **brdns;
+ 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;
+
+ while (rv == SECEqual && *ardns) {
+ rv = CERT_CompareRDN(*ardns++, *brdns++);
+ }
+ return rv;
+}
+
+/* Moved from certhtml.c */
+SECItem *
+CERT_DecodeAVAValue(const SECItem *derAVAValue)
+{
+ SECItem *retItem;
+ const SEC_ASN1Template *theTemplate = NULL;
+ enum { conv_none,
+ conv_ucs4,
+ conv_ucs2,
+ conv_iso88591 } convert = conv_none;
+ SECItem avaValue = { siBuffer, 0 };
+ PORTCheapArenaPool tmpArena;
+
+ if (!derAVAValue || !derAVAValue->len || !derAVAValue->data) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ switch (derAVAValue->data[0]) {
+ case SEC_ASN1_UNIVERSAL_STRING:
+ convert = conv_ucs4;
+ theTemplate = SEC_ASN1_GET(SEC_UniversalStringTemplate);
+ break;
+ case SEC_ASN1_IA5_STRING:
+ theTemplate = SEC_ASN1_GET(SEC_IA5StringTemplate);
+ break;
+ case SEC_ASN1_PRINTABLE_STRING:
+ theTemplate = SEC_ASN1_GET(SEC_PrintableStringTemplate);
+ break;
+ case SEC_ASN1_T61_STRING:
+ /*
+ * Per common practice, we're not decoding actual T.61, but instead
+ * treating T61-labeled strings as containing ISO-8859-1.
+ */
+ convert = conv_iso88591;
+ theTemplate = SEC_ASN1_GET(SEC_T61StringTemplate);
+ break;
+ case SEC_ASN1_BMP_STRING:
+ convert = conv_ucs2;
+ theTemplate = SEC_ASN1_GET(SEC_BMPStringTemplate);
+ break;
+ case SEC_ASN1_UTF8_STRING:
+ /* No conversion needed ! */
+ theTemplate = SEC_ASN1_GET(SEC_UTF8StringTemplate);
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_AVA);
+ return NULL;
+ }
+
+ PORT_Memset(&avaValue, 0, sizeof(SECItem));
+ PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+ if (SEC_QuickDERDecodeItem(&tmpArena.arena, &avaValue, theTemplate,
+ derAVAValue) != SECSuccess) {
+ PORT_DestroyCheapArena(&tmpArena);
+ return NULL;
+ }
+
+ if (convert != conv_none) {
+ unsigned int utf8ValLen = avaValue.len * 3;
+ unsigned char *utf8Val =
+ (unsigned char *)PORT_ArenaZAlloc(&tmpArena.arena, utf8ValLen);
+
+ switch (convert) {
+ case conv_ucs4:
+ if (avaValue.len % 4 != 0 ||
+ !PORT_UCS4_UTF8Conversion(PR_FALSE, avaValue.data,
+ avaValue.len, utf8Val, utf8ValLen,
+ &utf8ValLen)) {
+ PORT_DestroyCheapArena(&tmpArena);
+ PORT_SetError(SEC_ERROR_INVALID_AVA);
+ return NULL;
+ }
+ break;
+ case conv_ucs2:
+ if (avaValue.len % 2 != 0 ||
+ !PORT_UCS2_UTF8Conversion(PR_FALSE, avaValue.data,
+ avaValue.len, utf8Val, utf8ValLen,
+ &utf8ValLen)) {
+ PORT_DestroyCheapArena(&tmpArena);
+ PORT_SetError(SEC_ERROR_INVALID_AVA);
+ return NULL;
+ }
+ break;
+ case conv_iso88591:
+ if (!PORT_ISO88591_UTF8Conversion(avaValue.data, avaValue.len,
+ utf8Val, utf8ValLen,
+ &utf8ValLen)) {
+ PORT_DestroyCheapArena(&tmpArena);
+ PORT_SetError(SEC_ERROR_INVALID_AVA);
+ return NULL;
+ }
+ break;
+ case conv_none:
+ PORT_Assert(0); /* not reached */
+ break;
+ }
+
+ avaValue.data = utf8Val;
+ avaValue.len = utf8ValLen;
+ }
+
+ retItem = SECITEM_DupItem(&avaValue);
+ PORT_DestroyCheapArena(&tmpArena);
+ return retItem;
+}
diff --git a/security/nss/lib/certdb/stanpcertdb.c b/security/nss/lib/certdb/stanpcertdb.c
new file mode 100644
index 0000000000..e255df1053
--- /dev/null
+++ b/security/nss/lib/certdb/stanpcertdb.c
@@ -0,0 +1,1137 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prtime.h"
+
+#include "cert.h"
+#include "certi.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 "base64.h"
+#include "sechash.h"
+#include "plhash.h"
+#include "pk11func.h" /* sigh */
+
+#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"
+#include "secmodi.h"
+
+extern void CERT_MaybeLockCertTempPerm(const CERTCertificate *cert);
+extern void CERT_MaybeUnlockCertTempPerm(const CERTCertificate *cert);
+
+PRBool
+SEC_CertNicknameConflict(const char *nickname, const 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);
+ CERTCertTrust *certTrust;
+
+ if (c == NULL) {
+ /* error code is set */
+ return SECFailure;
+ }
+
+ certTrust = nssTrust_GetCERTCertTrustForCert(c, cert);
+ if (certTrust) {
+ NSSTrust *nssTrust = nssTrustDomain_FindTrustForCertificate(td, c);
+ if (nssTrust) {
+ nssrv = STAN_DeleteCertTrustMatchingSlot(c);
+ if (nssrv != PR_SUCCESS) {
+ CERT_MapStanError();
+ }
+ /* This call always returns PR_SUCCESS! */
+ (void)nssTrust_Destroy(nssTrust);
+ }
+ }
+
+ /* 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(const CERTCertificate *cert, CERTCertTrust *trust)
+{
+ SECStatus rv;
+ CERT_LockCertTrust(cert);
+ if (!cert || cert->trust == NULL) {
+ rv = SECFailure;
+ } else {
+ *trust = *cert->trust;
+ rv = SECSuccess;
+ }
+ CERT_UnlockCertTrust(cert);
+ return (rv);
+}
+
+extern const NSSError NSS_ERROR_NO_ERROR;
+extern const NSSError NSS_ERROR_INTERNAL_ERROR;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+extern const NSSError NSS_ERROR_INVALID_POINTER;
+extern const NSSError NSS_ERROR_INVALID_ARENA;
+extern const NSSError NSS_ERROR_INVALID_ARENA_MARK;
+extern const NSSError NSS_ERROR_DUPLICATE_POINTER;
+extern const NSSError NSS_ERROR_POINTER_NOT_REGISTERED;
+extern const NSSError NSS_ERROR_TRACKER_NOT_EMPTY;
+extern const NSSError NSS_ERROR_TRACKER_NOT_INITIALIZED;
+extern const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD;
+extern const NSSError NSS_ERROR_VALUE_TOO_LARGE;
+extern const NSSError NSS_ERROR_UNSUPPORTED_TYPE;
+extern const NSSError NSS_ERROR_BUFFER_TOO_SHORT;
+extern const NSSError NSS_ERROR_INVALID_ATOB_CONTEXT;
+extern const NSSError NSS_ERROR_INVALID_BASE64;
+extern const NSSError NSS_ERROR_INVALID_BTOA_CONTEXT;
+extern const NSSError NSS_ERROR_INVALID_ITEM;
+extern const NSSError NSS_ERROR_INVALID_STRING;
+extern const NSSError NSS_ERROR_INVALID_ASN1ENCODER;
+extern const NSSError NSS_ERROR_INVALID_ASN1DECODER;
+extern const NSSError NSS_ERROR_INVALID_BER;
+extern const NSSError NSS_ERROR_INVALID_ATAV;
+extern const NSSError NSS_ERROR_INVALID_ARGUMENT;
+extern const NSSError NSS_ERROR_INVALID_UTF8;
+extern const NSSError NSS_ERROR_INVALID_NSSOID;
+extern const NSSError NSS_ERROR_UNKNOWN_ATTRIBUTE;
+extern const NSSError NSS_ERROR_NOT_FOUND;
+extern const NSSError NSS_ERROR_INVALID_PASSWORD;
+extern const NSSError NSS_ERROR_USER_CANCELED;
+extern const NSSError NSS_ERROR_MAXIMUM_FOUND;
+extern const NSSError NSS_ERROR_CERTIFICATE_ISSUER_NOT_FOUND;
+extern const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE;
+extern const NSSError NSS_ERROR_HASH_COLLISION;
+extern const NSSError NSS_ERROR_DEVICE_ERROR;
+extern const NSSError NSS_ERROR_INVALID_CERTIFICATE;
+extern const NSSError NSS_ERROR_BUSY;
+extern const NSSError NSS_ERROR_ALREADY_INITIALIZED;
+extern const NSSError NSS_ERROR_PKCS11;
+
+/* Look at the stan error stack and map it to NSS 3 errors */
+#define STAN_MAP_ERROR(x, y) \
+ else if (error == (x)) { secError = y; }
+
+/*
+ * map Stan errors into NSS errors
+ * This function examines the stan error stack and automatically sets
+ * PORT_SetError(); to the appropriate SEC_ERROR value.
+ */
+void
+CERT_MapStanError()
+{
+ PRInt32 *errorStack;
+ NSSError error, prevError;
+ int secError;
+ int i;
+
+ errorStack = NSS_GetErrorStack();
+ if (errorStack == 0) {
+ PORT_SetError(0);
+ return;
+ }
+ error = prevError = CKR_GENERAL_ERROR;
+ /* get the 'top 2' error codes from the stack */
+ for (i = 0; errorStack[i]; i++) {
+ prevError = error;
+ error = errorStack[i];
+ }
+ if (error == NSS_ERROR_PKCS11) {
+ /* map it */
+ secError = PK11_MapError(prevError);
+ }
+ STAN_MAP_ERROR(NSS_ERROR_NO_ERROR, 0)
+ STAN_MAP_ERROR(NSS_ERROR_NO_MEMORY, SEC_ERROR_NO_MEMORY)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_BASE64, SEC_ERROR_BAD_DATA)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_BER, SEC_ERROR_BAD_DER)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_ATAV, SEC_ERROR_INVALID_AVA)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_PASSWORD, SEC_ERROR_BAD_PASSWORD)
+ STAN_MAP_ERROR(NSS_ERROR_BUSY, SEC_ERROR_BUSY)
+ STAN_MAP_ERROR(NSS_ERROR_DEVICE_ERROR, SEC_ERROR_IO)
+ STAN_MAP_ERROR(NSS_ERROR_CERTIFICATE_ISSUER_NOT_FOUND,
+ SEC_ERROR_UNKNOWN_ISSUER)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_CERTIFICATE, SEC_ERROR_CERT_NOT_VALID)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_UTF8, SEC_ERROR_BAD_DATA)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_NSSOID, SEC_ERROR_BAD_DATA)
+
+ /* these are library failure for lack of a better error code */
+ STAN_MAP_ERROR(NSS_ERROR_NOT_FOUND, SEC_ERROR_LIBRARY_FAILURE)
+ STAN_MAP_ERROR(NSS_ERROR_CERTIFICATE_IN_CACHE, SEC_ERROR_LIBRARY_FAILURE)
+ STAN_MAP_ERROR(NSS_ERROR_MAXIMUM_FOUND, SEC_ERROR_LIBRARY_FAILURE)
+ STAN_MAP_ERROR(NSS_ERROR_USER_CANCELED, SEC_ERROR_LIBRARY_FAILURE)
+ STAN_MAP_ERROR(NSS_ERROR_TRACKER_NOT_INITIALIZED, SEC_ERROR_LIBRARY_FAILURE)
+ STAN_MAP_ERROR(NSS_ERROR_ALREADY_INITIALIZED, SEC_ERROR_LIBRARY_FAILURE)
+ STAN_MAP_ERROR(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD,
+ SEC_ERROR_LIBRARY_FAILURE)
+ STAN_MAP_ERROR(NSS_ERROR_HASH_COLLISION, SEC_ERROR_LIBRARY_FAILURE)
+
+ STAN_MAP_ERROR(NSS_ERROR_INTERNAL_ERROR, SEC_ERROR_LIBRARY_FAILURE)
+
+ /* these are all invalid arguments */
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_ARGUMENT, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_POINTER, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_ARENA, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_ARENA_MARK, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_DUPLICATE_POINTER, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_POINTER_NOT_REGISTERED, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_TRACKER_NOT_EMPTY, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_VALUE_TOO_LARGE, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_UNSUPPORTED_TYPE, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_BUFFER_TOO_SHORT, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_ATOB_CONTEXT, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_BTOA_CONTEXT, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_ITEM, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_STRING, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_ASN1ENCODER, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_INVALID_ASN1DECODER, SEC_ERROR_INVALID_ARGS)
+ STAN_MAP_ERROR(NSS_ERROR_UNKNOWN_ATTRIBUTE, SEC_ERROR_INVALID_ARGS)
+ else { secError = SEC_ERROR_LIBRARY_FAILURE; }
+ PORT_SetError(secError);
+}
+
+SECStatus
+CERT_ChangeCertTrust(CERTCertDBHandle *handle, CERTCertificate *cert,
+ CERTCertTrust *trust)
+{
+ SECStatus rv = SECSuccess;
+ PRStatus ret;
+
+ ret = STAN_ChangeCertTrust(cert, trust);
+ if (ret != PR_SUCCESS) {
+ rv = SECFailure;
+ CERT_MapStanError();
+ }
+ 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);
+ nssCertificateStoreTrace lockTrace = { NULL, NULL, PR_FALSE, PR_FALSE };
+ nssCertificateStoreTrace unlockTrace = { NULL, NULL, PR_FALSE, PR_FALSE };
+ SECStatus rv;
+ PRStatus ret;
+
+ if (c == NULL) {
+ CERT_MapStanError();
+ return SECFailure;
+ }
+
+ context = c->object.cryptoContext;
+ if (!context) {
+ PORT_SetError(SEC_ERROR_ADDING_CERT);
+ return SECFailure; /* wasn't a temp cert */
+ }
+ stanNick = nssCertificate_GetNickname(c, NULL);
+ if (stanNick && nickname && strcmp(nickname, stanNick) != 0) {
+ /* different: take the new nickname */
+ cert->nickname = NULL;
+ nss_ZFreeIf(stanNick);
+ stanNick = NULL;
+ }
+ if (!stanNick && nickname) {
+ /* Either there was no nickname yet, or we have a new nickname */
+ stanNick = nssUTF8_Duplicate((NSSUTF8 *)nickname, NULL);
+ } /* else: old stanNick is identical to new nickname */
+ /* Delete the temp instance */
+ nssCertificateStore_Lock(context->certStore, &lockTrace);
+ nssCertificateStore_RemoveCertLOCKED(context->certStore, c);
+ nssCertificateStore_Unlock(context->certStore, &lockTrace, &unlockTrace);
+ c->object.cryptoContext = NULL;
+
+ /* if the id has not been set explicitly yet, create one from the public
+ * key. */
+ if (c->id.data == NULL) {
+ SECItem *keyID = pk11_mkcertKeyID(cert);
+ if (keyID) {
+ nssItem_Create(c->object.arena, &c->id, keyID->len, keyID->data);
+ SECITEM_FreeItem(keyID, PR_TRUE);
+ }
+ /* if any of these failed, continue with our null c->id */
+ }
+
+ /* Import the perm instance onto the internal token */
+ slot = PK11_GetInternalKeySlot();
+ internal = PK11Slot_GetNSSToken(slot);
+ if (!internal) {
+ PK11_FreeSlot(slot);
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ return SECFailure;
+ }
+ permInstance = nssToken_ImportCertificate(
+ internal, NULL, NSSCertificateType_PKIX, &c->id, stanNick, &c->encoding,
+ &c->issuer, &c->subject, &c->serial, cert->emailAddr, PR_TRUE);
+ (void)nssToken_Destroy(internal);
+ nss_ZFreeIf(stanNick);
+ stanNick = NULL;
+ 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_LockCertTempPerm(cert);
+ cert->nssCertificate = NULL;
+ CERT_UnlockCertTempPerm(cert);
+ cert = STAN_GetCERTCertificateOrRelease(c); /* should return same pointer */
+ if (!cert) {
+ CERT_MapStanError();
+ return SECFailure;
+ }
+ CERT_LockCertTempPerm(cert);
+ cert->istemp = PR_FALSE;
+ cert->isperm = PR_TRUE;
+ CERT_UnlockCertTempPerm(cert);
+ if (!trust) {
+ return SECSuccess;
+ }
+ ret = STAN_ChangeCertTrust(cert, trust);
+ rv = SECSuccess;
+ if (ret != PR_SUCCESS) {
+ rv = SECFailure;
+ CERT_MapStanError();
+ }
+ return rv;
+}
+
+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)
+{
+ NSSCertificate *c;
+ CERTCertificate *cc;
+ NSSCertificate *tempCert = NULL;
+ 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 && handle) {
+ /* Then, see if it is already a perm cert */
+ c = NSSTrustDomain_FindCertificateByEncodedCertificate(handle,
+ &encoding);
+ }
+ if (c) {
+ /* 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 (!nssItem_Equal(&c->encoding, &encoding, NULL)) {
+ nssCertificate_Destroy(c);
+ PORT_SetError(SEC_ERROR_REUSED_ISSUER_AND_SERIAL);
+ cc = NULL;
+ } else {
+ cc = STAN_GetCERTCertificateOrRelease(c);
+ if (cc == NULL) {
+ CERT_MapStanError();
+ }
+ }
+ return cc;
+ }
+ }
+ pkio = nssPKIObject_Create(NULL, NULL, gTD, gCC, nssPKIMonitor);
+ if (!pkio) {
+ CERT_MapStanError();
+ return NULL;
+ }
+ c = nss_ZNEW(pkio->arena, NSSCertificate);
+ if (!c) {
+ CERT_MapStanError();
+ 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
+ */
+ /* 'c' is not adopted here, if we fail loser frees what has been
+ * allocated so far for 'c' */
+ cc = STAN_GetCERTCertificate(c);
+ if (!cc) {
+ CERT_MapStanError();
+ goto loser;
+ }
+ 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);
+ /* 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 && cc->emailAddr[0]) {
+ c->email = nssUTF8_Create(
+ c->object.arena, nssStringType_PrintableString,
+ (NSSUTF8 *)cc->emailAddr, PORT_Strlen(cc->emailAddr));
+ }
+
+ tempCert = NSSCryptoContext_FindOrImportCertificate(gCC, c);
+ if (!tempCert) {
+ CERT_MapStanError();
+ goto loser;
+ }
+ /* destroy our copy */
+ NSSCertificate_Destroy(c);
+ /* and use the stored entry */
+ c = tempCert;
+ cc = STAN_GetCERTCertificateOrRelease(c);
+ if (!cc) {
+ /* STAN_GetCERTCertificateOrRelease destroys c on failure. */
+ CERT_MapStanError();
+ return NULL;
+ }
+
+ CERT_LockCertTempPerm(cc);
+ cc->istemp = PR_TRUE;
+ cc->isperm = PR_FALSE;
+ CERT_UnlockCertTempPerm(cc);
+ return cc;
+loser:
+ /* Perhaps this should be nssCertificate_Destroy(c) */
+ nssPKIObject_Destroy(&c->object);
+ return NULL;
+}
+
+/* This symbol is exported for backward compatibility. */
+CERTCertificate *
+__CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert,
+ char *nickname, PRBool isperm, PRBool copyDER)
+{
+ return CERT_NewTempCertificate(handle, derCert, nickname, isperm, copyDER);
+}
+
+static CERTCertificate *
+common_FindCertByIssuerAndSN(CERTCertDBHandle *handle,
+ CERTIssuerAndSN *issuerAndSN,
+ void *wincx)
+{
+ PK11SlotInfo *slot;
+ CERTCertificate *cert;
+
+ cert = PK11_FindCertByIssuerAndSN(&slot, issuerAndSN, wincx);
+ if (cert && slot) {
+ PK11_FreeSlot(slot);
+ }
+
+ return cert;
+}
+
+/* maybe all the wincx's should be some const for internal token login? */
+CERTCertificate *
+CERT_FindCertByIssuerAndSN(CERTCertDBHandle *handle,
+ CERTIssuerAndSN *issuerAndSN)
+{
+ return common_FindCertByIssuerAndSN(handle, issuerAndSN, NULL);
+}
+
+/* maybe all the wincx's should be some const for internal token login? */
+CERTCertificate *
+CERT_FindCertByIssuerAndSNCX(CERTCertDBHandle *handle,
+ CERTIssuerAndSN *issuerAndSN,
+ void *wincx)
+{
+ return common_FindCertByIssuerAndSN(handle, issuerAndSN, wincx);
+}
+
+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) {
+ CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct));
+ }
+ if (cp) {
+ CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(cp));
+ }
+ return c ? STAN_GetCERTCertificateOrRelease(c) : NULL;
+}
+
+CERTCertificate *
+CERT_FindCertByKeyID(CERTCertDBHandle *handle, SECItem *name, SECItem *keyID)
+{
+ CERTCertList *list;
+ CERTCertificate *cert = NULL;
+ CERTCertListNode *node;
+
+ list = CERT_CreateSubjectCertList(NULL, handle, name, 0, PR_FALSE);
+ if (list == NULL)
+ return NULL;
+
+ node = CERT_LIST_HEAD(list);
+ while (!CERT_LIST_END(node, list)) {
+ if (node->cert &&
+ SECITEM_ItemsAreEqual(&node->cert->subjectKeyID, keyID)) {
+ cert = CERT_DupCertificate(node->cert);
+ goto done;
+ }
+ node = CERT_LIST_NEXT(node);
+ }
+ PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
+
+done:
+ CERT_DestroyCertList(list);
+ return cert;
+}
+
+CERTCertificate *
+CERT_FindCertByNickname(CERTCertDBHandle *handle, const 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) {
+ CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct));
+ }
+ } else {
+ c = ct;
+ }
+ return c ? STAN_GetCERTCertificateOrRelease(c) : 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_GetCERTCertificateOrRelease(c);
+}
+
+static CERTCertificate *
+common_FindCertByNicknameOrEmailAddrForUsage(CERTCertDBHandle *handle,
+ const char *name, PRBool anyUsage,
+ SECCertUsage lookingForUsage,
+ void *wincx)
+{
+ NSSCryptoContext *cc;
+ NSSCertificate *c, *ct;
+ CERTCertificate *cert = NULL;
+ NSSUsage usage;
+ CERTCertList *certlist;
+
+ if (NULL == name) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ usage.anyUsage = anyUsage;
+
+ if (!anyUsage) {
+ usage.nss3lookingForCA = PR_FALSE;
+ usage.nss3usage = lookingForUsage;
+ }
+
+ cc = STAN_GetDefaultCryptoContext();
+ ct = NSSCryptoContext_FindBestCertificateByNickname(cc, name, NULL, &usage,
+ NULL);
+ if (!ct && PORT_Strchr(name, '@') != NULL) {
+ char *lowercaseName = CERT_FixupEmailAddr(name);
+ if (lowercaseName) {
+ ct = NSSCryptoContext_FindBestCertificateByEmail(
+ cc, lowercaseName, NULL, &usage, NULL);
+ PORT_Free(lowercaseName);
+ }
+ }
+
+ if (anyUsage) {
+ cert = PK11_FindCertFromNickname(name, wincx);
+ } else {
+ if (ct) {
+ /* Does ct really have the required usage? */
+ nssDecodedCert *dc;
+ dc = nssCertificate_GetDecoding(ct);
+ if (!dc->matchUsage(dc, &usage)) {
+ CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct));
+ ct = NULL;
+ }
+ }
+
+ certlist = PK11_FindCertsFromNickname(name, wincx);
+ if (certlist) {
+ SECStatus rv =
+ CERT_FilterCertListByUsage(certlist, lookingForUsage, PR_FALSE);
+ if (SECSuccess == rv && !CERT_LIST_EMPTY(certlist)) {
+ cert = CERT_DupCertificate(CERT_LIST_HEAD(certlist)->cert);
+ }
+ CERT_DestroyCertList(certlist);
+ }
+ }
+
+ if (cert) {
+ c = get_best_temp_or_perm(ct, STAN_GetNSSCertificate(cert));
+ CERT_DestroyCertificate(cert);
+ if (ct) {
+ CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct));
+ }
+ } else {
+ c = ct;
+ }
+ return c ? STAN_GetCERTCertificateOrRelease(c) : NULL;
+}
+
+CERTCertificate *
+CERT_FindCertByNicknameOrEmailAddr(CERTCertDBHandle *handle, const char *name)
+{
+ return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_TRUE,
+ 0, NULL);
+}
+
+CERTCertificate *
+CERT_FindCertByNicknameOrEmailAddrCX(CERTCertDBHandle *handle, const char *name,
+ void *wincx)
+{
+ return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_TRUE,
+ 0, wincx);
+}
+
+CERTCertificate *
+CERT_FindCertByNicknameOrEmailAddrForUsage(CERTCertDBHandle *handle,
+ const char *name,
+ SECCertUsage lookingForUsage)
+{
+ return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_FALSE,
+ lookingForUsage, NULL);
+}
+
+CERTCertificate *
+CERT_FindCertByNicknameOrEmailAddrForUsageCX(CERTCertDBHandle *handle,
+ const char *name,
+ SECCertUsage lookingForUsage,
+ void *wincx)
+{
+ return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_FALSE,
+ lookingForUsage, wincx);
+}
+
+static void
+add_to_subject_list(CERTCertList *certList, CERTCertificate *cert,
+ PRBool validOnly, PRTime 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,
+ const SECItem *name, PRTime 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_GetCERTCertificateOrRelease(*ci);
+ /* *ci may be invalid at this point, don't reference it again */
+ if (cert) {
+ /* NOTE: add_to_subject_list adopts the incoming 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_GetCERTCertificateOrRelease(*ci);
+ /* *ci may be invalid at this point, don't reference it again */
+ if (cert) {
+ /* NOTE: add_to_subject_list adopts the incoming cert. */
+ add_to_subject_list(certList, cert, validOnly, sorttime);
+ }
+ ci++;
+ }
+ /* all the references have been adopted or freed at this point, just
+ * free the arrays now */
+ nss_ZFreeIf(tSubjectCerts);
+ nss_ZFreeIf(pSubjectCerts);
+ return certList;
+loser:
+ /* need to free the references in tSubjectCerts and pSubjectCerts! */
+ nssCertificateArray_Destroy(tSubjectCerts);
+ nssCertificateArray_Destroy(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
+ *
+ * cert->nssCertificate contains its own locks and refcount, but as it
+ * may be NULL, the pointer itself must be guarded by some other lock.
+ * Rather than creating a new global lock for only this purpose, share
+ * an existing global lock that happens to be taken near the write in
+ * fill_CERTCertificateFields(). The longer-term goal is to refactor
+ * all these global locks to be certificate-scoped. */
+ CERT_MaybeLockCertTempPerm(cert);
+ NSSCertificate *tmp = cert->nssCertificate;
+ CERT_MaybeUnlockCertTempPerm(cert);
+ if (tmp) {
+ /* delete the NSSCertificate */
+ NSSCertificate_Destroy(tmp);
+ } else if (cert->arena) {
+ PORT_FreeArena(cert->arena, PR_FALSE);
+ }
+ }
+ return;
+}
+
+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)
+{
+ PRTime oldtime;
+ PRTime 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 && profileTime && emailProfile) {
+ /* 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;
+ NSSItem profTime, profData;
+ NSSITEM_FROM_SECITEM(&profTime, profileTime);
+ NSSITEM_FROM_SECITEM(&profData, emailProfile);
+ stanProfile = nssSMIMEProfile_Create(c, &profTime, &profData);
+ 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;
+ PRBool isperm = PR_FALSE;
+
+ 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;
+ }
+ }
+
+ rv = CERT_GetCertIsPerm(cert, &isperm);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ if (cert->slot && isperm && CERT_IsUserCert(cert) &&
+ (!emailProfile || !emailProfile->len)) {
+ /* Don't clobber emailProfile for user certs. */
+ return SECSuccess;
+ }
+
+ 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;
+
+ if (!cert || !cert->emailAddr || !cert->emailAddr[0]) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return 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;
+}
+
+SECStatus
+CERT_GetCertIsPerm(const CERTCertificate *cert, PRBool *isperm)
+{
+ if (cert == NULL) {
+ return SECFailure;
+ }
+
+ CERT_LockCertTempPerm(cert);
+ *isperm = cert->isperm;
+ CERT_UnlockCertTempPerm(cert);
+ return SECSuccess;
+}
+
+SECStatus
+CERT_GetCertIsTemp(const CERTCertificate *cert, PRBool *istemp)
+{
+ if (cert == NULL) {
+ return SECFailure;
+ }
+
+ CERT_LockCertTempPerm(cert);
+ *istemp = cert->istemp;
+ CERT_UnlockCertTempPerm(cert);
+ return SECSuccess;
+}
+
+/*
+ * deprecated functions that are now just stubs.
+ */
+/*
+ * Close the database
+ */
+void
+__CERT_ClosePermCertDB(CERTCertDBHandle *handle)
+{
+ PORT_Assert("CERT_ClosePermCertDB is Deprecated" == NULL);
+ return;
+}
+
+SECStatus
+CERT_OpenCertDBFilename(CERTCertDBHandle *handle, char *certdbname,
+ PRBool readOnly)
+{
+ PORT_Assert("CERT_OpenCertDBFilename is Deprecated" == NULL);
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return SECFailure;
+}
+
+SECItem *
+SECKEY_HashPassword(char *pw, SECItem *salt)
+{
+ PORT_Assert("SECKEY_HashPassword is Deprecated" == NULL);
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return NULL;
+}
+
+SECStatus
+__CERT_TraversePermCertsForSubject(CERTCertDBHandle *handle,
+ SECItem *derSubject, void *cb, void *cbarg)
+{
+ PORT_Assert("CERT_TraversePermCertsForSubject is Deprecated" == NULL);
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return SECFailure;
+}
+
+SECStatus
+__CERT_TraversePermCertsForNickname(CERTCertDBHandle *handle, char *nickname,
+ void *cb, void *cbarg)
+{
+ PORT_Assert("CERT_TraversePermCertsForNickname is Deprecated" == NULL);
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return SECFailure;
+}
diff --git a/security/nss/lib/certdb/xauthkid.c b/security/nss/lib/certdb/xauthkid.c
new file mode 100644
index 0000000000..c7ef046db0
--- /dev/null
+++ b/security/nss/lib/certdb/xauthkid.c
@@ -0,0 +1,128 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * X.509 v3 Subject Key Usage Extension
+ *
+ */
+
+#include "prtypes.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"
+
+SEC_ASN1_MKSUB(SEC_IntegerTemplate)
+SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
+
+const SEC_ASN1Template CERTAuthKeyIDTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTAuthKeyID) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(CERTAuthKeyID, keyID), SEC_ASN1_SUB(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 | SEC_ASN1_XTRN | 2,
+ offsetof(CERTAuthKeyID, authCertSerialNumber),
+ SEC_ASN1_SUB(SEC_IntegerTemplate) },
+ { 0 }
+};
+
+SECStatus
+CERT_EncodeAuthKeyID(PLArenaPool *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(PLArenaPool *arena, const 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));
+ if (value == NULL)
+ break;
+ value->DERAuthCertIssuer = NULL;
+ /* 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 0000000000..df70dbbefb
--- /dev/null
+++ b/security/nss/lib/certdb/xbsconst.c
@@ -0,0 +1,146 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * X.509 v3 Basic Constraints Extension
+ */
+
+#include "prtypes.h"
+#include <limits.h> /* for LONG_MAX */
+#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;
+ PLArenaPool *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(PLArenaPool *arena, CERTBasicConstraints *value,
+ SECItem *encodedValue)
+{
+ EncodedContext encodeContext;
+ PLArenaPool *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,
+ const SECItem *encodedValue)
+{
+ EncodedContext decodeContext;
+ PORTCheapArenaPool tmpArena;
+ 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;
+
+ PORT_InitCheapArena(&tmpArena, SEC_ASN1_DEFAULT_ARENA_SIZE);
+
+ rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &decodeContext,
+ CERTBasicConstraintsTemplate, encodedValue);
+ if (rv == SECFailure)
+ break;
+
+ value->isCA = decodeContext.isCA.data
+ ? (PRBool)(decodeContext.isCA.data[0] != 0)
+ : PR_FALSE;
+ 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 {
+ value->pathLenConstraint = 0;
+ }
+ } else if (value->isCA) {
+ long len = DER_GetInteger(&decodeContext.pathLenConstraint);
+ if (len < 0 || len == LONG_MAX) {
+ PORT_SetError(SEC_ERROR_BAD_DER);
+ GEN_BREAK(SECFailure);
+ }
+ value->pathLenConstraint = len;
+ } 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_DestroyCheapArena(&tmpArena);
+ return (rv);
+}
diff --git a/security/nss/lib/certdb/xconst.c b/security/nss/lib/certdb/xconst.c
new file mode 100644
index 0000000000..9a5634a906
--- /dev/null
+++ b/security/nss/lib/certdb/xconst.c
@@ -0,0 +1,269 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * X.509 Extension Encoding
+ */
+
+#include "prtypes.h"
+#include "seccomon.h"
+#include "secdert.h"
+#include "secoidt.h"
+#include "secasn1t.h"
+#include "secasn1.h"
+#include "cert.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 }
+};
+
+SEC_ASN1_MKSUB(SEC_GeneralizedTimeTemplate)
+
+static const SEC_ASN1Template CERTPrivateKeyUsagePeriodTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTPrivKeyUsagePeriod) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(CERTPrivKeyUsagePeriod, notBefore),
+ SEC_ASN1_SUB(SEC_GeneralizedTimeTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
+ offsetof(CERTPrivKeyUsagePeriod, notAfter),
+ SEC_ASN1_SUB(SEC_GeneralizedTimeTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template CERTAltNameTemplate[] = {
+ { SEC_ASN1_CONSTRUCTED, offsetof(CERTAltNameEncodedContext, 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(PLArenaPool *arena, const SECItem *srcString,
+ SECItem *encodedValue)
+{
+ SECStatus rv = SECSuccess;
+
+ if (!srcString) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (SEC_ASN1EncodeItem(arena, encodedValue, srcString,
+ CERTSubjectKeyIDTemplate) == NULL) {
+ rv = SECFailure;
+ }
+
+ return (rv);
+}
+
+SECStatus
+CERT_EncodePrivateKeyUsagePeriod(PLArenaPool *arena,
+ CERTPrivKeyUsagePeriod *pkup,
+ SECItem *encodedValue)
+{
+ SECStatus rv = SECSuccess;
+
+ if (SEC_ASN1EncodeItem(arena, encodedValue, pkup,
+ CERTPrivateKeyUsagePeriodTemplate) == NULL) {
+ rv = SECFailure;
+ }
+ return (rv);
+}
+
+CERTPrivKeyUsagePeriod *
+CERT_DecodePrivKeyUsagePeriodExtension(PLArenaPool *arena, SECItem *extnValue)
+{
+ SECStatus rv;
+ CERTPrivKeyUsagePeriod *pPeriod;
+ SECItem newExtnValue;
+
+ /* allocate the certificate policies structure */
+ pPeriod = PORT_ArenaZNew(arena, CERTPrivKeyUsagePeriod);
+ if (pPeriod == NULL) {
+ goto loser;
+ }
+
+ pPeriod->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;
+ }
+
+ rv = SEC_QuickDERDecodeItem(
+ arena, pPeriod, CERTPrivateKeyUsagePeriodTemplate, &newExtnValue);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ return pPeriod;
+
+loser:
+ return NULL;
+}
+
+SECStatus
+CERT_EncodeIA5TypeExtension(PLArenaPool *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(PLArenaPool *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(PLArenaPool *reqArena, SECItem *EncodedAltName)
+{
+ SECStatus rv = SECSuccess;
+ CERTAltNameEncodedContext encodedContext;
+ SECItem *newEncodedAltName;
+
+ if (!reqArena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ newEncodedAltName = SECITEM_ArenaDupItem(reqArena, EncodedAltName);
+ if (!newEncodedAltName) {
+ return NULL;
+ }
+
+ encodedContext.encodedGenName = NULL;
+ PORT_Memset(&encodedContext, 0, sizeof(CERTAltNameEncodedContext));
+ rv = SEC_QuickDERDecodeItem(reqArena, &encodedContext,
+ CERT_GeneralNamesTemplate, newEncodedAltName);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+ if (encodedContext.encodedGenName && encodedContext.encodedGenName[0])
+ return cert_DecodeGeneralNames(reqArena, 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(PLArenaPool *arena,
+ CERTNameConstraints *value,
+ SECItem *encodedValue)
+{
+ SECStatus rv = SECSuccess;
+
+ rv = cert_EncodeNameConstraints(value, arena, encodedValue);
+ return rv;
+}
+
+CERTNameConstraints *
+CERT_DecodeNameConstraintsExtension(PLArenaPool *arena,
+ const SECItem *encodedConstraints)
+{
+ return cert_DecodeNameConstraints(arena, encodedConstraints);
+}
+
+CERTAuthInfoAccess **
+CERT_DecodeAuthInfoAccessExtension(PLArenaPool *reqArena,
+ const SECItem *encodedExtension)
+{
+ CERTAuthInfoAccess **info = NULL;
+ SECStatus rv;
+ int i;
+ SECItem *newEncodedExtension;
+
+ if (!reqArena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ newEncodedExtension = SECITEM_ArenaDupItem(reqArena, encodedExtension);
+ if (!newEncodedExtension) {
+ return NULL;
+ }
+
+ rv = SEC_QuickDERDecodeItem(reqArena, &info, CERTAuthInfoAccessTemplate,
+ newEncodedExtension);
+ if (rv != SECSuccess || info == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; info[i] != NULL; i++) {
+ info[i]->location =
+ CERT_DecodeGeneralName(reqArena, &(info[i]->derLocation), NULL);
+ }
+ return info;
+}
+
+SECStatus
+CERT_EncodeInfoAccessExtension(PLArenaPool *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 0000000000..8cf2e826e0
--- /dev/null
+++ b/security/nss/lib/certdb/xconst.h
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _XCONST_H_
+#define _XCONST_H_
+
+#include "certt.h"
+
+typedef struct CERTAltNameEncodedContextStr {
+ SECItem **encodedGenName;
+} CERTAltNameEncodedContext;
+
+SEC_BEGIN_PROTOS
+
+extern SECStatus CERT_EncodePrivateKeyUsagePeriod(PLArenaPool *arena,
+ CERTPrivKeyUsagePeriod *pkup,
+ SECItem *encodedValue);
+
+extern SECStatus CERT_EncodeNameConstraintsExtension(PLArenaPool *arena,
+ CERTNameConstraints *value,
+ SECItem *encodedValue);
+
+extern SECStatus CERT_EncodeIA5TypeExtension(PLArenaPool *arena, char *value,
+ SECItem *encodedValue);
+
+SECStatus cert_EncodeAuthInfoAccessExtension(PLArenaPool *arena,
+ CERTAuthInfoAccess **info,
+ SECItem *dest);
+SEC_END_PROTOS
+#endif