summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/certhigh
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/certhigh
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/lib/certhigh')
-rw-r--r--security/nss/lib/certhigh/Makefile45
-rw-r--r--security/nss/lib/certhigh/certhigh.c1210
-rw-r--r--security/nss/lib/certhigh/certhigh.gyp31
-rw-r--r--security/nss/lib/certhigh/certhtml.c321
-rw-r--r--security/nss/lib/certhigh/certreq.c331
-rw-r--r--security/nss/lib/certhigh/certvfy.c2158
-rw-r--r--security/nss/lib/certhigh/certvfypkix.c2302
-rw-r--r--security/nss/lib/certhigh/crlv2.c160
-rw-r--r--security/nss/lib/certhigh/exports.gyp33
-rw-r--r--security/nss/lib/certhigh/manifest.mn35
-rw-r--r--security/nss/lib/certhigh/ocsp.c6119
-rw-r--r--security/nss/lib/certhigh/ocsp.h723
-rw-r--r--security/nss/lib/certhigh/ocspi.h166
-rw-r--r--security/nss/lib/certhigh/ocspsig.c597
-rw-r--r--security/nss/lib/certhigh/ocspt.h301
-rw-r--r--security/nss/lib/certhigh/ocspti.h356
-rw-r--r--security/nss/lib/certhigh/xcrldist.c212
17 files changed, 15100 insertions, 0 deletions
diff --git a/security/nss/lib/certhigh/Makefile b/security/nss/lib/certhigh/Makefile
new file mode 100644
index 0000000000..bb88772853
--- /dev/null
+++ b/security/nss/lib/certhigh/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/certhigh/certhigh.c b/security/nss/lib/certhigh/certhigh.c
new file mode 100644
index 0000000000..7ae80b193e
--- /dev/null
+++ b/security/nss/lib/certhigh/certhigh.c
@@ -0,0 +1,1210 @@
+/* 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 "nspr.h"
+#include "secerr.h"
+#include "secasn1.h"
+#include "seccomon.h"
+#include "pk11func.h"
+#include "certdb.h"
+#include "certt.h"
+#include "cert.h"
+#include "certxutl.h"
+
+#include "certi.h"
+#include "nsspki.h"
+#include "pki.h"
+#include "pkit.h"
+#include "pkitm.h"
+#include "pki3hack.h"
+
+PRBool
+CERT_MatchNickname(char *name1, char *name2)
+{
+ char *nickname1 = NULL;
+ char *nickname2 = NULL;
+ char *token1;
+ char *token2;
+
+ /* first deal with the straight comparison */
+ if (PORT_Strcmp(name1, name2) == 0) {
+ return PR_TRUE;
+ }
+ /* we need to handle the case where one name has an explicit token and the other
+ * doesn't */
+ token1 = PORT_Strchr(name1, ':');
+ token2 = PORT_Strchr(name2, ':');
+ if ((token1 && token2) || (!token1 && !token2)) {
+ /* either both token names are specified or neither are, not match */
+ return PR_FALSE;
+ }
+ if (token1) {
+ nickname1 = token1;
+ nickname2 = name2;
+ } else {
+ nickname1 = token2;
+ nickname2 = name1;
+ }
+ nickname1++;
+ if (PORT_Strcmp(nickname1, nickname2) != 0) {
+ return PR_FALSE;
+ }
+ /* Bug 1192443 - compare the other token with the internal slot here */
+ return PR_TRUE;
+}
+
+/*
+ * 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)
+{
+ CERTCertNicknames *nicknames = NULL;
+ char **nnptr;
+ int nn;
+ CERTCertificate *cert = NULL;
+ CERTCertList *certList = NULL;
+ SECStatus rv;
+ PRTime time;
+ CERTCertListNode *node = NULL;
+ CERTCertListNode *freenode = NULL;
+ int n;
+
+ time = PR_Now();
+
+ nicknames = CERT_GetCertNicknames(handle, SEC_CERT_NICKNAMES_USER,
+ proto_win);
+
+ if ((nicknames == NULL) || (nicknames->numnicknames == 0)) {
+ goto loser;
+ }
+
+ nnptr = nicknames->nicknames;
+ nn = nicknames->numnicknames;
+
+ while (nn > 0) {
+ cert = NULL;
+ /* use the pk11 call so that we pick up any certs on tokens,
+ * which may require login
+ */
+ if (proto_win != NULL) {
+ cert = PK11_FindCertFromNickname(*nnptr, proto_win);
+ }
+
+ /* Sigh, It turns out if the cert is already in the temp db, because
+ * it's in the perm db, then the nickname lookup doesn't work.
+ * since we already have the cert here, though, than we can just call
+ * CERT_CreateSubjectCertList directly. For those cases where we didn't
+ * find the cert in pkcs #11 (because we didn't have a password arg,
+ * or because the nickname is for a peer, server, or CA cert, then we
+ * go look the cert up.
+ */
+ if (cert == NULL) {
+ cert = CERT_FindCertByNickname(handle, *nnptr);
+ }
+
+ if (cert != NULL) {
+ /* collect certs for this nickname, sorting them into the list */
+ certList = CERT_CreateSubjectCertList(certList, handle,
+ &cert->derSubject, time, validOnly);
+
+ CERT_FilterCertListForUserCerts(certList);
+
+ /* drop the extra reference */
+ CERT_DestroyCertificate(cert);
+ }
+
+ nnptr++;
+ nn--;
+ }
+
+ /* remove certs with incorrect usage */
+ rv = CERT_FilterCertListByUsage(certList, usage, PR_FALSE);
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* remove any extra certs for each name */
+ if (oneCertPerName) {
+ PRBool *flags;
+
+ nn = nicknames->numnicknames;
+ nnptr = nicknames->nicknames;
+
+ if (!certList) {
+ goto loser;
+ }
+
+ flags = (PRBool *)PORT_ZAlloc(sizeof(PRBool) * nn);
+ if (flags == NULL) {
+ goto loser;
+ }
+
+ node = CERT_LIST_HEAD(certList);
+
+ /* treverse all certs in the list */
+ while (!CERT_LIST_END(node, certList)) {
+
+ /* find matching nickname index */
+ for (n = 0; n < nn; n++) {
+ if (CERT_MatchNickname(nnptr[n], node->cert->nickname)) {
+ /* We found a match. If this is the first one, then
+ * set the flag and move on to the next cert. If this
+ * is not the first one then delete it from the list.
+ */
+ if (flags[n]) {
+ /* We have already seen a cert with this nickname,
+ * so delete this one.
+ */
+ freenode = node;
+ node = CERT_LIST_NEXT(node);
+ CERT_RemoveCertListNode(freenode);
+ } else {
+ /* keep the first cert for each nickname, but set the
+ * flag so we know to delete any others with the same
+ * nickname.
+ */
+ flags[n] = PR_TRUE;
+ node = CERT_LIST_NEXT(node);
+ }
+ break;
+ }
+ }
+ if (n == nn) {
+ /* if we get here it means that we didn't find a matching
+ * nickname, which should not happen.
+ */
+ PORT_Assert(0);
+ node = CERT_LIST_NEXT(node);
+ }
+ }
+ PORT_Free(flags);
+ }
+
+ goto done;
+
+loser:
+ if (certList != NULL) {
+ CERT_DestroyCertList(certList);
+ certList = NULL;
+ }
+
+done:
+ if (nicknames != NULL) {
+ CERT_FreeNicknames(nicknames);
+ }
+
+ return (certList);
+}
+
+/*
+ * 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)
+{
+ CERTCertificate *cert = NULL;
+ CERTCertList *certList = NULL;
+ SECStatus rv;
+ PRTime time;
+
+ time = PR_Now();
+
+ /* use the pk11 call so that we pick up any certs on tokens,
+ * which may require login
+ */
+ /* XXX - why is this restricted? */
+ if (proto_win != NULL) {
+ cert = PK11_FindCertFromNickname(nickname, proto_win);
+ }
+
+ /* sigh, There are still problems find smart cards from the temp
+ * db. This will get smart cards working again. The real fix
+ * is to make sure we can search the temp db by their token nickname.
+ */
+ if (cert == NULL) {
+ cert = CERT_FindCertByNickname(handle, nickname);
+ }
+
+ if (cert != NULL) {
+ unsigned int requiredKeyUsage;
+ unsigned int requiredCertType;
+
+ rv = CERT_KeyUsageAndTypeForCertUsage(usage, PR_FALSE,
+ &requiredKeyUsage, &requiredCertType);
+ if (rv != SECSuccess) {
+ /* drop the extra reference */
+ CERT_DestroyCertificate(cert);
+ cert = NULL;
+ goto loser;
+ }
+ /* If we already found the right cert, just return it */
+ if ((!validOnly || CERT_CheckCertValidTimes(cert, time, PR_FALSE) == secCertTimeValid) &&
+ (CERT_CheckKeyUsage(cert, requiredKeyUsage) == SECSuccess) &&
+ (cert->nsCertType & requiredCertType) &&
+ CERT_IsUserCert(cert)) {
+ return (cert);
+ }
+
+ /* collect certs for this nickname, sorting them into the list */
+ certList = CERT_CreateSubjectCertList(certList, handle,
+ &cert->derSubject, time, validOnly);
+
+ CERT_FilterCertListForUserCerts(certList);
+
+ /* drop the extra reference */
+ CERT_DestroyCertificate(cert);
+ cert = NULL;
+ }
+
+ if (certList == NULL) {
+ goto loser;
+ }
+
+ /* remove certs with incorrect usage */
+ rv = CERT_FilterCertListByUsage(certList, usage, PR_FALSE);
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ if (!CERT_LIST_EMPTY(certList)) {
+ cert = CERT_DupCertificate(CERT_LIST_HEAD(certList)->cert);
+ }
+
+loser:
+ if (certList != NULL) {
+ CERT_DestroyCertList(certList);
+ }
+
+ return (cert);
+}
+
+CERTCertList *
+CERT_MatchUserCert(CERTCertDBHandle *handle,
+ SECCertUsage usage,
+ int nCANames, char **caNames,
+ void *proto_win)
+{
+ CERTCertList *certList = NULL;
+ SECStatus rv;
+
+ certList = CERT_FindUserCertsByUsage(handle, usage, PR_TRUE, PR_TRUE,
+ proto_win);
+ if (certList == NULL) {
+ goto loser;
+ }
+
+ rv = CERT_FilterCertListByCANames(certList, nCANames, caNames, usage);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ goto done;
+
+loser:
+ if (certList != NULL) {
+ CERT_DestroyCertList(certList);
+ certList = NULL;
+ }
+
+done:
+
+ return (certList);
+}
+
+typedef struct stringNode {
+ struct stringNode *next;
+ char *string;
+} stringNode;
+
+static PRStatus
+CollectNicknames(NSSCertificate *c, void *data)
+{
+ CERTCertNicknames *names;
+ PRBool saveit = PR_FALSE;
+ stringNode *node;
+ int len;
+#ifdef notdef
+ NSSTrustDomain *td;
+ NSSTrust *trust;
+#endif
+ char *stanNickname;
+ char *nickname = NULL;
+
+ names = (CERTCertNicknames *)data;
+
+ stanNickname = nssCertificate_GetNickname(c, NULL);
+
+ if (stanNickname) {
+ nss_ZFreeIf(stanNickname);
+ stanNickname = NULL;
+ if (names->what == SEC_CERT_NICKNAMES_USER) {
+ saveit = NSSCertificate_IsPrivateKeyAvailable(c, NULL, NULL);
+ }
+#ifdef notdef
+ else {
+ td = NSSCertificate_GetTrustDomain(c);
+ if (!td) {
+ return PR_SUCCESS;
+ }
+ trust = nssTrustDomain_FindTrustForCertificate(td, c);
+
+ switch (names->what) {
+ case SEC_CERT_NICKNAMES_ALL:
+ if ((trust->sslFlags & (CERTDB_VALID_CA | CERTDB_VALID_PEER)) ||
+ (trust->emailFlags & (CERTDB_VALID_CA | CERTDB_VALID_PEER)) ||
+ (trust->objectSigningFlags &
+ (CERTDB_VALID_CA | CERTDB_VALID_PEER))) {
+ saveit = PR_TRUE;
+ }
+
+ break;
+ case SEC_CERT_NICKNAMES_SERVER:
+ if (trust->sslFlags & CERTDB_VALID_PEER) {
+ saveit = PR_TRUE;
+ }
+
+ break;
+ case SEC_CERT_NICKNAMES_CA:
+ if (((trust->sslFlags & CERTDB_VALID_CA) == CERTDB_VALID_CA) ||
+ ((trust->emailFlags & CERTDB_VALID_CA) == CERTDB_VALID_CA) ||
+ ((trust->objectSigningFlags & CERTDB_VALID_CA) ==
+ CERTDB_VALID_CA)) {
+ saveit = PR_TRUE;
+ }
+ break;
+ }
+ }
+#endif
+ }
+
+ /* traverse the list of collected nicknames and make sure we don't make
+ * a duplicate
+ */
+ if (saveit) {
+ nickname = STAN_GetCERTCertificateName(NULL, c);
+ /* nickname can only be NULL here if we are having memory
+ * alloc problems */
+ if (nickname == NULL) {
+ return PR_FAILURE;
+ }
+ node = (stringNode *)names->head;
+ while (node != NULL) {
+ if (PORT_Strcmp(nickname, node->string) == 0) {
+ /* if the string matches, then don't save this one */
+ saveit = PR_FALSE;
+ break;
+ }
+ node = node->next;
+ }
+ }
+
+ if (saveit) {
+
+ /* allocate the node */
+ node = (stringNode *)PORT_ArenaAlloc(names->arena, sizeof(stringNode));
+ if (node == NULL) {
+ PORT_Free(nickname);
+ return PR_FAILURE;
+ }
+
+ /* copy the string */
+ len = PORT_Strlen(nickname) + 1;
+ node->string = (char *)PORT_ArenaAlloc(names->arena, len);
+ if (node->string == NULL) {
+ PORT_Free(nickname);
+ return PR_FAILURE;
+ }
+ PORT_Memcpy(node->string, nickname, len);
+
+ /* link it into the list */
+ node->next = (stringNode *)names->head;
+ names->head = (void *)node;
+
+ /* bump the count */
+ names->numnicknames++;
+ }
+
+ if (nickname)
+ PORT_Free(nickname);
+ return (PR_SUCCESS);
+}
+
+CERTCertNicknames *
+CERT_GetCertNicknames(CERTCertDBHandle *handle, int what, void *wincx)
+{
+ PLArenaPool *arena;
+ CERTCertNicknames *names;
+ int i;
+ stringNode *node;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return (NULL);
+ }
+
+ names = (CERTCertNicknames *)PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames));
+ if (names == NULL) {
+ goto loser;
+ }
+
+ names->arena = arena;
+ names->head = NULL;
+ names->numnicknames = 0;
+ names->nicknames = NULL;
+ names->what = what;
+ names->totallen = 0;
+
+ /* make sure we are logged in */
+ (void)pk11_TraverseAllSlots(NULL, NULL, PR_TRUE, wincx);
+
+ NSSTrustDomain_TraverseCertificates(handle,
+ CollectNicknames, (void *)names);
+ if (names->numnicknames) {
+ names->nicknames = (char **)PORT_ArenaAlloc(arena,
+ names->numnicknames *
+ sizeof(char *));
+
+ if (names->nicknames == NULL) {
+ goto loser;
+ }
+
+ node = (stringNode *)names->head;
+
+ for (i = 0; i < names->numnicknames; i++) {
+ PORT_Assert(node != NULL);
+
+ names->nicknames[i] = node->string;
+ names->totallen += PORT_Strlen(node->string);
+ node = node->next;
+ }
+
+ PORT_Assert(node == NULL);
+ }
+
+ return (names);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ return (NULL);
+}
+
+void
+CERT_FreeNicknames(CERTCertNicknames *nicknames)
+{
+ PORT_FreeArena(nicknames->arena, PR_FALSE);
+
+ return;
+}
+
+/* [ FROM pcertdb.c ] */
+
+typedef struct dnameNode {
+ struct dnameNode *next;
+ SECItem name;
+} dnameNode;
+
+void
+CERT_FreeDistNames(CERTDistNames *names)
+{
+ PORT_FreeArena(names->arena, PR_FALSE);
+
+ return;
+}
+
+static SECStatus
+CollectDistNames(CERTCertificate *cert, SECItem *k, void *data)
+{
+ CERTDistNames *names;
+ PRBool saveit = PR_FALSE;
+ CERTCertTrust trust;
+ dnameNode *node;
+ int len;
+
+ names = (CERTDistNames *)data;
+
+ if (CERT_GetCertTrust(cert, &trust) == SECSuccess) {
+ /* only collect names of CAs trusted for issuing SSL clients */
+ if (trust.sslFlags & CERTDB_TRUSTED_CLIENT_CA) {
+ saveit = PR_TRUE;
+ }
+ }
+
+ if (saveit) {
+ /* allocate the node */
+ node = (dnameNode *)PORT_ArenaAlloc(names->arena, sizeof(dnameNode));
+ if (node == NULL) {
+ return (SECFailure);
+ }
+
+ /* copy the name */
+ node->name.len = len = cert->derSubject.len;
+ node->name.type = siBuffer;
+ node->name.data = (unsigned char *)PORT_ArenaAlloc(names->arena, len);
+ if (node->name.data == NULL) {
+ return (SECFailure);
+ }
+ PORT_Memcpy(node->name.data, cert->derSubject.data, len);
+
+ /* link it into the list */
+ node->next = (dnameNode *)names->head;
+ names->head = (void *)node;
+
+ /* bump the count */
+ names->nnames++;
+ }
+
+ return (SECSuccess);
+}
+
+/*
+ * Return all of the CAs that are "trusted" for SSL.
+ */
+CERTDistNames *
+CERT_DupDistNames(CERTDistNames *orig)
+{
+ PLArenaPool *arena;
+ CERTDistNames *names;
+ int i;
+ SECStatus rv;
+
+ /* allocate an arena to use */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return (NULL);
+ }
+
+ /* allocate the header structure */
+ names = (CERTDistNames *)PORT_ArenaAlloc(arena, sizeof(CERTDistNames));
+ if (names == NULL) {
+ goto loser;
+ }
+
+ /* initialize the header struct */
+ names->arena = arena;
+ names->head = NULL;
+ names->nnames = orig->nnames;
+ names->names = NULL;
+
+ /* construct the array from the list */
+ if (orig->nnames) {
+ names->names = (SECItem *)PORT_ArenaNewArray(arena, SECItem,
+ orig->nnames);
+ if (names->names == NULL) {
+ goto loser;
+ }
+ for (i = 0; i < orig->nnames; i++) {
+ rv = SECITEM_CopyItem(arena, &names->names[i], &orig->names[i]);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+ }
+ return (names);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ return (NULL);
+}
+
+CERTDistNames *
+CERT_GetSSLCACerts(CERTCertDBHandle *handle)
+{
+ PLArenaPool *arena;
+ CERTDistNames *names;
+ int i;
+ SECStatus rv;
+ dnameNode *node;
+
+ /* allocate an arena to use */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return (NULL);
+ }
+
+ /* allocate the header structure */
+ names = (CERTDistNames *)PORT_ArenaAlloc(arena, sizeof(CERTDistNames));
+ if (names == NULL) {
+ goto loser;
+ }
+
+ /* initialize the header struct */
+ names->arena = arena;
+ names->head = NULL;
+ names->nnames = 0;
+ names->names = NULL;
+
+ /* collect the names from the database */
+ rv = PK11_TraverseSlotCerts(CollectDistNames, (void *)names, NULL);
+ if (rv) {
+ goto loser;
+ }
+
+ /* construct the array from the list */
+ if (names->nnames) {
+ names->names = (SECItem *)PORT_ArenaAlloc(arena, names->nnames * sizeof(SECItem));
+
+ if (names->names == NULL) {
+ goto loser;
+ }
+
+ node = (dnameNode *)names->head;
+
+ for (i = 0; i < names->nnames; i++) {
+ PORT_Assert(node != NULL);
+
+ names->names[i] = node->name;
+ node = node->next;
+ }
+
+ PORT_Assert(node == NULL);
+ }
+
+ return (names);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ return (NULL);
+}
+
+CERTDistNames *
+CERT_DistNamesFromCertList(CERTCertList *certList)
+{
+ CERTDistNames *dnames = NULL;
+ PLArenaPool *arena;
+ CERTCertListNode *node = NULL;
+ SECItem *names = NULL;
+ int listLen = 0, i = 0;
+
+ if (certList == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ node = CERT_LIST_HEAD(certList);
+ while (!CERT_LIST_END(node, certList)) {
+ listLen += 1;
+ node = CERT_LIST_NEXT(node);
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ goto loser;
+ dnames = PORT_ArenaZNew(arena, CERTDistNames);
+ if (dnames == NULL)
+ goto loser;
+
+ dnames->arena = arena;
+ dnames->nnames = listLen;
+ dnames->names = names = PORT_ArenaZNewArray(arena, SECItem, listLen);
+ if (names == NULL)
+ goto loser;
+
+ node = CERT_LIST_HEAD(certList);
+ while (!CERT_LIST_END(node, certList)) {
+ CERTCertificate *cert = node->cert;
+ SECStatus rv = SECITEM_CopyItem(arena, &names[i++], &cert->derSubject);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+ node = CERT_LIST_NEXT(node);
+ }
+ return dnames;
+loser:
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+CERTDistNames *
+CERT_DistNamesFromNicknames(CERTCertDBHandle *handle, char **nicknames,
+ int nnames)
+{
+ CERTDistNames *dnames = NULL;
+ PLArenaPool *arena;
+ int i, rv;
+ SECItem *names = NULL;
+ CERTCertificate *cert = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ goto loser;
+ dnames = PORT_ArenaZNew(arena, CERTDistNames);
+ if (dnames == NULL)
+ goto loser;
+
+ dnames->arena = arena;
+ dnames->nnames = nnames;
+ dnames->names = names = PORT_ArenaZNewArray(arena, SECItem, nnames);
+ if (names == NULL)
+ goto loser;
+
+ for (i = 0; i < nnames; i++) {
+ cert = CERT_FindCertByNicknameOrEmailAddr(handle, nicknames[i]);
+ if (cert == NULL)
+ goto loser;
+ rv = SECITEM_CopyItem(arena, &names[i], &cert->derSubject);
+ if (rv == SECFailure)
+ goto loser;
+ CERT_DestroyCertificate(cert);
+ }
+ return dnames;
+
+loser:
+ if (cert != NULL)
+ CERT_DestroyCertificate(cert);
+ if (arena != NULL)
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+}
+
+/* [ from pcertdb.c - calls Ascii to Name ] */
+/*
+ * Lookup a certificate in the database by name
+ */
+CERTCertificate *
+CERT_FindCertByNameString(CERTCertDBHandle *handle, char *nameStr)
+{
+ CERTName *name;
+ SECItem *nameItem;
+ CERTCertificate *cert = NULL;
+ PLArenaPool *arena = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+
+ if (arena == NULL) {
+ goto loser;
+ }
+
+ name = CERT_AsciiToName(nameStr);
+
+ if (name) {
+ nameItem = SEC_ASN1EncodeItem(arena, NULL, (void *)name,
+ CERT_NameTemplate);
+ if (nameItem != NULL) {
+ cert = CERT_FindCertByName(handle, nameItem);
+ }
+ CERT_DestroyName(name);
+ }
+
+loser:
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return (cert);
+}
+
+/* From certv3.c */
+
+CERTCrlDistributionPoints *
+CERT_FindCRLDistributionPoints(CERTCertificate *cert)
+{
+ SECItem encodedExtenValue;
+ SECStatus rv;
+ CERTCrlDistributionPoints *dps;
+
+ encodedExtenValue.data = NULL;
+ encodedExtenValue.len = 0;
+
+ rv = cert_FindExtension(cert->extensions, SEC_OID_X509_CRL_DIST_POINTS,
+ &encodedExtenValue);
+ if (rv != SECSuccess) {
+ return (NULL);
+ }
+
+ dps = CERT_DecodeCRLDistributionPoints(cert->arena, &encodedExtenValue);
+
+ PORT_Free(encodedExtenValue.data);
+
+ return dps;
+}
+
+/* From crl.c */
+CERTSignedCrl *
+CERT_ImportCRL(CERTCertDBHandle *handle, SECItem *derCRL, char *url, int type, void *wincx)
+{
+ CERTSignedCrl *retCrl = NULL;
+ PK11SlotInfo *slot = PK11_GetInternalKeySlot();
+ retCrl = PK11_ImportCRL(slot, derCRL, url, type, wincx,
+ CRL_IMPORT_DEFAULT_OPTIONS, NULL, CRL_DECODE_DEFAULT_OPTIONS);
+ PK11_FreeSlot(slot);
+
+ return retCrl;
+}
+
+/* From certdb.c */
+static SECStatus
+cert_ImportCAChain(SECItem *certs, int numcerts, SECCertUsage certUsage, PRBool trusted)
+{
+ SECStatus rv;
+ SECItem *derCert;
+ CERTCertificate *cert = NULL;
+ CERTCertificate *newcert = NULL;
+ CERTCertDBHandle *handle;
+ CERTCertTrust trust;
+ PRBool isca;
+ char *nickname;
+ unsigned int certtype;
+ PRBool istemp = PR_FALSE;
+
+ handle = CERT_GetDefaultCertDB();
+
+ while (numcerts--) {
+ derCert = certs;
+ certs++;
+
+ /* decode my certificate */
+ /* This use is ok -- only looks at decoded parts, calls NewTemp later */
+ newcert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL);
+ if (newcert == NULL) {
+ goto loser;
+ }
+
+ if (!trusted) {
+ /* make sure that cert is valid */
+ rv = CERT_CertTimesValid(newcert);
+ if (rv == SECFailure) {
+ goto endloop;
+ }
+ }
+
+ /* does it have the CA extension */
+
+ /*
+ * Make sure that if this is an intermediate CA in the chain that
+ * it was given permission by its signer to be a CA.
+ */
+ isca = CERT_IsCACert(newcert, &certtype);
+
+ if (!isca) {
+ if (!trusted) {
+ goto endloop;
+ }
+ trust.sslFlags = CERTDB_VALID_CA;
+ trust.emailFlags = CERTDB_VALID_CA;
+ trust.objectSigningFlags = CERTDB_VALID_CA;
+ } else {
+ /* SSL ca's must have the ssl bit set */
+ if ((certUsage == certUsageSSLCA) &&
+ ((certtype & NS_CERT_TYPE_SSL_CA) != NS_CERT_TYPE_SSL_CA)) {
+ goto endloop;
+ }
+
+ /* it passed all of the tests, so lets add it to the database */
+ /* mark it as a CA */
+ PORT_Memset((void *)&trust, 0, sizeof(trust));
+ switch (certUsage) {
+ case certUsageSSLCA:
+ trust.sslFlags = CERTDB_VALID_CA;
+ break;
+ case certUsageUserCertImport:
+ if ((certtype & NS_CERT_TYPE_SSL_CA) == NS_CERT_TYPE_SSL_CA) {
+ trust.sslFlags = CERTDB_VALID_CA;
+ }
+ if ((certtype & NS_CERT_TYPE_EMAIL_CA) ==
+ NS_CERT_TYPE_EMAIL_CA) {
+ trust.emailFlags = CERTDB_VALID_CA;
+ }
+ if ((certtype & NS_CERT_TYPE_OBJECT_SIGNING_CA) ==
+ NS_CERT_TYPE_OBJECT_SIGNING_CA) {
+ trust.objectSigningFlags = CERTDB_VALID_CA;
+ }
+ break;
+ default:
+ PORT_Assert(0);
+ break;
+ }
+ }
+
+ cert = CERT_NewTempCertificate(handle, derCert, NULL,
+ PR_FALSE, PR_FALSE);
+ if (cert == NULL) {
+ goto loser;
+ }
+
+ /* if the cert is temp, make it perm; otherwise we're done */
+ rv = CERT_GetCertIsTemp(cert, &istemp);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ if (istemp) {
+ /* get a default nickname for it */
+ nickname = CERT_MakeCANickname(cert);
+
+ rv = CERT_AddTempCertToPerm(cert, nickname, &trust);
+
+ /* free the nickname */
+ if (nickname) {
+ PORT_Free(nickname);
+ }
+ } else {
+ rv = SECSuccess;
+ }
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ endloop:
+ if (newcert) {
+ CERT_DestroyCertificate(newcert);
+ newcert = NULL;
+ }
+ }
+
+ rv = SECSuccess;
+ goto done;
+loser:
+ rv = SECFailure;
+done:
+
+ if (newcert) {
+ CERT_DestroyCertificate(newcert);
+ newcert = NULL;
+ }
+
+ if (cert) {
+ CERT_DestroyCertificate(cert);
+ cert = NULL;
+ }
+
+ return (rv);
+}
+
+SECStatus
+CERT_ImportCAChain(SECItem *certs, int numcerts, SECCertUsage certUsage)
+{
+ return cert_ImportCAChain(certs, numcerts, certUsage, PR_FALSE);
+}
+
+SECStatus
+CERT_ImportCAChainTrusted(SECItem *certs, int numcerts, SECCertUsage certUsage)
+{
+ return cert_ImportCAChain(certs, numcerts, certUsage, PR_TRUE);
+}
+
+/* Moved from certdb.c */
+/*
+** CERT_CertChainFromCert
+**
+** Construct a CERTCertificateList consisting of the given certificate and all
+** of the issuer certs until we either get to a self-signed cert or can't find
+** an issuer. Since we don't know how many certs are in the chain we have to
+** build a linked list first as we count them.
+*/
+
+typedef struct certNode {
+ struct certNode *next;
+ CERTCertificate *cert;
+} certNode;
+
+CERTCertificateList *
+CERT_CertChainFromCert(CERTCertificate *cert, SECCertUsage usage,
+ PRBool includeRoot)
+{
+ CERTCertificateList *chain = NULL;
+ NSSCertificate **stanChain;
+ NSSCertificate *stanCert;
+ PLArenaPool *arena;
+ NSSUsage nssUsage;
+ int i, len;
+ NSSTrustDomain *td = STAN_GetDefaultTrustDomain();
+ NSSCryptoContext *cc = STAN_GetDefaultCryptoContext();
+
+ stanCert = STAN_GetNSSCertificate(cert);
+ if (!stanCert) {
+ /* error code is set */
+ return NULL;
+ }
+ nssUsage.anyUsage = PR_FALSE;
+ nssUsage.nss3usage = usage;
+ nssUsage.nss3lookingForCA = PR_FALSE;
+ stanChain = NSSCertificate_BuildChain(stanCert, NULL, &nssUsage, NULL, NULL,
+ CERT_MAX_CERT_CHAIN, NULL, NULL, td, cc);
+ if (!stanChain) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
+ return NULL;
+ }
+
+ len = 0;
+ stanCert = stanChain[0];
+ while (stanCert) {
+ stanCert = stanChain[++len];
+ }
+
+ arena = PORT_NewArena(4096);
+ if (arena == NULL) {
+ goto loser;
+ }
+
+ chain = (CERTCertificateList *)PORT_ArenaAlloc(arena,
+ sizeof(CERTCertificateList));
+ if (!chain)
+ goto loser;
+ chain->certs = (SECItem *)PORT_ArenaAlloc(arena, len * sizeof(SECItem));
+ if (!chain->certs)
+ goto loser;
+ i = 0;
+ stanCert = stanChain[i];
+ while (stanCert) {
+ SECItem derCert;
+ CERTCertificate *cCert = STAN_GetCERTCertificate(stanCert);
+ if (!cCert) {
+ goto loser;
+ }
+ derCert.len = (unsigned int)stanCert->encoding.size;
+ derCert.data = (unsigned char *)stanCert->encoding.data;
+ derCert.type = siBuffer;
+ if (SECITEM_CopyItem(arena, &chain->certs[i], &derCert) != SECSuccess) {
+ CERT_DestroyCertificate(cCert);
+ goto loser;
+ }
+ stanCert = stanChain[++i];
+ if (!stanCert && !cCert->isRoot) {
+ /* reached the end of the chain, but the final cert is
+ * not a root. Don't discard it.
+ */
+ includeRoot = PR_TRUE;
+ }
+ CERT_DestroyCertificate(cCert);
+ }
+ if (!includeRoot && len > 1) {
+ chain->len = len - 1;
+ } else {
+ chain->len = len;
+ }
+
+ chain->arena = arena;
+ nss_ZFreeIf(stanChain);
+ return chain;
+loser:
+ i = 0;
+ stanCert = stanChain[i];
+ while (stanCert) {
+ CERTCertificate *cCert = STAN_GetCERTCertificate(stanCert);
+ if (cCert) {
+ CERT_DestroyCertificate(cCert);
+ }
+ stanCert = stanChain[++i];
+ }
+ nss_ZFreeIf(stanChain);
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+/* Builds a CERTCertificateList holding just one DER-encoded cert, namely
+** the one for the cert passed as an argument.
+*/
+CERTCertificateList *
+CERT_CertListFromCert(CERTCertificate *cert)
+{
+ CERTCertificateList *chain = NULL;
+ int rv;
+ PLArenaPool *arena;
+
+ /* arena for SecCertificateList */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ goto no_memory;
+
+ /* build the CERTCertificateList */
+ chain = (CERTCertificateList *)PORT_ArenaAlloc(arena, sizeof(CERTCertificateList));
+ if (chain == NULL)
+ goto no_memory;
+ chain->certs = (SECItem *)PORT_ArenaAlloc(arena, 1 * sizeof(SECItem));
+ if (chain->certs == NULL)
+ goto no_memory;
+ rv = SECITEM_CopyItem(arena, chain->certs, &(cert->derCert));
+ if (rv < 0)
+ goto loser;
+ chain->len = 1;
+ chain->arena = arena;
+
+ return chain;
+
+no_memory:
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+loser:
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+CERTCertificateList *
+CERT_DupCertList(const CERTCertificateList *oldList)
+{
+ CERTCertificateList *newList = NULL;
+ PLArenaPool *arena = NULL;
+ SECItem *newItem;
+ SECItem *oldItem;
+ int len = oldList->len;
+ int rv;
+
+ /* arena for SecCertificateList */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ goto no_memory;
+
+ /* now build the CERTCertificateList */
+ newList = PORT_ArenaNew(arena, CERTCertificateList);
+ if (newList == NULL)
+ goto no_memory;
+ newList->arena = arena;
+ newItem = (SECItem *)PORT_ArenaAlloc(arena, len * sizeof(SECItem));
+ if (newItem == NULL)
+ goto no_memory;
+ newList->certs = newItem;
+ newList->len = len;
+
+ for (oldItem = oldList->certs; len > 0; --len, ++newItem, ++oldItem) {
+ rv = SECITEM_CopyItem(arena, newItem, oldItem);
+ if (rv < 0)
+ goto loser;
+ }
+ return newList;
+
+no_memory:
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+loser:
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+void
+CERT_DestroyCertificateList(CERTCertificateList *list)
+{
+ PORT_FreeArena(list->arena, PR_FALSE);
+}
diff --git a/security/nss/lib/certhigh/certhigh.gyp b/security/nss/lib/certhigh/certhigh.gyp
new file mode 100644
index 0000000000..5817c3eb5f
--- /dev/null
+++ b/security/nss/lib/certhigh/certhigh.gyp
@@ -0,0 +1,31 @@
+# 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': 'certhi',
+ 'type': 'static_library',
+ 'sources': [
+ 'certhigh.c',
+ 'certhtml.c',
+ 'certreq.c',
+ 'certvfy.c',
+ 'certvfypkix.c',
+ 'crlv2.c',
+ 'ocsp.c',
+ 'ocspsig.c',
+ 'xcrldist.c'
+ ],
+ 'dependencies': [
+ '<(DEPTH)/exports.gyp:nss_exports'
+ ]
+ }
+ ],
+ 'variables': {
+ 'module': 'nss'
+ }
+} \ No newline at end of file
diff --git a/security/nss/lib/certhigh/certhtml.c b/security/nss/lib/certhigh/certhtml.c
new file mode 100644
index 0000000000..2d708cc950
--- /dev/null
+++ b/security/nss/lib/certhigh/certhtml.c
@@ -0,0 +1,321 @@
+/* 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/. */
+
+/*
+ * certhtml.c --- convert a cert to html
+ */
+
+#include "seccomon.h"
+#include "secitem.h"
+#include "sechash.h"
+#include "cert.h"
+#include "keyhi.h"
+#include "secder.h"
+#include "prprf.h"
+#include "secport.h"
+#include "secasn1.h"
+#include "pk11func.h"
+
+static char *hex = "0123456789ABCDEF";
+
+/*
+** Convert a der-encoded integer to a hex printable string form
+*/
+char *
+CERT_Hexify(SECItem *i, int do_colon)
+{
+ unsigned char *cp, *end;
+ char *rv, *o;
+
+ if (!i->len) {
+ return PORT_Strdup("00");
+ }
+
+ rv = o = (char *)PORT_Alloc(i->len * 3);
+ if (!rv)
+ return rv;
+
+ cp = i->data;
+ end = cp + i->len;
+ while (cp < end) {
+ unsigned char ch = *cp++;
+ *o++ = hex[(ch >> 4) & 0xf];
+ *o++ = hex[ch & 0xf];
+ if (cp != end) {
+ if (do_colon) {
+ *o++ = ':';
+ }
+ }
+ }
+ *o = 0; /* Null terminate the string */
+ return rv;
+}
+
+#define BREAK "<br>"
+#define BREAKLEN 4
+#define COMMA ", "
+#define COMMALEN 2
+
+#define MAX_OUS 20
+#define MAX_DC MAX_OUS
+
+char *
+CERT_FormatName(CERTName *name)
+{
+ CERTRDN **rdns;
+ CERTRDN *rdn;
+ CERTAVA **avas;
+ CERTAVA *ava;
+ char *buf = 0;
+ char *tmpbuf = 0;
+ SECItem *cn = 0;
+ SECItem *email = 0;
+ SECItem *org = 0;
+ SECItem *loc = 0;
+ SECItem *state = 0;
+ SECItem *country = 0;
+ SECItem *dq = 0;
+
+ unsigned len = 0;
+ int tag;
+ int i;
+ int ou_count = 0;
+ int dc_count = 0;
+ PRBool first;
+ SECItem *orgunit[MAX_OUS];
+ SECItem *dc[MAX_DC];
+
+ /* Loop over name components and gather the interesting ones */
+ rdns = name->rdns;
+ while ((rdn = *rdns++) != 0) {
+ avas = rdn->avas;
+ while ((ava = *avas++) != 0) {
+ tag = CERT_GetAVATag(ava);
+ switch (tag) {
+ case SEC_OID_AVA_COMMON_NAME:
+ if (cn) {
+ break;
+ }
+ cn = CERT_DecodeAVAValue(&ava->value);
+ if (!cn) {
+ goto loser;
+ }
+ len += cn->len;
+ // cn will always have BREAK after it
+ len += BREAKLEN;
+ break;
+ case SEC_OID_AVA_COUNTRY_NAME:
+ if (country) {
+ break;
+ }
+ country = CERT_DecodeAVAValue(&ava->value);
+ if (!country) {
+ goto loser;
+ }
+ len += country->len;
+ // country may have COMMA after it (if we over-count len,
+ // that's fine - we'll just allocate a buffer larger than we
+ // need)
+ len += COMMALEN;
+ break;
+ case SEC_OID_AVA_LOCALITY:
+ if (loc) {
+ break;
+ }
+ loc = CERT_DecodeAVAValue(&ava->value);
+ if (!loc) {
+ goto loser;
+ }
+ len += loc->len;
+ // loc may have COMMA after it
+ len += COMMALEN;
+ break;
+ case SEC_OID_AVA_STATE_OR_PROVINCE:
+ if (state) {
+ break;
+ }
+ state = CERT_DecodeAVAValue(&ava->value);
+ if (!state) {
+ goto loser;
+ }
+ len += state->len;
+ // state currently won't have COMMA after it, but this is a
+ // (probably vain) attempt to future-proof this code
+ len += COMMALEN;
+ break;
+ case SEC_OID_AVA_ORGANIZATION_NAME:
+ if (org) {
+ break;
+ }
+ org = CERT_DecodeAVAValue(&ava->value);
+ if (!org) {
+ goto loser;
+ }
+ len += org->len;
+ // org will have BREAK after it
+ len += BREAKLEN;
+ break;
+ case SEC_OID_AVA_DN_QUALIFIER:
+ if (dq) {
+ break;
+ }
+ dq = CERT_DecodeAVAValue(&ava->value);
+ if (!dq) {
+ goto loser;
+ }
+ len += dq->len;
+ // dq will have BREAK after it
+ len += BREAKLEN;
+ break;
+ case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME:
+ if (ou_count < MAX_OUS) {
+ orgunit[ou_count] = CERT_DecodeAVAValue(&ava->value);
+ if (!orgunit[ou_count]) {
+ goto loser;
+ }
+ len += orgunit[ou_count++]->len;
+ // each ou will have BREAK after it
+ len += BREAKLEN;
+ }
+ break;
+ case SEC_OID_AVA_DC:
+ if (dc_count < MAX_DC) {
+ dc[dc_count] = CERT_DecodeAVAValue(&ava->value);
+ if (!dc[dc_count]) {
+ goto loser;
+ }
+ len += dc[dc_count++]->len;
+ // each dc will have BREAK after it
+ len += BREAKLEN;
+ }
+ break;
+ case SEC_OID_PKCS9_EMAIL_ADDRESS:
+ case SEC_OID_RFC1274_MAIL:
+ if (email) {
+ break;
+ }
+ email = CERT_DecodeAVAValue(&ava->value);
+ if (!email) {
+ goto loser;
+ }
+ len += email->len;
+ // email will have BREAK after it
+ len += BREAKLEN;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // there may be a final BREAK
+ len += BREAKLEN;
+
+ /* allocate buffer */
+ buf = (char *)PORT_Alloc(len);
+ if (!buf) {
+ goto loser;
+ }
+
+ tmpbuf = buf;
+
+ if (cn) {
+ PORT_Memcpy(tmpbuf, cn->data, cn->len);
+ tmpbuf += cn->len;
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ }
+ if (email) {
+ PORT_Memcpy(tmpbuf, email->data, email->len);
+ tmpbuf += (email->len);
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ }
+ for (i = ou_count - 1; i >= 0; i--) {
+ PORT_Memcpy(tmpbuf, orgunit[i]->data, orgunit[i]->len);
+ tmpbuf += (orgunit[i]->len);
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ }
+ if (dq) {
+ PORT_Memcpy(tmpbuf, dq->data, dq->len);
+ tmpbuf += (dq->len);
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ }
+ if (org) {
+ PORT_Memcpy(tmpbuf, org->data, org->len);
+ tmpbuf += (org->len);
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ }
+ for (i = dc_count - 1; i >= 0; i--) {
+ PORT_Memcpy(tmpbuf, dc[i]->data, dc[i]->len);
+ tmpbuf += (dc[i]->len);
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ }
+ first = PR_TRUE;
+ if (loc) {
+ PORT_Memcpy(tmpbuf, loc->data, loc->len);
+ tmpbuf += (loc->len);
+ first = PR_FALSE;
+ }
+ if (state) {
+ if (!first) {
+ PORT_Memcpy(tmpbuf, COMMA, COMMALEN);
+ tmpbuf += COMMALEN;
+ }
+ PORT_Memcpy(tmpbuf, state->data, state->len);
+ tmpbuf += (state->len);
+ first = PR_FALSE;
+ }
+ if (country) {
+ if (!first) {
+ PORT_Memcpy(tmpbuf, COMMA, COMMALEN);
+ tmpbuf += COMMALEN;
+ }
+ PORT_Memcpy(tmpbuf, country->data, country->len);
+ tmpbuf += (country->len);
+ first = PR_FALSE;
+ }
+ if (!first) {
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ }
+
+ *tmpbuf = 0;
+
+/* fall through and clean */
+loser:
+ if (cn) {
+ SECITEM_FreeItem(cn, PR_TRUE);
+ }
+ if (email) {
+ SECITEM_FreeItem(email, PR_TRUE);
+ }
+ for (i = ou_count - 1; i >= 0; i--) {
+ SECITEM_FreeItem(orgunit[i], PR_TRUE);
+ }
+ if (dq) {
+ SECITEM_FreeItem(dq, PR_TRUE);
+ }
+ if (org) {
+ SECITEM_FreeItem(org, PR_TRUE);
+ }
+ for (i = dc_count - 1; i >= 0; i--) {
+ SECITEM_FreeItem(dc[i], PR_TRUE);
+ }
+ if (loc) {
+ SECITEM_FreeItem(loc, PR_TRUE);
+ }
+ if (state) {
+ SECITEM_FreeItem(state, PR_TRUE);
+ }
+ if (country) {
+ SECITEM_FreeItem(country, PR_TRUE);
+ }
+
+ return (buf);
+}
diff --git a/security/nss/lib/certhigh/certreq.c b/security/nss/lib/certhigh/certreq.c
new file mode 100644
index 0000000000..2ab4f1ab7b
--- /dev/null
+++ b/security/nss/lib/certhigh/certreq.c
@@ -0,0 +1,331 @@
+/* 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 "certt.h"
+#include "secder.h"
+#include "keyhi.h"
+#include "secitem.h"
+#include "secasn1.h"
+#include "secerr.h"
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+
+const SEC_ASN1Template CERT_AttributeTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTAttribute) },
+ { SEC_ASN1_OBJECT_ID, offsetof(CERTAttribute, attrType) },
+ { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, offsetof(CERTAttribute, attrValue),
+ SEC_ASN1_SUB(SEC_AnyTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template CERT_SetOfAttributeTemplate[] = {
+ { SEC_ASN1_SET_OF, 0, CERT_AttributeTemplate },
+};
+
+const SEC_ASN1Template CERT_CertificateRequestTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTCertificateRequest) },
+ { SEC_ASN1_INTEGER,
+ offsetof(CERTCertificateRequest, version) },
+ { SEC_ASN1_INLINE,
+ offsetof(CERTCertificateRequest, subject),
+ CERT_NameTemplate },
+ { SEC_ASN1_INLINE,
+ offsetof(CERTCertificateRequest, subjectPublicKeyInfo),
+ CERT_SubjectPublicKeyInfoTemplate },
+ { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(CERTCertificateRequest, attributes),
+ CERT_SetOfAttributeTemplate },
+ { 0 }
+};
+
+SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateRequestTemplate)
+
+CERTCertificate *
+CERT_CreateCertificate(unsigned long serialNumber,
+ CERTName *issuer,
+ CERTValidity *validity,
+ CERTCertificateRequest *req)
+{
+ CERTCertificate *c;
+ int rv;
+ PLArenaPool *arena;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+
+ if (!arena) {
+ return (0);
+ }
+
+ c = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate));
+
+ if (!c) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return 0;
+ }
+
+ c->referenceCount = 1;
+ c->arena = arena;
+
+ /*
+ * Default is a plain version 1.
+ * If extensions are added, it will get changed as appropriate.
+ */
+ rv = DER_SetUInteger(arena, &c->version, SEC_CERTIFICATE_VERSION_1);
+ if (rv)
+ goto loser;
+
+ rv = DER_SetUInteger(arena, &c->serialNumber, serialNumber);
+ if (rv)
+ goto loser;
+
+ rv = CERT_CopyName(arena, &c->issuer, issuer);
+ if (rv)
+ goto loser;
+
+ rv = CERT_CopyValidity(arena, &c->validity, validity);
+ if (rv)
+ goto loser;
+
+ rv = CERT_CopyName(arena, &c->subject, &req->subject);
+ if (rv)
+ goto loser;
+ rv = SECKEY_CopySubjectPublicKeyInfo(arena, &c->subjectPublicKeyInfo,
+ &req->subjectPublicKeyInfo);
+ if (rv)
+ goto loser;
+
+ return c;
+
+loser:
+ CERT_DestroyCertificate(c);
+ return 0;
+}
+
+/************************************************************************/
+/* It's clear from the comments that the original author of this
+ * function expected the template for certificate requests to treat
+ * the attributes as a SET OF ANY. This function expected to be
+ * passed an array of SECItems each of which contained an already encoded
+ * Attribute. But the cert request template does not treat the
+ * Attributes as a SET OF ANY, and AFAIK never has. Instead the template
+ * encodes attributes as a SET OF xxxxxxx. That is, it expects to encode
+ * each of the Attributes, not have them pre-encoded. Consequently an
+ * array of SECItems containing encoded Attributes is of no value to this
+ * function. But we cannot change the signature of this public function.
+ * It must continue to take SECItems.
+ *
+ * I have recoded this function so that each SECItem contains an
+ * encoded cert extension. The encoded cert extensions form the list for the
+ * single attribute of the cert request. In this implementation there is at most
+ * one attribute and it is always of type SEC_OID_PKCS9_EXTENSION_REQUEST.
+ */
+
+CERTCertificateRequest *
+CERT_CreateCertificateRequest(CERTName *subject,
+ CERTSubjectPublicKeyInfo *spki,
+ SECItem **attributes)
+{
+ CERTCertificateRequest *certreq;
+ PLArenaPool *arena;
+ CERTAttribute *attribute;
+ SECOidData *oidData;
+ SECStatus rv;
+ int i = 0;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ certreq = PORT_ArenaZNew(arena, CERTCertificateRequest);
+ if (!certreq) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+ /* below here it is safe to goto loser */
+
+ certreq->arena = arena;
+
+ rv = DER_SetUInteger(arena, &certreq->version,
+ SEC_CERTIFICATE_REQUEST_VERSION);
+ if (rv != SECSuccess)
+ goto loser;
+
+ rv = CERT_CopyName(arena, &certreq->subject, subject);
+ if (rv != SECSuccess)
+ goto loser;
+
+ rv = SECKEY_CopySubjectPublicKeyInfo(arena,
+ &certreq->subjectPublicKeyInfo,
+ spki);
+ if (rv != SECSuccess)
+ goto loser;
+
+ certreq->attributes = PORT_ArenaZNewArray(arena, CERTAttribute *, 2);
+ if (!certreq->attributes)
+ goto loser;
+
+ /* Copy over attribute information */
+ if (!attributes || !attributes[0]) {
+ /*
+ ** Invent empty attribute information. According to the
+ ** pkcs#10 spec, attributes has this ASN.1 type:
+ **
+ ** attributes [0] IMPLICIT Attributes
+ **
+ ** Which means, we should create a NULL terminated list
+ ** with the first entry being NULL;
+ */
+ certreq->attributes[0] = NULL;
+ return certreq;
+ }
+
+ /* allocate space for attributes */
+ attribute = PORT_ArenaZNew(arena, CERTAttribute);
+ if (!attribute)
+ goto loser;
+
+ oidData = SECOID_FindOIDByTag(SEC_OID_PKCS9_EXTENSION_REQUEST);
+ PORT_Assert(oidData);
+ if (!oidData)
+ goto loser;
+ rv = SECITEM_CopyItem(arena, &attribute->attrType, &oidData->oid);
+ if (rv != SECSuccess)
+ goto loser;
+
+ for (i = 0; attributes[i] != NULL; i++)
+ ;
+ attribute->attrValue = PORT_ArenaZNewArray(arena, SECItem *, i + 1);
+ if (!attribute->attrValue)
+ goto loser;
+
+ /* copy attributes */
+ for (i = 0; attributes[i]; i++) {
+ /*
+ ** Attributes are a SetOf Attribute which implies
+ ** lexigraphical ordering. It is assumes that the
+ ** attributes are passed in sorted. If we need to
+ ** add functionality to sort them, there is an
+ ** example in the PKCS 7 code.
+ */
+ attribute->attrValue[i] = SECITEM_ArenaDupItem(arena, attributes[i]);
+ if (!attribute->attrValue[i])
+ goto loser;
+ }
+
+ certreq->attributes[0] = attribute;
+
+ return certreq;
+
+loser:
+ CERT_DestroyCertificateRequest(certreq);
+ return NULL;
+}
+
+void
+CERT_DestroyCertificateRequest(CERTCertificateRequest *req)
+{
+ if (req && req->arena) {
+ PORT_FreeArena(req->arena, PR_FALSE);
+ }
+ return;
+}
+
+static void
+setCRExt(void *o, CERTCertExtension **exts)
+{
+ ((CERTCertificateRequest *)o)->attributes = (struct CERTAttributeStr **)exts;
+}
+
+/*
+** Set up to start gathering cert extensions for a cert request.
+** The list is created as CertExtensions and converted to an
+** attribute list by CERT_FinishCRAttributes().
+ */
+extern void *cert_StartExtensions(void *owner, PLArenaPool *ownerArena,
+ void (*setExts)(void *object, CERTCertExtension **exts));
+void *
+CERT_StartCertificateRequestAttributes(CERTCertificateRequest *req)
+{
+ return (cert_StartExtensions((void *)req, req->arena, setCRExt));
+}
+
+/*
+** At entry req->attributes actually contains an list of cert extensions--
+** req-attributes is overloaded until the list is DER encoded (the first
+** ...EncodeItem() below).
+** We turn this into an attribute list by encapsulating it
+** in a PKCS 10 Attribute structure
+ */
+SECStatus
+CERT_FinishCertificateRequestAttributes(CERTCertificateRequest *req)
+{
+ SECItem *extlist;
+ SECOidData *oidrec;
+ CERTAttribute *attribute;
+
+ if (!req || !req->arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (req->attributes == NULL || req->attributes[0] == NULL)
+ return SECSuccess;
+
+ extlist = SEC_ASN1EncodeItem(req->arena, NULL, &req->attributes,
+ SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate));
+ if (extlist == NULL)
+ return (SECFailure);
+
+ oidrec = SECOID_FindOIDByTag(SEC_OID_PKCS9_EXTENSION_REQUEST);
+ if (oidrec == NULL)
+ return SECFailure;
+
+ /* now change the list of cert extensions into a list of attributes
+ */
+ req->attributes = PORT_ArenaZNewArray(req->arena, CERTAttribute *, 2);
+
+ attribute = PORT_ArenaZNew(req->arena, CERTAttribute);
+
+ if (req->attributes == NULL || attribute == NULL ||
+ SECITEM_CopyItem(req->arena, &attribute->attrType, &oidrec->oid) != 0) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ attribute->attrValue = PORT_ArenaZNewArray(req->arena, SECItem *, 2);
+
+ if (attribute->attrValue == NULL)
+ return SECFailure;
+
+ attribute->attrValue[0] = extlist;
+ attribute->attrValue[1] = NULL;
+ req->attributes[0] = attribute;
+ req->attributes[1] = NULL;
+
+ return SECSuccess;
+}
+
+SECStatus
+CERT_GetCertificateRequestExtensions(CERTCertificateRequest *req,
+ CERTCertExtension ***exts)
+{
+ if (req == NULL || exts == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (req->attributes == NULL || *req->attributes == NULL)
+ return SECSuccess;
+
+ if ((*req->attributes)->attrValue == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ return (SEC_ASN1DecodeItem(req->arena, exts,
+ SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate),
+ (*req->attributes)->attrValue[0]));
+}
diff --git a/security/nss/lib/certhigh/certvfy.c b/security/nss/lib/certhigh/certvfy.c
new file mode 100644
index 0000000000..8e742279f3
--- /dev/null
+++ b/security/nss/lib/certhigh/certvfy.c
@@ -0,0 +1,2158 @@
+/* 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 "nspr.h"
+#include "secerr.h"
+#include "secport.h"
+#include "seccomon.h"
+#include "secoid.h"
+#include "genname.h"
+#include "keyhi.h"
+#include "cert.h"
+#include "certdb.h"
+#include "certi.h"
+#include "cryptohi.h"
+
+#ifndef NSS_DISABLE_LIBPKIX
+#include "pkix.h"
+#include "pkix_pl_cert.h"
+#else
+#include "nss.h"
+#endif /* NSS_DISABLE_LIBPKIX */
+
+#include "nsspki.h"
+#include "pkitm.h"
+#include "pkim.h"
+#include "pki3hack.h"
+#include "base.h"
+#include "keyi.h"
+
+/*
+ * Check the validity times of a certificate
+ */
+SECStatus
+CERT_CertTimesValid(CERTCertificate *c)
+{
+ SECCertTimeValidity valid = CERT_CheckCertValidTimes(c, PR_Now(), PR_TRUE);
+ return (valid == secCertTimeValid) ? SECSuccess : SECFailure;
+}
+
+static SECStatus
+checkKeyParams(const SECAlgorithmID *sigAlgorithm, const SECKEYPublicKey *key)
+{
+ SECStatus rv;
+ SECOidTag sigAlg;
+ SECOidTag curve;
+ PRUint32 policyFlags = 0;
+ PRInt32 minLen, len;
+
+ sigAlg = SECOID_GetAlgorithmTag(sigAlgorithm);
+
+ switch (sigAlg) {
+ case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
+ case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE:
+ case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
+ case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
+ case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
+ if (key->keyType != ecKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+ }
+
+ curve = SECKEY_GetECCOid(&key->u.ec.DEREncodedParams);
+ if (curve != 0) {
+ if (NSS_GetAlgorithmPolicy(curve, &policyFlags) == SECFailure ||
+ !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
+ PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+ return SECFailure;
+ }
+ return SECSuccess;
+ }
+ PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
+ return SECFailure;
+
+ case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: {
+ PORTCheapArenaPool tmpArena;
+ SECOidTag hashAlg;
+ SECOidTag maskHashAlg;
+
+ PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+ rv = sec_DecodeRSAPSSParams(&tmpArena.arena,
+ &sigAlgorithm->parameters,
+ &hashAlg, &maskHashAlg, NULL);
+ PORT_DestroyCheapArena(&tmpArena);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (NSS_GetAlgorithmPolicy(hashAlg, &policyFlags) == SECSuccess &&
+ !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
+ PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+ return SECFailure;
+ }
+ if (NSS_GetAlgorithmPolicy(maskHashAlg, &policyFlags) == SECSuccess &&
+ !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
+ PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+ return SECFailure;
+ }
+ }
+ /* fall through to RSA key checking */
+ case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
+ case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
+ case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE:
+ if (key->keyType != rsaKey && key->keyType != rsaPssKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+ }
+
+ len = 8 * key->u.rsa.modulus.len;
+
+ rv = NSS_OptionGet(NSS_RSA_MIN_KEY_SIZE, &minLen);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (len < minLen) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
+ case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
+ case SEC_OID_SDN702_DSA_SIGNATURE:
+ case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST:
+ case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST:
+ if (key->keyType != dsaKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+ }
+
+ len = 8 * key->u.dsa.params.prime.len;
+
+ rv = NSS_OptionGet(NSS_DSA_MIN_KEY_SIZE, &minLen);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (len < minLen) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+ default:
+ return SECSuccess;
+ }
+}
+
+/*
+ * verify the signature of a signed data object with the given DER publickey
+ */
+SECStatus
+CERT_VerifySignedDataWithPublicKey(const CERTSignedData *sd,
+ SECKEYPublicKey *pubKey,
+ void *wincx)
+{
+ SECStatus rv;
+ SECItem sig;
+ SECOidTag sigAlg;
+ SECOidTag encAlg;
+ SECOidTag hashAlg;
+ PRUint32 policyFlags;
+
+ if (!pubKey || !sd) {
+ PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
+ return SECFailure;
+ }
+
+ /* Can we use this algorithm for signature verification? */
+ sigAlg = SECOID_GetAlgorithmTag(&sd->signatureAlgorithm);
+ rv = sec_DecodeSigAlg(pubKey, sigAlg,
+ &sd->signatureAlgorithm.parameters,
+ &encAlg, &hashAlg);
+ if (rv != SECSuccess) {
+ return SECFailure; /* error is set */
+ }
+ rv = NSS_GetAlgorithmPolicy(encAlg, &policyFlags);
+ if (rv == SECSuccess &&
+ !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
+ PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+ return SECFailure;
+ }
+ rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags);
+ if (rv == SECSuccess &&
+ !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
+ PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+ return SECFailure;
+ }
+ rv = checkKeyParams(&sd->signatureAlgorithm, pubKey);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+ return SECFailure;
+ }
+
+ /* check the signature */
+ sig = sd->signature;
+ /* convert sig->len from bit counts to byte count. */
+ DER_ConvertBitString(&sig);
+
+ rv = VFY_VerifyDataWithAlgorithmID(sd->data.data, sd->data.len, pubKey,
+ &sig, &sd->signatureAlgorithm,
+ &hashAlg, wincx);
+ if (rv != SECSuccess) {
+ return SECFailure; /* error is set */
+ }
+
+ /* for some algorithms, hash algorithm is only known after verification */
+ rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags);
+ if (rv == SECSuccess &&
+ !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
+ PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * verify the signature of a signed data object with the given DER publickey
+ */
+SECStatus
+CERT_VerifySignedDataWithPublicKeyInfo(CERTSignedData *sd,
+ CERTSubjectPublicKeyInfo *pubKeyInfo,
+ void *wincx)
+{
+ SECKEYPublicKey *pubKey;
+ SECStatus rv = SECFailure;
+
+ /* get cert's public key */
+ pubKey = SECKEY_ExtractPublicKey(pubKeyInfo);
+ if (pubKey) {
+ rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx);
+ SECKEY_DestroyPublicKey(pubKey);
+ }
+ return rv;
+}
+
+/*
+ * verify the signature of a signed data object with the given certificate
+ */
+SECStatus
+CERT_VerifySignedData(CERTSignedData *sd, CERTCertificate *cert,
+ PRTime t, void *wincx)
+{
+ SECKEYPublicKey *pubKey = 0;
+ SECStatus rv = SECFailure;
+ SECCertTimeValidity validity;
+
+ /* check the certificate's validity */
+ validity = CERT_CheckCertValidTimes(cert, t, PR_FALSE);
+ if (validity != secCertTimeValid) {
+ return rv;
+ }
+
+ /* get cert's public key */
+ pubKey = CERT_ExtractPublicKey(cert);
+ if (pubKey) {
+ rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx);
+ SECKEY_DestroyPublicKey(pubKey);
+ }
+ return rv;
+}
+
+SECStatus
+SEC_CheckCRL(CERTCertDBHandle *handle, CERTCertificate *cert,
+ CERTCertificate *caCert, PRTime t, void *wincx)
+{
+ return CERT_CheckCRL(cert, caCert, NULL, t, wincx);
+}
+
+/*
+ * Find the issuer of a cert. Use the authorityKeyID if it exists.
+ */
+CERTCertificate *
+CERT_FindCertIssuer(CERTCertificate *cert, PRTime validTime, SECCertUsage usage)
+{
+ NSSCertificate *me;
+ NSSTime *nssTime;
+ NSSTrustDomain *td;
+ NSSCryptoContext *cc;
+ NSSCertificate *chain[3];
+ NSSUsage nssUsage;
+ PRStatus status;
+
+ me = STAN_GetNSSCertificate(cert);
+ if (!me) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+ nssTime = NSSTime_SetPRTime(NULL, validTime);
+ nssUsage.anyUsage = PR_FALSE;
+ nssUsage.nss3usage = usage;
+ nssUsage.nss3lookingForCA = PR_TRUE;
+ memset(chain, 0, 3 * sizeof(NSSCertificate *));
+ td = STAN_GetDefaultTrustDomain();
+ cc = STAN_GetDefaultCryptoContext();
+ (void)NSSCertificate_BuildChain(me, nssTime, &nssUsage, NULL,
+ chain, 2, NULL, &status, td, cc);
+ nss_ZFreeIf(nssTime);
+ if (status == PR_SUCCESS) {
+ PORT_Assert(me == chain[0]);
+ /* if it's a root, the chain will only have one cert */
+ if (!chain[1]) {
+ /* already has a reference from the call to BuildChain */
+ return cert;
+ }
+ NSSCertificate_Destroy(chain[0]); /* the first cert in the chain */
+ return STAN_GetCERTCertificate(chain[1]); /* return the 2nd */
+ }
+ if (chain[0]) {
+ PORT_Assert(me == chain[0]);
+ NSSCertificate_Destroy(chain[0]); /* the first cert in the chain */
+ }
+ PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
+ return NULL;
+}
+
+/*
+ * return required trust flags for various cert usages for CAs
+ */
+SECStatus
+CERT_TrustFlagsForCACertUsage(SECCertUsage usage,
+ unsigned int *retFlags,
+ SECTrustType *retTrustType)
+{
+ unsigned int requiredFlags;
+ SECTrustType trustType;
+
+ switch (usage) {
+ case certUsageSSLClient:
+ requiredFlags = CERTDB_TRUSTED_CLIENT_CA;
+ trustType = trustSSL;
+ break;
+ case certUsageSSLServer:
+ case certUsageSSLCA:
+ requiredFlags = CERTDB_TRUSTED_CA;
+ trustType = trustSSL;
+ break;
+ case certUsageIPsec:
+ requiredFlags = CERTDB_TRUSTED_CA;
+ trustType = trustSSL;
+ break;
+ case certUsageSSLServerWithStepUp:
+ requiredFlags = CERTDB_TRUSTED_CA | CERTDB_GOVT_APPROVED_CA;
+ trustType = trustSSL;
+ break;
+ case certUsageEmailSigner:
+ case certUsageEmailRecipient:
+ requiredFlags = CERTDB_TRUSTED_CA;
+ trustType = trustEmail;
+ break;
+ case certUsageObjectSigner:
+ requiredFlags = CERTDB_TRUSTED_CA;
+ trustType = trustObjectSigning;
+ break;
+ case certUsageVerifyCA:
+ case certUsageAnyCA:
+ case certUsageStatusResponder:
+ requiredFlags = CERTDB_TRUSTED_CA;
+ trustType = trustTypeNone;
+ break;
+ default:
+ PORT_Assert(0);
+ goto loser;
+ }
+ if (retFlags != NULL) {
+ *retFlags = requiredFlags;
+ }
+ if (retTrustType != NULL) {
+ *retTrustType = trustType;
+ }
+
+ return (SECSuccess);
+loser:
+ return (SECFailure);
+}
+
+void
+cert_AddToVerifyLog(CERTVerifyLog *log, CERTCertificate *cert, long error,
+ unsigned int depth, void *arg)
+{
+ CERTVerifyLogNode *node, *tnode;
+
+ PORT_Assert(log != NULL);
+
+ node = (CERTVerifyLogNode *)PORT_ArenaAlloc(log->arena,
+ sizeof(CERTVerifyLogNode));
+ if (node != NULL) {
+ node->cert = CERT_DupCertificate(cert);
+ node->error = error;
+ node->depth = depth;
+ node->arg = arg;
+
+ if (log->tail == NULL) {
+ /* empty list */
+ log->head = log->tail = node;
+ node->prev = NULL;
+ node->next = NULL;
+ } else if (depth >= log->tail->depth) {
+ /* add to tail */
+ node->prev = log->tail;
+ log->tail->next = node;
+ log->tail = node;
+ node->next = NULL;
+ } else if (depth < log->head->depth) {
+ /* add at head */
+ node->prev = NULL;
+ node->next = log->head;
+ log->head->prev = node;
+ log->head = node;
+ } else {
+ /* add in middle */
+ tnode = log->tail;
+ while (tnode != NULL) {
+ if (depth >= tnode->depth) {
+ /* insert after tnode */
+ node->prev = tnode;
+ node->next = tnode->next;
+ tnode->next->prev = node;
+ tnode->next = node;
+ break;
+ }
+
+ tnode = tnode->prev;
+ }
+ }
+
+ log->count++;
+ }
+ return;
+}
+
+#define EXIT_IF_NOT_LOGGING(log) \
+ if (log == NULL) { \
+ goto loser; \
+ }
+
+#define LOG_ERROR_OR_EXIT(log, cert, depth, arg) \
+ if (log != NULL) { \
+ cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \
+ (void *)(PRWord)arg); \
+ } else { \
+ goto loser; \
+ }
+
+#define LOG_ERROR(log, cert, depth, arg) \
+ if (log != NULL) { \
+ cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \
+ (void *)(PRWord)arg); \
+ }
+
+/* /C=CN/O=WoSign CA Limited/CN=CA \xE6\xB2\x83\xE9\x80\x9A\xE6\xA0\xB9\xE8\xAF\x81\xE4\xB9\xA6
+ * Using a consistent naming convention, this would actually be called
+ * 'CA沃通根证书DN', but since GCC 6.2.1 apparently can't handle UTF-8
+ * identifiers, this will have to do.
+ */
+static const unsigned char CAWoSignRootDN[72] = {
+ 0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x0C, 0x12, 0x43, 0x41, 0x20, 0xE6, 0xB2, 0x83, 0xE9, 0x80, 0x9A, 0xE6, 0xA0,
+ 0xB9, 0xE8, 0xAF, 0x81, 0xE4, 0xB9, 0xA6
+};
+
+/* /C=CN/O=WoSign CA Limited/CN=CA WoSign ECC Root */
+static const unsigned char CAWoSignECCRootDN[72] = {
+ 0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x12, 0x43, 0x41, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x45,
+ 0x43, 0x43, 0x20, 0x52, 0x6F, 0x6F, 0x74
+};
+
+/* /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign */
+static const unsigned char CertificationAuthorityofWoSignDN[87] = {
+ 0x30, 0x55, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x2A, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x21, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20,
+ 0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E
+};
+
+/* /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign G2 */
+static const unsigned char CertificationAuthorityofWoSignG2DN[90] = {
+ 0x30, 0x58, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x2D, 0x30, 0x2B, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x24, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20,
+ 0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x47, 0x32
+};
+
+/* /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority */
+static const unsigned char StartComCertificationAuthorityDN[127] = {
+ 0x30, 0x7D, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E,
+ 0x31, 0x2B, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x22, 0x53, 0x65,
+ 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53,
+ 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79
+};
+
+/* /C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2 */
+static const unsigned char StartComCertificationAuthorityG2DN[85] = {
+ 0x30, 0x53, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E,
+ 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F,
+ 0x72, 0x69, 0x74, 0x79, 0x20, 0x47, 0x32
+};
+
+struct DataAndLength {
+ const unsigned char *data;
+ PRUint32 len;
+};
+
+static const struct DataAndLength StartComAndWoSignDNs[] = {
+ { CAWoSignRootDN,
+ sizeof(CAWoSignRootDN) },
+ { CAWoSignECCRootDN,
+ sizeof(CAWoSignECCRootDN) },
+ { CertificationAuthorityofWoSignDN,
+ sizeof(CertificationAuthorityofWoSignDN) },
+ { CertificationAuthorityofWoSignG2DN,
+ sizeof(CertificationAuthorityofWoSignG2DN) },
+ { StartComCertificationAuthorityDN,
+ sizeof(StartComCertificationAuthorityDN) },
+ { StartComCertificationAuthorityG2DN,
+ sizeof(StartComCertificationAuthorityG2DN) },
+};
+
+static PRBool
+CertIsStartComOrWoSign(const CERTCertificate *cert)
+{
+ int i;
+ const struct DataAndLength *dn = StartComAndWoSignDNs;
+
+ for (i = 0; i < sizeof(StartComAndWoSignDNs) / sizeof(struct DataAndLength); ++i, dn++) {
+ if (cert->derSubject.len == dn->len &&
+ memcmp(cert->derSubject.data, dn->data, dn->len) == 0) {
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+SECStatus
+isIssuerCertAllowedAtCertIssuanceTime(CERTCertificate *issuerCert,
+ CERTCertificate *referenceCert)
+{
+ if (!issuerCert || !referenceCert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (CertIsStartComOrWoSign(issuerCert)) {
+ /* PRTime is microseconds since the epoch, whereas JS time is milliseconds.
+ * (new Date("2016-10-21T00:00:00Z")).getTime() * 1000
+ */
+ static const PRTime OCTOBER_21_2016 = 1477008000000000;
+
+ PRTime notBefore, notAfter;
+ SECStatus rv;
+
+ rv = CERT_GetCertTimes(referenceCert, &notBefore, &notAfter);
+ if (rv != SECSuccess)
+ return rv;
+
+ if (notBefore > OCTOBER_21_2016) {
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}
+
+static SECStatus
+cert_VerifyCertChainOld(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool checkSig, PRBool *sigerror,
+ SECCertUsage certUsage, PRTime t, void *wincx,
+ CERTVerifyLog *log, PRBool *revoked)
+{
+ SECTrustType trustType;
+ CERTBasicConstraints basicConstraint;
+ CERTCertificate *issuerCert = NULL;
+ CERTCertificate *subjectCert = NULL;
+ CERTCertificate *badCert = NULL;
+ PRBool isca;
+ SECStatus rv;
+ SECStatus rvFinal = SECSuccess;
+ int count;
+ int currentPathLen = 0;
+ int pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT;
+ unsigned int caCertType;
+ unsigned int requiredCAKeyUsage;
+ unsigned int requiredFlags;
+ PLArenaPool *arena = NULL;
+ CERTGeneralName *namesList = NULL;
+ CERTCertificate **certsList = NULL;
+ int certsListLen = 16;
+ int namesCount = 0;
+ PRBool subjectCertIsSelfIssued;
+ CERTCertTrust issuerTrust;
+
+ if (revoked) {
+ *revoked = PR_FALSE;
+ }
+
+ if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE,
+ &requiredCAKeyUsage,
+ &caCertType) !=
+ SECSuccess) {
+ PORT_Assert(0);
+ EXIT_IF_NOT_LOGGING(log);
+ requiredCAKeyUsage = 0;
+ caCertType = 0;
+ }
+
+ switch (certUsage) {
+ case certUsageSSLClient:
+ case certUsageSSLServer:
+ case certUsageIPsec:
+ case certUsageSSLCA:
+ case certUsageSSLServerWithStepUp:
+ case certUsageEmailSigner:
+ case certUsageEmailRecipient:
+ case certUsageObjectSigner:
+ case certUsageVerifyCA:
+ case certUsageAnyCA:
+ case certUsageStatusResponder:
+ if (CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags,
+ &trustType) != SECSuccess) {
+ PORT_Assert(0);
+ EXIT_IF_NOT_LOGGING(log);
+ /* XXX continuing with requiredFlags = 0 seems wrong. It'll
+ * cause the following test to be true incorrectly:
+ * flags = SEC_GET_TRUST_FLAGS(issuerCert->trust, trustType);
+ * if (( flags & requiredFlags ) == requiredFlags) {
+ * rv = rvFinal;
+ * goto done;
+ * }
+ * There are three other instances of this problem.
+ */
+ requiredFlags = 0;
+ trustType = trustSSL;
+ }
+ break;
+ default:
+ PORT_Assert(0);
+ EXIT_IF_NOT_LOGGING(log);
+ requiredFlags = 0;
+ trustType = trustSSL; /* This used to be 0, but we need something
+ * that matches the enumeration type.
+ */
+ caCertType = 0;
+ }
+
+ subjectCert = CERT_DupCertificate(cert);
+ if (subjectCert == NULL) {
+ goto loser;
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+
+ certsList = PORT_ZNewArray(CERTCertificate *, certsListLen);
+ if (certsList == NULL)
+ goto loser;
+
+ /* RFC 3280 says that the name constraints will apply to the names
+ ** in the leaf (EE) cert, whether it is self issued or not, so
+ ** we pretend that it is not.
+ */
+ subjectCertIsSelfIssued = PR_FALSE;
+ for (count = 0; count < CERT_MAX_CERT_CHAIN; count++) {
+ PRBool validCAOverride = PR_FALSE;
+
+ /* Construct a list of names for the current and all previous
+ * certifcates (except leaf (EE) certs, root CAs, and self-issued
+ * intermediate CAs) to be verified against the name constraints
+ * extension of the issuer certificate.
+ */
+ if (subjectCertIsSelfIssued == PR_FALSE) {
+ CERTGeneralName *subjectNameList;
+ int subjectNameListLen;
+ int i;
+ PRBool getSubjectCN = (!count &&
+ (certUsage == certUsageSSLServer || certUsage == certUsageIPsec));
+ subjectNameList =
+ CERT_GetConstrainedCertificateNames(subjectCert, arena,
+ getSubjectCN);
+ if (!subjectNameList)
+ goto loser;
+ subjectNameListLen = CERT_GetNamesLength(subjectNameList);
+ if (!subjectNameListLen)
+ goto loser;
+ if (certsListLen <= namesCount + subjectNameListLen) {
+ CERTCertificate **tmpCertsList;
+ certsListLen = (namesCount + subjectNameListLen) * 2;
+ tmpCertsList =
+ (CERTCertificate **)PORT_Realloc(certsList,
+ certsListLen *
+ sizeof(CERTCertificate *));
+ if (tmpCertsList == NULL) {
+ goto loser;
+ }
+ certsList = tmpCertsList;
+ }
+ for (i = 0; i < subjectNameListLen; i++) {
+ certsList[namesCount + i] = subjectCert;
+ }
+ namesCount += subjectNameListLen;
+ namesList = cert_CombineNamesLists(namesList, subjectNameList);
+ }
+
+ /* check if the cert has an unsupported critical extension */
+ if (subjectCert->options.bits.hasUnsupportedCriticalExt) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
+ LOG_ERROR_OR_EXIT(log, subjectCert, count, 0);
+ }
+
+ /* check that the signatureAlgorithm field of the certificate
+ * matches the signature field of the tbsCertificate */
+ if (SECOID_CompareAlgorithmID(
+ &subjectCert->signatureWrap.signatureAlgorithm,
+ &subjectCert->signature)) {
+ PORT_SetError(SEC_ERROR_ALGORITHM_MISMATCH);
+ LOG_ERROR(log, subjectCert, count, 0);
+ goto loser;
+ }
+
+ /* find the certificate of the issuer */
+ issuerCert = CERT_FindCertIssuer(subjectCert, t, certUsage);
+ if (!issuerCert) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
+ LOG_ERROR(log, subjectCert, count, 0);
+ goto loser;
+ }
+
+ /* verify the signature on the cert */
+ if (checkSig) {
+ rv = CERT_VerifySignedData(&subjectCert->signatureWrap,
+ issuerCert, t, wincx);
+
+ if (rv != SECSuccess) {
+ if (sigerror) {
+ *sigerror = PR_TRUE;
+ }
+ if (PORT_GetError() == SEC_ERROR_EXPIRED_CERTIFICATE) {
+ PORT_SetError(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE);
+ LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
+ } else {
+ if (PORT_GetError() !=
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ }
+ LOG_ERROR_OR_EXIT(log, subjectCert, count, 0);
+ }
+ }
+ }
+
+ /* If the basicConstraint extension is included in an immediate CA
+ * certificate, make sure that the isCA flag is on. If the
+ * pathLenConstraint component exists, it must be greater than the
+ * number of CA certificates we have seen so far. If the extension
+ * is omitted, we will assume that this is a CA certificate with
+ * an unlimited pathLenConstraint (since it already passes the
+ * netscape-cert-type extension checking).
+ */
+
+ rv = CERT_FindBasicConstraintExten(issuerCert, &basicConstraint);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
+ LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
+ }
+ pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT;
+ /* no basic constraints found, we aren't (yet) a CA. */
+ isca = PR_FALSE;
+ } else {
+ if (basicConstraint.isCA == PR_FALSE) {
+ PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
+ LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
+ }
+ pathLengthLimit = basicConstraint.pathLenConstraint;
+ isca = PR_TRUE;
+ }
+ /* make sure that the path len constraint is properly set.*/
+ if (pathLengthLimit >= 0 && currentPathLen > pathLengthLimit) {
+ PORT_SetError(SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID);
+ LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, pathLengthLimit);
+ }
+
+ /* make sure that the entire chain is within the name space of the
+ * current issuer certificate.
+ */
+ rv = CERT_CompareNameSpace(issuerCert, namesList, certsList,
+ arena, &badCert);
+ if (rv != SECSuccess || badCert != NULL) {
+ PORT_SetError(SEC_ERROR_CERT_NOT_IN_NAME_SPACE);
+ LOG_ERROR_OR_EXIT(log, badCert, count + 1, 0);
+ goto loser;
+ }
+
+ rv = isIssuerCertAllowedAtCertIssuanceTime(issuerCert, cert);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
+ LOG_ERROR(log, issuerCert, count + 1, 0);
+ goto loser;
+ }
+
+ /* XXX - the error logging may need to go down into CRL stuff at some
+ * point
+ */
+ /* check revoked list (issuer) */
+ rv = SEC_CheckCRL(handle, subjectCert, issuerCert, t, wincx);
+ if (rv == SECFailure) {
+ if (revoked) {
+ *revoked = PR_TRUE;
+ }
+ LOG_ERROR_OR_EXIT(log, subjectCert, count, 0);
+ } else if (rv == SECWouldBlock) {
+ /* We found something fishy, so we intend to issue an
+ * error to the user, but the user may wish to continue
+ * processing, in which case we better make sure nothing
+ * worse has happened... so keep cranking the loop */
+ rvFinal = SECFailure;
+ if (revoked) {
+ *revoked = PR_TRUE;
+ }
+ LOG_ERROR(log, subjectCert, count, 0);
+ }
+
+ if (CERT_GetCertTrust(issuerCert, &issuerTrust) == SECSuccess) {
+ /* we have some trust info, but this does NOT imply that this
+ * cert is actually trusted for any purpose. The cert may be
+ * explicitly UNtrusted. We won't know until we examine the
+ * trust bits.
+ */
+ unsigned int flags;
+
+ if (certUsage != certUsageAnyCA &&
+ certUsage != certUsageStatusResponder) {
+
+ /*
+ * XXX This choice of trustType seems arbitrary.
+ */
+ if (certUsage == certUsageVerifyCA) {
+ if (subjectCert->nsCertType & NS_CERT_TYPE_EMAIL_CA) {
+ trustType = trustEmail;
+ } else if (subjectCert->nsCertType & NS_CERT_TYPE_SSL_CA) {
+ trustType = trustSSL;
+ } else {
+ trustType = trustObjectSigning;
+ }
+ }
+
+ flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType);
+ if ((flags & requiredFlags) == requiredFlags) {
+ /* we found a trusted one, so return */
+ rv = rvFinal;
+ goto done;
+ }
+ if (flags & CERTDB_VALID_CA) {
+ validCAOverride = PR_TRUE;
+ }
+ /* is it explicitly distrusted? */
+ if ((flags & CERTDB_TERMINAL_RECORD) &&
+ ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) {
+ /* untrusted -- the cert is explicitly untrusted, not
+ * just that it doesn't chain to a trusted cert */
+ PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
+ LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, flags);
+ }
+ } else {
+ /* Check if we have any valid trust when cheching for
+ * certUsageAnyCA or certUsageStatusResponder. */
+ for (trustType = trustSSL; trustType < trustTypeNone;
+ trustType++) {
+ flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType);
+ if ((flags & requiredFlags) == requiredFlags) {
+ rv = rvFinal;
+ goto done;
+ }
+ if (flags & CERTDB_VALID_CA)
+ validCAOverride = PR_TRUE;
+ }
+ /* We have 2 separate loops because we want any single trust
+ * bit to allow this usage to return trusted. Only if none of
+ * the trust bits are on do we check to see if the cert is
+ * untrusted */
+ for (trustType = trustSSL; trustType < trustTypeNone;
+ trustType++) {
+ flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType);
+ /* is it explicitly distrusted? */
+ if ((flags & CERTDB_TERMINAL_RECORD) &&
+ ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) {
+ /* untrusted -- the cert is explicitly untrusted, not
+ * just that it doesn't chain to a trusted cert */
+ PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
+ LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, flags);
+ }
+ }
+ }
+ }
+
+ if (!validCAOverride) {
+ /*
+ * Make sure that if this is an intermediate CA in the chain that
+ * it was given permission by its signer to be a CA.
+ */
+ /*
+ * if basicConstraints says it is a ca, then we check the
+ * nsCertType. If the nsCertType has any CA bits set, then
+ * it must have the right one.
+ */
+ if (!isca || (issuerCert->nsCertType & NS_CERT_TYPE_CA)) {
+ isca = (issuerCert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE;
+ }
+
+ if (!isca) {
+ PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
+ LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
+ }
+
+ /* make sure key usage allows cert signing */
+ if (CERT_CheckKeyUsage(issuerCert, requiredCAKeyUsage) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
+ LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, requiredCAKeyUsage);
+ }
+ }
+
+ /* make sure that the issuer is not self signed. If it is, then
+ * stop here to prevent looping.
+ */
+ if (issuerCert->isRoot) {
+ PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
+ LOG_ERROR(log, issuerCert, count + 1, 0);
+ goto loser;
+ }
+ /* The issuer cert will be the subject cert in the next loop.
+ * A cert is self-issued if its subject and issuer are equal and
+ * both are of non-zero length.
+ */
+ subjectCertIsSelfIssued = (PRBool)
+ SECITEM_ItemsAreEqual(&issuerCert->derIssuer,
+ &issuerCert->derSubject) &&
+ issuerCert->derSubject.len >
+ 0;
+ if (subjectCertIsSelfIssued == PR_FALSE) {
+ /* RFC 3280 says only non-self-issued intermediate CA certs
+ * count in path length.
+ */
+ ++currentPathLen;
+ }
+
+ CERT_DestroyCertificate(subjectCert);
+ subjectCert = issuerCert;
+ issuerCert = NULL;
+ }
+
+ PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
+ LOG_ERROR(log, subjectCert, count, 0);
+loser:
+ rv = SECFailure;
+done:
+ if (certsList != NULL) {
+ PORT_Free(certsList);
+ }
+ if (issuerCert) {
+ CERT_DestroyCertificate(issuerCert);
+ }
+
+ if (subjectCert) {
+ CERT_DestroyCertificate(subjectCert);
+ }
+
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return rv;
+}
+
+SECStatus
+cert_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool checkSig, PRBool *sigerror,
+ SECCertUsage certUsage, PRTime t, void *wincx,
+ CERTVerifyLog *log, PRBool *revoked)
+{
+ if (CERT_GetUsePKIXForValidation()) {
+ return cert_VerifyCertChainPkix(cert, checkSig, certUsage, t,
+ wincx, log, sigerror, revoked);
+ }
+ return cert_VerifyCertChainOld(handle, cert, checkSig, sigerror,
+ certUsage, t, wincx, log, revoked);
+}
+
+SECStatus
+CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool checkSig, SECCertUsage certUsage, PRTime t,
+ void *wincx, CERTVerifyLog *log)
+{
+ return cert_VerifyCertChain(handle, cert, checkSig, NULL, certUsage, t,
+ wincx, log, NULL);
+}
+
+/*
+ * verify that a CA can sign a certificate with the requested usage.
+ */
+SECStatus
+CERT_VerifyCACertForUsage(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool checkSig, SECCertUsage certUsage, PRTime t,
+ void *wincx, CERTVerifyLog *log)
+{
+ SECTrustType trustType;
+ CERTBasicConstraints basicConstraint;
+ PRBool isca;
+ PRBool validCAOverride = PR_FALSE;
+ SECStatus rv;
+ SECStatus rvFinal = SECSuccess;
+ unsigned int flags;
+ unsigned int caCertType;
+ unsigned int requiredCAKeyUsage;
+ unsigned int requiredFlags;
+ CERTCertificate *issuerCert;
+ CERTCertTrust certTrust;
+
+ if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE,
+ &requiredCAKeyUsage,
+ &caCertType) != SECSuccess) {
+ PORT_Assert(0);
+ EXIT_IF_NOT_LOGGING(log);
+ requiredCAKeyUsage = 0;
+ caCertType = 0;
+ }
+
+ switch (certUsage) {
+ case certUsageSSLClient:
+ case certUsageSSLServer:
+ case certUsageIPsec:
+ case certUsageSSLCA:
+ case certUsageSSLServerWithStepUp:
+ case certUsageEmailSigner:
+ case certUsageEmailRecipient:
+ case certUsageObjectSigner:
+ case certUsageVerifyCA:
+ case certUsageStatusResponder:
+ if (CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags,
+ &trustType) != SECSuccess) {
+ PORT_Assert(0);
+ EXIT_IF_NOT_LOGGING(log);
+ requiredFlags = 0;
+ trustType = trustSSL;
+ }
+ break;
+ default:
+ PORT_Assert(0);
+ EXIT_IF_NOT_LOGGING(log);
+ requiredFlags = 0;
+ trustType = trustSSL; /* This used to be 0, but we need something
+ * that matches the enumeration type.
+ */
+ caCertType = 0;
+ }
+
+ /* If the basicConstraint extension is included in an intermmediate CA
+ * certificate, make sure that the isCA flag is on. If the
+ * pathLenConstraint component exists, it must be greater than the
+ * number of CA certificates we have seen so far. If the extension
+ * is omitted, we will assume that this is a CA certificate with
+ * an unlimited pathLenConstraint (since it already passes the
+ * netscape-cert-type extension checking).
+ */
+
+ rv = CERT_FindBasicConstraintExten(cert, &basicConstraint);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
+ LOG_ERROR_OR_EXIT(log, cert, 0, 0);
+ }
+ /* no basic constraints found, we aren't (yet) a CA. */
+ isca = PR_FALSE;
+ } else {
+ if (basicConstraint.isCA == PR_FALSE) {
+ PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
+ LOG_ERROR_OR_EXIT(log, cert, 0, 0);
+ }
+
+ /* can't check path length if we don't know the previous path */
+ isca = PR_TRUE;
+ }
+
+ if (CERT_GetCertTrust(cert, &certTrust) == SECSuccess) {
+ /* we have some trust info, but this does NOT imply that this
+ * cert is actually trusted for any purpose. The cert may be
+ * explicitly UNtrusted. We won't know until we examine the
+ * trust bits.
+ */
+ if (certUsage == certUsageStatusResponder) {
+ /* Check the special case of certUsageStatusResponder */
+ issuerCert = CERT_FindCertIssuer(cert, t, certUsage);
+ if (issuerCert) {
+ if (SEC_CheckCRL(handle, cert, issuerCert, t, wincx) !=
+ SECSuccess) {
+ PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
+ CERT_DestroyCertificate(issuerCert);
+ goto loser;
+ }
+ CERT_DestroyCertificate(issuerCert);
+ }
+ /* XXX We have NOT determined that this cert is trusted.
+ * For years, NSS has treated this as trusted,
+ * but it seems incorrect.
+ */
+ rv = rvFinal;
+ goto done;
+ }
+
+ /*
+ * check the trust params of the issuer
+ */
+ flags = SEC_GET_TRUST_FLAGS(&certTrust, trustType);
+ if ((flags & requiredFlags) == requiredFlags) {
+ /* we found a trusted one, so return */
+ rv = rvFinal;
+ goto done;
+ }
+ if (flags & CERTDB_VALID_CA) {
+ validCAOverride = PR_TRUE;
+ }
+ /* is it explicitly distrusted? */
+ if ((flags & CERTDB_TERMINAL_RECORD) &&
+ ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) {
+ /* untrusted -- the cert is explicitly untrusted, not
+ * just that it doesn't chain to a trusted cert */
+ PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
+ LOG_ERROR_OR_EXIT(log, cert, 0, flags);
+ }
+ }
+ if (!validCAOverride) {
+ /*
+ * Make sure that if this is an intermediate CA in the chain that
+ * it was given permission by its signer to be a CA.
+ */
+ /*
+ * if basicConstraints says it is a ca, then we check the
+ * nsCertType. If the nsCertType has any CA bits set, then
+ * it must have the right one.
+ */
+ if (!isca || (cert->nsCertType & NS_CERT_TYPE_CA)) {
+ isca = (cert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE;
+ }
+
+ if (!isca) {
+ PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
+ LOG_ERROR_OR_EXIT(log, cert, 0, 0);
+ }
+
+ /* make sure key usage allows cert signing */
+ if (CERT_CheckKeyUsage(cert, requiredCAKeyUsage) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
+ LOG_ERROR_OR_EXIT(log, cert, 0, requiredCAKeyUsage);
+ }
+ }
+ /* make sure that the issuer is not self signed. If it is, then
+ * stop here to prevent looping.
+ */
+ if (cert->isRoot) {
+ PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
+ LOG_ERROR(log, cert, 0, 0);
+ goto loser;
+ }
+
+ return CERT_VerifyCertChain(handle, cert, checkSig, certUsage, t,
+ wincx, log);
+loser:
+ rv = SECFailure;
+done:
+ return rv;
+}
+
+#define NEXT_USAGE() \
+ { \
+ i *= 2; \
+ certUsage++; \
+ continue; \
+ }
+
+#define VALID_USAGE() \
+ { \
+ NEXT_USAGE(); \
+ }
+
+#define INVALID_USAGE() \
+ { \
+ if (returnedUsages) { \
+ *returnedUsages &= (~i); \
+ } \
+ if (PR_TRUE == requiredUsage) { \
+ valid = SECFailure; \
+ } \
+ NEXT_USAGE(); \
+ }
+
+/*
+ * check the leaf cert against trust and usage.
+ * returns success if the cert is not distrusted. If the cert is
+ * trusted, then the trusted bool will be true.
+ * returns failure if the cert is distrusted. If failure, flags
+ * will return the flag bits that indicated distrust.
+ */
+SECStatus
+cert_CheckLeafTrust(CERTCertificate *cert, SECCertUsage certUsage,
+ unsigned int *failedFlags, PRBool *trusted)
+{
+ unsigned int flags;
+ CERTCertTrust trust;
+
+ *failedFlags = 0;
+ *trusted = PR_FALSE;
+
+ /* check trust flags to see if this cert is directly trusted */
+ if (CERT_GetCertTrust(cert, &trust) == SECSuccess) {
+ switch (certUsage) {
+ case certUsageSSLClient:
+ case certUsageSSLServer:
+ case certUsageIPsec:
+ flags = trust.sslFlags;
+
+ /* is the cert directly trusted or not trusted ? */
+ if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
+ * authoritative */
+ if (flags & CERTDB_TRUSTED) { /* trust this cert */
+ *trusted = PR_TRUE;
+ return SECSuccess;
+ } else { /* don't trust this cert */
+ *failedFlags = flags;
+ return SECFailure;
+ }
+ }
+ break;
+ case certUsageSSLServerWithStepUp:
+ /* XXX - step up certs can't be directly trusted, only distrust */
+ flags = trust.sslFlags;
+ if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
+ * authoritative */
+ if ((flags & CERTDB_TRUSTED) == 0) {
+ /* don't trust this cert */
+ *failedFlags = flags;
+ return SECFailure;
+ }
+ }
+ break;
+ case certUsageSSLCA:
+ flags = trust.sslFlags;
+ if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
+ * authoritative */
+ if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
+ /* don't trust this cert */
+ *failedFlags = flags;
+ return SECFailure;
+ }
+ }
+ break;
+ case certUsageEmailSigner:
+ case certUsageEmailRecipient:
+ flags = trust.emailFlags;
+ if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
+ * authoritative */
+ if (flags & CERTDB_TRUSTED) { /* trust this cert */
+ *trusted = PR_TRUE;
+ return SECSuccess;
+ } else { /* don't trust this cert */
+ *failedFlags = flags;
+ return SECFailure;
+ }
+ }
+
+ break;
+ case certUsageObjectSigner:
+ flags = trust.objectSigningFlags;
+
+ if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
+ * authoritative */
+ if (flags & CERTDB_TRUSTED) { /* trust this cert */
+ *trusted = PR_TRUE;
+ return SECSuccess;
+ } else { /* don't trust this cert */
+ *failedFlags = flags;
+ return SECFailure;
+ }
+ }
+ break;
+ case certUsageVerifyCA:
+ case certUsageStatusResponder:
+ flags = trust.sslFlags;
+ /* is the cert directly trusted or not trusted ? */
+ if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) ==
+ (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) {
+ *trusted = PR_TRUE;
+ return SECSuccess;
+ }
+ flags = trust.emailFlags;
+ /* is the cert directly trusted or not trusted ? */
+ if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) ==
+ (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) {
+ *trusted = PR_TRUE;
+ return SECSuccess;
+ }
+ flags = trust.objectSigningFlags;
+ /* is the cert directly trusted or not trusted ? */
+ if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) ==
+ (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) {
+ *trusted = PR_TRUE;
+ return SECSuccess;
+ }
+ /* fall through to test distrust */
+ case certUsageAnyCA:
+ case certUsageUserCertImport:
+ /* do we distrust these certs explicitly */
+ flags = trust.sslFlags;
+ if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
+ * authoritative */
+ if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
+ *failedFlags = flags;
+ return SECFailure;
+ }
+ }
+ flags = trust.emailFlags;
+ if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
+ * authoritative */
+ if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
+ *failedFlags = flags;
+ return SECFailure;
+ }
+ }
+ /* fall through */
+ case certUsageProtectedObjectSigner:
+ flags = trust.objectSigningFlags;
+ if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
+ * authoritative */
+ if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
+ *failedFlags = flags;
+ return SECFailure;
+ }
+ }
+ break;
+ }
+ }
+ return SECSuccess;
+}
+
+/*
+ * verify a certificate by checking if it's valid and that we
+ * trust the issuer.
+ *
+ * certificateUsage contains a bitfield of all cert usages that are
+ * required for verification to succeed
+ *
+ * a bitfield of cert usages is returned in *returnedUsages
+ * if requiredUsages is non-zero, the returned bitmap is only
+ * for those required usages, otherwise it is for all usages
+ *
+ */
+SECStatus
+CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool checkSig, SECCertificateUsage requiredUsages, PRTime t,
+ void *wincx, CERTVerifyLog *log, SECCertificateUsage *returnedUsages)
+{
+ SECStatus rv;
+ SECStatus valid;
+ unsigned int requiredKeyUsage;
+ unsigned int requiredCertType;
+ unsigned int flags;
+ unsigned int certType;
+ PRBool allowOverride;
+ SECCertTimeValidity validity;
+ CERTStatusConfig *statusConfig;
+ PRInt32 i;
+ SECCertUsage certUsage = 0;
+ PRBool checkedOCSP = PR_FALSE;
+ PRBool checkAllUsages = PR_FALSE;
+ PRBool revoked = PR_FALSE;
+ PRBool sigerror = PR_FALSE;
+ PRBool trusted = PR_FALSE;
+
+ if (!requiredUsages) {
+ /* there are no required usages, so the user probably wants to
+ get status for all usages */
+ checkAllUsages = PR_TRUE;
+ }
+
+ if (returnedUsages) {
+ *returnedUsages = 0;
+ } else {
+ /* we don't have a place to return status for all usages,
+ so we can skip checks for usages that aren't required */
+ checkAllUsages = PR_FALSE;
+ }
+ valid = SECSuccess; /* start off assuming cert is valid */
+
+ /* make sure that the cert is valid at time t */
+ allowOverride = (PRBool)((requiredUsages & certificateUsageSSLServer) ||
+ (requiredUsages & certificateUsageSSLServerWithStepUp) ||
+ (requiredUsages & certificateUsageIPsec));
+ validity = CERT_CheckCertValidTimes(cert, t, allowOverride);
+ if (validity != secCertTimeValid) {
+ valid = SECFailure;
+ LOG_ERROR_OR_EXIT(log, cert, 0, validity);
+ }
+
+ /* check key usage and netscape cert type */
+ cert_GetCertType(cert);
+ certType = cert->nsCertType;
+
+ for (i = 1; i <= certificateUsageHighest &&
+ (SECSuccess == valid || returnedUsages || log);) {
+ PRBool requiredUsage = (i & requiredUsages) ? PR_TRUE : PR_FALSE;
+ if (PR_FALSE == requiredUsage && PR_FALSE == checkAllUsages) {
+ NEXT_USAGE();
+ }
+ if (returnedUsages) {
+ *returnedUsages |= i; /* start off assuming this usage is valid */
+ }
+ switch (certUsage) {
+ case certUsageSSLClient:
+ case certUsageSSLServer:
+ case certUsageSSLServerWithStepUp:
+ case certUsageSSLCA:
+ case certUsageEmailSigner:
+ case certUsageEmailRecipient:
+ case certUsageObjectSigner:
+ case certUsageStatusResponder:
+ case certUsageIPsec:
+ rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE,
+ &requiredKeyUsage,
+ &requiredCertType);
+ if (rv != SECSuccess) {
+ PORT_Assert(0);
+ /* EXIT_IF_NOT_LOGGING(log); XXX ??? */
+ requiredKeyUsage = 0;
+ requiredCertType = 0;
+ INVALID_USAGE();
+ }
+ break;
+
+ case certUsageAnyCA:
+ case certUsageProtectedObjectSigner:
+ case certUsageUserCertImport:
+ case certUsageVerifyCA:
+ /* these usages cannot be verified */
+ NEXT_USAGE();
+
+ default:
+ PORT_Assert(0);
+ requiredKeyUsage = 0;
+ requiredCertType = 0;
+ INVALID_USAGE();
+ }
+ if (CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess) {
+ if (PR_TRUE == requiredUsage) {
+ PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
+ }
+ LOG_ERROR(log, cert, 0, requiredKeyUsage);
+ INVALID_USAGE();
+ }
+ if (!(certType & requiredCertType)) {
+ if (PR_TRUE == requiredUsage) {
+ PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE);
+ }
+ LOG_ERROR(log, cert, 0, requiredCertType);
+ INVALID_USAGE();
+ }
+
+ rv = cert_CheckLeafTrust(cert, certUsage, &flags, &trusted);
+ if (rv == SECFailure) {
+ if (PR_TRUE == requiredUsage) {
+ PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
+ }
+ LOG_ERROR(log, cert, 0, flags);
+ INVALID_USAGE();
+ } else if (trusted) {
+ VALID_USAGE();
+ }
+
+ if (PR_TRUE == revoked || PR_TRUE == sigerror) {
+ INVALID_USAGE();
+ }
+
+ rv = cert_VerifyCertChain(handle, cert,
+ checkSig, &sigerror,
+ certUsage, t, wincx, log,
+ &revoked);
+
+ if (rv != SECSuccess) {
+ /* EXIT_IF_NOT_LOGGING(log); XXX ???? */
+ INVALID_USAGE();
+ }
+
+ /*
+ * Check OCSP revocation status, but only if the cert we are checking
+ * is not a status responder itself. We only do this in the case
+ * where we checked the cert chain (above); explicit trust "wins"
+ * (avoids status checking, just as it avoids CRL checking) by
+ * bypassing this code.
+ */
+
+ if (PR_FALSE == checkedOCSP) {
+ checkedOCSP = PR_TRUE; /* only check OCSP once */
+ statusConfig = CERT_GetStatusConfig(handle);
+ if (requiredUsages != certificateUsageStatusResponder &&
+ statusConfig != NULL) {
+ if (statusConfig->statusChecker != NULL) {
+ rv = (*statusConfig->statusChecker)(handle, cert,
+ t, wincx);
+ if (rv != SECSuccess) {
+ LOG_ERROR(log, cert, 0, 0);
+ revoked = PR_TRUE;
+ INVALID_USAGE();
+ }
+ }
+ }
+ }
+
+ NEXT_USAGE();
+ }
+
+loser:
+ return (valid);
+}
+
+SECStatus
+CERT_VerifyCert(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool checkSig, SECCertUsage certUsage, PRTime t,
+ void *wincx, CERTVerifyLog *log)
+{
+ return cert_VerifyCertWithFlags(handle, cert, checkSig, certUsage, t,
+ CERT_VERIFYCERT_USE_DEFAULTS, wincx, log);
+}
+
+SECStatus
+cert_VerifyCertWithFlags(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool checkSig, SECCertUsage certUsage, PRTime t,
+ PRUint32 flags, void *wincx, CERTVerifyLog *log)
+{
+ SECStatus rv;
+ unsigned int requiredKeyUsage;
+ unsigned int requiredCertType;
+ unsigned int failedFlags;
+ unsigned int certType;
+ PRBool trusted;
+ PRBool allowOverride;
+ SECCertTimeValidity validity;
+ CERTStatusConfig *statusConfig;
+
+#ifdef notdef
+ /* check if this cert is in the Evil list */
+ rv = CERT_CheckForEvilCert(cert);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
+ LOG_ERROR_OR_EXIT(log, cert, 0, 0);
+ }
+#endif
+
+ /* make sure that the cert is valid at time t */
+ allowOverride = (PRBool)((certUsage == certUsageSSLServer) ||
+ (certUsage == certUsageSSLServerWithStepUp) ||
+ (certUsage == certUsageIPsec));
+ validity = CERT_CheckCertValidTimes(cert, t, allowOverride);
+ if (validity != secCertTimeValid) {
+ LOG_ERROR_OR_EXIT(log, cert, 0, validity);
+ }
+
+ /* check key usage and netscape cert type */
+ cert_GetCertType(cert);
+ certType = cert->nsCertType;
+ switch (certUsage) {
+ case certUsageSSLClient:
+ case certUsageSSLServer:
+ case certUsageSSLServerWithStepUp:
+ case certUsageIPsec:
+ case certUsageSSLCA:
+ case certUsageEmailSigner:
+ case certUsageEmailRecipient:
+ case certUsageObjectSigner:
+ case certUsageStatusResponder:
+ rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE,
+ &requiredKeyUsage,
+ &requiredCertType);
+ if (rv != SECSuccess) {
+ PORT_Assert(0);
+ EXIT_IF_NOT_LOGGING(log);
+ requiredKeyUsage = 0;
+ requiredCertType = 0;
+ }
+ break;
+ case certUsageVerifyCA:
+ case certUsageAnyCA:
+ requiredKeyUsage = KU_KEY_CERT_SIGN;
+ requiredCertType = NS_CERT_TYPE_CA;
+ if (!(certType & NS_CERT_TYPE_CA)) {
+ certType |= NS_CERT_TYPE_CA;
+ }
+ break;
+ default:
+ PORT_Assert(0);
+ EXIT_IF_NOT_LOGGING(log);
+ requiredKeyUsage = 0;
+ requiredCertType = 0;
+ }
+ if (CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
+ LOG_ERROR_OR_EXIT(log, cert, 0, requiredKeyUsage);
+ }
+ if (!(certType & requiredCertType)) {
+ PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE);
+ LOG_ERROR_OR_EXIT(log, cert, 0, requiredCertType);
+ }
+
+ rv = cert_CheckLeafTrust(cert, certUsage, &failedFlags, &trusted);
+ if (rv == SECFailure) {
+ PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
+ LOG_ERROR_OR_EXIT(log, cert, 0, failedFlags);
+ } else if (trusted) {
+ goto done;
+ }
+
+ rv = CERT_VerifyCertChain(handle, cert, checkSig, certUsage,
+ t, wincx, log);
+ if (rv != SECSuccess) {
+ EXIT_IF_NOT_LOGGING(log);
+ }
+
+ /*
+ * Check revocation status, but only if the cert we are checking is not a
+ * status responder itself and the caller did not ask us to skip the check.
+ * We only do this in the case where we checked the cert chain (above);
+ * explicit trust "wins" (avoids status checking, just as it avoids CRL
+ * checking, which is all done inside VerifyCertChain) by bypassing this
+ * code.
+ */
+ if (!(flags & CERT_VERIFYCERT_SKIP_OCSP) &&
+ certUsage != certUsageStatusResponder) {
+ statusConfig = CERT_GetStatusConfig(handle);
+ if (statusConfig && statusConfig->statusChecker) {
+ rv = (*statusConfig->statusChecker)(handle, cert,
+ t, wincx);
+ if (rv != SECSuccess) {
+ LOG_ERROR_OR_EXIT(log, cert, 0, 0);
+ }
+ }
+ }
+
+done:
+ if (log && log->head) {
+ return SECFailure;
+ }
+ return (SECSuccess);
+
+loser:
+ rv = SECFailure;
+
+ return (rv);
+}
+
+/*
+ * verify a certificate by checking if its valid and that we
+ * trust the issuer. Verify time against now.
+ */
+SECStatus
+CERT_VerifyCertificateNow(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool checkSig, SECCertificateUsage requiredUsages,
+ void *wincx, SECCertificateUsage *returnedUsages)
+{
+ return (CERT_VerifyCertificate(handle, cert, checkSig,
+ requiredUsages, PR_Now(), wincx, NULL, returnedUsages));
+}
+
+/* obsolete, do not use for new code */
+SECStatus
+CERT_VerifyCertNow(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool checkSig, SECCertUsage certUsage, void *wincx)
+{
+ return (CERT_VerifyCert(handle, cert, checkSig,
+ certUsage, PR_Now(), wincx, NULL));
+}
+
+/* [ FROM pcertdb.c ] */
+/*
+ * Supported usage values and types:
+ * certUsageSSLClient
+ * certUsageSSLServer
+ * certUsageSSLServerWithStepUp
+ * certUsageIPsec
+ * certUsageEmailSigner
+ * certUsageEmailRecipient
+ * certUsageObjectSigner
+ */
+
+CERTCertificate *
+CERT_FindMatchingCert(CERTCertDBHandle *handle, SECItem *derName,
+ CERTCertOwner owner, SECCertUsage usage,
+ PRBool preferTrusted, PRTime validTime, PRBool validOnly)
+{
+ CERTCertList *certList = NULL;
+ CERTCertificate *cert = NULL;
+ CERTCertTrust certTrust;
+ unsigned int requiredTrustFlags;
+ SECTrustType requiredTrustType;
+ unsigned int flags;
+
+ PRBool lookingForCA = PR_FALSE;
+ SECStatus rv;
+ CERTCertListNode *node;
+ CERTCertificate *saveUntrustedCA = NULL;
+
+ /* if preferTrusted is set, must be a CA cert */
+ PORT_Assert(!(preferTrusted && (owner != certOwnerCA)));
+
+ if (owner == certOwnerCA) {
+ lookingForCA = PR_TRUE;
+ if (preferTrusted) {
+ rv = CERT_TrustFlagsForCACertUsage(usage, &requiredTrustFlags,
+ &requiredTrustType);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ requiredTrustFlags |= CERTDB_VALID_CA;
+ }
+ }
+
+ certList = CERT_CreateSubjectCertList(NULL, handle, derName, validTime,
+ validOnly);
+ if (certList != NULL) {
+ rv = CERT_FilterCertListByUsage(certList, usage, lookingForCA);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ node = CERT_LIST_HEAD(certList);
+
+ while (!CERT_LIST_END(node, certList)) {
+ cert = node->cert;
+
+ /* looking for a trusted CA cert */
+ if ((owner == certOwnerCA) && preferTrusted &&
+ (requiredTrustType != trustTypeNone)) {
+
+ if (CERT_GetCertTrust(cert, &certTrust) != SECSuccess) {
+ flags = 0;
+ } else {
+ flags = SEC_GET_TRUST_FLAGS(&certTrust, requiredTrustType);
+ }
+
+ if ((flags & requiredTrustFlags) != requiredTrustFlags) {
+ /* cert is not trusted */
+ /* if this is the first cert to get this far, then save
+ * it, so we can use it if we can't find a trusted one
+ */
+ if (saveUntrustedCA == NULL) {
+ saveUntrustedCA = cert;
+ }
+ goto endloop;
+ }
+ }
+ /* if we got this far, then this cert meets all criteria */
+ break;
+
+ endloop:
+ node = CERT_LIST_NEXT(node);
+ cert = NULL;
+ }
+
+ /* use the saved one if we have it */
+ if (cert == NULL) {
+ cert = saveUntrustedCA;
+ }
+
+ /* if we found one then bump the ref count before freeing the list */
+ if (cert != NULL) {
+ /* bump the ref count */
+ cert = CERT_DupCertificate(cert);
+ }
+
+ CERT_DestroyCertList(certList);
+ }
+
+ return (cert);
+
+loser:
+ if (certList != NULL) {
+ CERT_DestroyCertList(certList);
+ }
+
+ return (NULL);
+}
+
+/* [ From certdb.c ] */
+/*
+ * 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)
+{
+ CERTCertificate *issuerCert = NULL;
+ CERTCertificate *subjectCert;
+ CERTCertListNode *node, *freenode;
+ CERTCertificate *cert;
+ int n;
+ char **names;
+ PRBool found;
+ PRTime time;
+
+ if (nCANames <= 0) {
+ return (SECSuccess);
+ }
+
+ time = PR_Now();
+
+ node = CERT_LIST_HEAD(certList);
+
+ while (!CERT_LIST_END(node, certList)) {
+ cert = node->cert;
+
+ subjectCert = CERT_DupCertificate(cert);
+
+ /* traverse the CA certs for this cert */
+ found = PR_FALSE;
+ while (subjectCert != NULL) {
+ n = nCANames;
+ names = caNames;
+
+ if (subjectCert->issuerName != NULL) {
+ while (n > 0) {
+ if (PORT_Strcmp(*names, subjectCert->issuerName) == 0) {
+ found = PR_TRUE;
+ break;
+ }
+
+ n--;
+ names++;
+ }
+ }
+
+ if (found) {
+ break;
+ }
+
+ issuerCert = CERT_FindCertIssuer(subjectCert, time, usage);
+ if (issuerCert == subjectCert) {
+ CERT_DestroyCertificate(issuerCert);
+ issuerCert = NULL;
+ break;
+ }
+ CERT_DestroyCertificate(subjectCert);
+ subjectCert = issuerCert;
+ }
+ CERT_DestroyCertificate(subjectCert);
+ if (!found) {
+ /* CA was not found, so remove this cert from the list */
+ freenode = node;
+ node = CERT_LIST_NEXT(node);
+ CERT_RemoveCertListNode(freenode);
+ } else {
+ /* CA was found, so leave it in the list */
+ node = CERT_LIST_NEXT(node);
+ }
+ }
+
+ return (SECSuccess);
+}
+
+/*
+ * 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)
+{
+ SECCertTimeValidity validity;
+ char *nickname = NULL, *tmpstr = NULL;
+ const char *srcNickname = cert->nickname;
+ if (!srcNickname) {
+ srcNickname = "{???}";
+ }
+
+ validity = CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE);
+
+ /* if the cert is good, then just use the nickname directly */
+ if (validity == secCertTimeValid) {
+ if (arena == NULL) {
+ nickname = PORT_Strdup(srcNickname);
+ } else {
+ nickname = PORT_ArenaStrdup(arena, srcNickname);
+ }
+
+ if (nickname == NULL) {
+ goto loser;
+ }
+ } else {
+
+ /* if the cert is not valid, then tack one of the strings on the
+ * end
+ */
+ if (validity == secCertTimeExpired) {
+ tmpstr = PR_smprintf("%s%s", srcNickname,
+ expiredString);
+ } else if (validity == secCertTimeNotValidYet) {
+ /* not yet valid */
+ tmpstr = PR_smprintf("%s%s", srcNickname,
+ notYetGoodString);
+ } else {
+ /* undetermined */
+ tmpstr = PR_smprintf("%s",
+ "(NULL) (Validity Unknown)");
+ }
+
+ if (tmpstr == NULL) {
+ goto loser;
+ }
+
+ if (arena) {
+ /* copy the string into the arena and free the malloc'd one */
+ nickname = PORT_ArenaStrdup(arena, tmpstr);
+ PORT_Free(tmpstr);
+ } else {
+ nickname = tmpstr;
+ }
+ if (nickname == NULL) {
+ goto loser;
+ }
+ }
+ return (nickname);
+
+loser:
+ return (NULL);
+}
+
+/*
+ * 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)
+{
+ CERTCertNicknames *names;
+ PLArenaPool *arena;
+ CERTCertListNode *node;
+ char **nn;
+
+ /* allocate an arena */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return (NULL);
+ }
+
+ /* allocate the structure */
+ names = PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames));
+ if (names == NULL) {
+ goto loser;
+ }
+
+ /* init the structure */
+ names->arena = arena;
+ names->head = NULL;
+ names->numnicknames = 0;
+ names->nicknames = NULL;
+ names->totallen = 0;
+
+ /* count the certs in the list */
+ node = CERT_LIST_HEAD(certList);
+ while (!CERT_LIST_END(node, certList)) {
+ names->numnicknames++;
+ node = CERT_LIST_NEXT(node);
+ }
+
+ /* allocate nicknames array */
+ names->nicknames = PORT_ArenaAlloc(arena,
+ sizeof(char *) * names->numnicknames);
+ if (names->nicknames == NULL) {
+ goto loser;
+ }
+
+ /* just in case printf can't deal with null strings */
+ if (expiredString == NULL) {
+ expiredString = "";
+ }
+
+ if (notYetGoodString == NULL) {
+ notYetGoodString = "";
+ }
+
+ /* traverse the list of certs and collect the nicknames */
+ nn = names->nicknames;
+ node = CERT_LIST_HEAD(certList);
+ while (!CERT_LIST_END(node, certList)) {
+ *nn = CERT_GetCertNicknameWithValidity(arena, node->cert,
+ expiredString,
+ notYetGoodString);
+ if (*nn == NULL) {
+ goto loser;
+ }
+
+ names->totallen += PORT_Strlen(*nn);
+
+ nn++;
+ node = CERT_LIST_NEXT(node);
+ }
+
+ return (names);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ return (NULL);
+}
+
+/*
+ * 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)
+{
+ int explen, nyglen, namelen;
+ int retlen;
+ char *retstr;
+
+ namelen = PORT_Strlen(namestring);
+ explen = PORT_Strlen(expiredString);
+ nyglen = PORT_Strlen(notYetGoodString);
+
+ if (namelen > explen) {
+ if (PORT_Strcmp(expiredString, &namestring[namelen - explen]) == 0) {
+ retlen = namelen - explen;
+ retstr = (char *)PORT_Alloc(retlen + 1);
+ if (retstr == NULL) {
+ goto loser;
+ }
+
+ PORT_Memcpy(retstr, namestring, retlen);
+ retstr[retlen] = '\0';
+ goto done;
+ }
+ }
+
+ if (namelen > nyglen) {
+ if (PORT_Strcmp(notYetGoodString, &namestring[namelen - nyglen]) == 0) {
+ retlen = namelen - nyglen;
+ retstr = (char *)PORT_Alloc(retlen + 1);
+ if (retstr == NULL) {
+ goto loser;
+ }
+
+ PORT_Memcpy(retstr, namestring, retlen);
+ retstr[retlen] = '\0';
+ goto done;
+ }
+ }
+
+ /* if name string is shorter than either invalid string, then it must
+ * be a raw nickname
+ */
+ retstr = PORT_Strdup(namestring);
+
+done:
+ return (retstr);
+
+loser:
+ return (NULL);
+}
+
+CERTCertList *
+CERT_GetCertChainFromCert(CERTCertificate *cert, PRTime time, SECCertUsage usage)
+{
+ CERTCertList *chain = NULL;
+ int count = 0;
+
+ if (NULL == cert) {
+ return NULL;
+ }
+
+ cert = CERT_DupCertificate(cert);
+ if (NULL == cert) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ chain = CERT_NewCertList();
+ if (NULL == chain) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ while (cert != NULL && ++count <= CERT_MAX_CERT_CHAIN) {
+ if (SECSuccess != CERT_AddCertToListTail(chain, cert)) {
+ /* return partial chain */
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return chain;
+ }
+
+ if (cert->isRoot) {
+ /* return complete chain */
+ return chain;
+ }
+
+ cert = CERT_FindCertIssuer(cert, time, usage);
+ }
+
+ /* return partial chain */
+ PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
+ return chain;
+}
diff --git a/security/nss/lib/certhigh/certvfypkix.c b/security/nss/lib/certhigh/certvfypkix.c
new file mode 100644
index 0000000000..1f6762b919
--- /dev/null
+++ b/security/nss/lib/certhigh/certvfypkix.c
@@ -0,0 +1,2302 @@
+/* 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/. */
+/*
+ * nss_pkix_proxy.h
+ *
+ * PKIX - NSS proxy functions
+ *
+ * NOTE: All structures, functions, data types are parts of library private
+ * api and are subjects to change in any following releases.
+ *
+ */
+#include "prerror.h"
+#include "prprf.h"
+
+#include "nspr.h"
+#include "pk11func.h"
+#include "certdb.h"
+#include "cert.h"
+#include "secerr.h"
+#include "nssb64.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "pkit.h"
+
+#ifndef NSS_DISABLE_LIBPKIX
+#include "pkix_pl_common.h"
+
+extern PRLogModuleInfo *pkixLog;
+
+#ifdef PKIX_OBJECT_LEAK_TEST
+
+extern PKIX_UInt32
+pkix_pl_lifecycle_ObjectLeakCheck(int *);
+
+extern SECStatus
+pkix_pl_lifecycle_ObjectTableUpdate(int *objCountTable);
+
+PRInt32 parallelFnInvocationCount;
+#endif /* PKIX_OBJECT_LEAK_TEST */
+
+static PRBool usePKIXValidationEngine = PR_FALSE;
+#endif /* NSS_DISABLE_LIBPKIX */
+
+/*
+ * FUNCTION: CERT_SetUsePKIXForValidation
+ * DESCRIPTION:
+ *
+ * Enables or disables use of libpkix for certificate validation
+ *
+ * PARAMETERS:
+ * "enable"
+ * PR_TRUE: enables use of libpkix for cert validation.
+ * PR_FALSE: disables.
+ * THREAD SAFETY:
+ * NOT Thread Safe.
+ * RETURNS:
+ * Returns SECSuccess if successfully enabled
+ */
+SECStatus
+CERT_SetUsePKIXForValidation(PRBool enable)
+{
+#ifdef NSS_DISABLE_LIBPKIX
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return SECFailure;
+#else
+ usePKIXValidationEngine = (enable > 0) ? PR_TRUE : PR_FALSE;
+ return SECSuccess;
+#endif /* NSS_DISABLE_LIBPKIX */
+}
+
+/*
+ * FUNCTION: CERT_GetUsePKIXForValidation
+ * DESCRIPTION:
+ *
+ * Checks if libpkix building function should be use for certificate
+ * chain building.
+ *
+ * PARAMETERS:
+ * NONE
+ * THREAD SAFETY:
+ * NOT Thread Safe
+ * RETURNS:
+ * Returns PR_TRUE if libpkix should be used. PR_FALSE otherwise.
+ */
+PRBool
+CERT_GetUsePKIXForValidation()
+{
+#ifdef NSS_DISABLE_LIBPKIX
+ return PR_FALSE;
+#else
+ return usePKIXValidationEngine;
+#endif /* NSS_DISABLE_LIBPKIX */
+}
+
+#ifndef NSS_DISABLE_LIBPKIX
+#ifdef NOTDEF
+/*
+ * FUNCTION: cert_NssKeyUsagesToPkix
+ * DESCRIPTION:
+ *
+ * Converts nss key usage bit field(PRUint32) to pkix key usage
+ * bit field.
+ *
+ * PARAMETERS:
+ * "nssKeyUsage"
+ * Nss key usage bit field.
+ * "pkixKeyUsage"
+ * Pkix key usage big field.
+ * "plContext"
+ * Platform-specific context pointer.
+ * THREAD SAFETY:
+ * Thread Safe (see Thread Safety Definitions in Programmer's Guide)
+ * RETURNS:
+ * Returns NULL if the function succeeds.
+ * Returns a Fatal Error if the function fails in an unrecoverable way.
+ */
+static PKIX_Error *
+cert_NssKeyUsagesToPkix(
+ PRUint32 nssKeyUsage,
+ PKIX_UInt32 *pPkixKeyUsage,
+ void *plContext)
+{
+ PKIX_UInt32 pkixKeyUsage = 0;
+
+ PKIX_ENTER(CERTVFYPKIX, "cert_NssKeyUsagesToPkix");
+ PKIX_NULLCHECK_ONE(pPkixKeyUsage);
+
+ *pPkixKeyUsage = 0;
+
+ if (nssKeyUsage & KU_DIGITAL_SIGNATURE) {
+ pkixKeyUsage |= PKIX_DIGITAL_SIGNATURE;
+ }
+
+ if (nssKeyUsage & KU_NON_REPUDIATION) {
+ pkixKeyUsage |= PKIX_NON_REPUDIATION;
+ }
+
+ if (nssKeyUsage & KU_KEY_ENCIPHERMENT) {
+ pkixKeyUsage |= PKIX_KEY_ENCIPHERMENT;
+ }
+
+ if (nssKeyUsage & KU_DATA_ENCIPHERMENT) {
+ pkixKeyUsage |= PKIX_DATA_ENCIPHERMENT;
+ }
+
+ if (nssKeyUsage & KU_KEY_AGREEMENT) {
+ pkixKeyUsage |= PKIX_KEY_AGREEMENT;
+ }
+
+ if (nssKeyUsage & KU_KEY_CERT_SIGN) {
+ pkixKeyUsage |= PKIX_KEY_CERT_SIGN;
+ }
+
+ if (nssKeyUsage & KU_CRL_SIGN) {
+ pkixKeyUsage |= PKIX_CRL_SIGN;
+ }
+
+ if (nssKeyUsage & KU_ENCIPHER_ONLY) {
+ pkixKeyUsage |= PKIX_ENCIPHER_ONLY;
+ }
+
+ /* Not supported. XXX we should support this once it is
+ * fixed in NSS */
+ /* pkixKeyUsage |= PKIX_DECIPHER_ONLY; */
+
+ *pPkixKeyUsage = pkixKeyUsage;
+
+ PKIX_RETURN(CERTVFYPKIX);
+}
+
+extern SECOidTag ekuOidStrings[];
+
+enum {
+ ekuIndexSSLServer = 0,
+ ekuIndexSSLClient,
+ ekuIndexCodeSigner,
+ ekuIndexEmail,
+ ekuIndexTimeStamp,
+ ekuIndexStatusResponder,
+ ekuIndexUnknown
+} ekuIndex;
+
+typedef struct {
+ SECCertUsage certUsage;
+ PRUint32 ekuStringIndex;
+} SECCertUsageToEku;
+
+const SECCertUsageToEku certUsageEkuStringMap[] = {
+ { certUsageSSLClient, ekuIndexSSLClient },
+ { certUsageSSLServer, ekuIndexSSLServer },
+ { certUsageSSLCA, ekuIndexSSLServer },
+ { certUsageEmailSigner, ekuIndexEmail },
+ { certUsageEmailRecipient, ekuIndexEmail },
+ { certUsageObjectSigner, ekuIndexCodeSigner },
+ { certUsageUserCertImport, ekuIndexUnknown },
+ { certUsageVerifyCA, ekuIndexUnknown },
+ { certUsageProtectedObjectSigner, ekuIndexUnknown },
+ { certUsageStatusResponder, ekuIndexStatusResponder },
+ { certUsageAnyCA, ekuIndexUnknown },
+};
+
+/*
+ * FUNCTION: cert_NssCertificateUsageToPkixKUAndEKU
+ * DESCRIPTION:
+ *
+ * Converts nss CERTCertificateUsage bit field to pkix key and
+ * extended key usages.
+ *
+ * PARAMETERS:
+ * "cert"
+ * Pointer to CERTCertificate structure of validating cert.
+ * "requiredCertUsages"
+ * Required usage that will be converted to pkix eku and ku.
+ * "requiredKeyUsage",
+ * Additional key usages impose to cert.
+ * "isCA",
+ * it true, convert usages for cert that is a CA cert.
+ * "ppkixEKUList"
+ * Returned address of a list of pkix extended key usages.
+ * "ppkixKU"
+ * Returned address of pkix required key usages bit field.
+ * "plContext"
+ * Platform-specific context pointer.
+ * THREAD SAFETY:
+ * Thread Safe (see Thread Safety Definitions in Programmer's Guide)
+ * RETURNS:
+ * Returns NULL if the function succeeds.
+ * Returns a Cert Verify Error if the function fails in an unrecoverable way.
+ * Returns a Fatal Error if the function fails in an unrecoverable way.
+ */
+static PKIX_Error *
+cert_NssCertificateUsageToPkixKUAndEKU(
+ CERTCertificate *cert,
+ SECCertUsage requiredCertUsage,
+ PRUint32 requiredKeyUsages,
+ PRBool isCA,
+ PKIX_List **ppkixEKUList,
+ PKIX_UInt32 *ppkixKU,
+ void *plContext)
+{
+ PKIX_List *ekuOidsList = NULL;
+ PKIX_PL_OID *ekuOid = NULL;
+ int i = 0;
+ int ekuIndex = ekuIndexUnknown;
+
+ PKIX_ENTER(CERTVFYPKIX, "cert_NssCertificateUsageToPkixEku");
+ PKIX_NULLCHECK_TWO(ppkixEKUList, ppkixKU);
+
+ PKIX_CHECK(
+ PKIX_List_Create(&ekuOidsList, plContext),
+ PKIX_LISTCREATEFAILED);
+
+ for (; i < PR_ARRAY_SIZE(certUsageEkuStringMap); i++) {
+ const SECCertUsageToEku *usageToEkuElem =
+ &certUsageEkuStringMap[i];
+ if (usageToEkuElem->certUsage == requiredCertUsage) {
+ ekuIndex = usageToEkuElem->ekuStringIndex;
+ break;
+ }
+ }
+ if (ekuIndex != ekuIndexUnknown) {
+ PRUint32 reqKeyUsage = 0;
+ PRUint32 reqCertType = 0;
+
+ CERT_KeyUsageAndTypeForCertUsage(requiredCertUsage, isCA,
+ &reqKeyUsage,
+ &reqCertType);
+
+ requiredKeyUsages |= reqKeyUsage;
+
+ PKIX_CHECK(
+ PKIX_PL_OID_Create(ekuOidStrings[ekuIndex], &ekuOid,
+ plContext),
+ PKIX_OIDCREATEFAILED);
+
+ PKIX_CHECK(
+ PKIX_List_AppendItem(ekuOidsList, (PKIX_PL_Object *)ekuOid,
+ plContext),
+ PKIX_LISTAPPENDITEMFAILED);
+
+ PKIX_DECREF(ekuOid);
+ }
+
+ PKIX_CHECK(
+ cert_NssKeyUsagesToPkix(requiredKeyUsages, ppkixKU, plContext),
+ PKIX_NSSCERTIFICATEUSAGETOPKIXKUANDEKUFAILED);
+
+ *ppkixEKUList = ekuOidsList;
+ ekuOidsList = NULL;
+
+cleanup:
+
+ PKIX_DECREF(ekuOid);
+ PKIX_DECREF(ekuOidsList);
+
+ PKIX_RETURN(CERTVFYPKIX);
+}
+
+#endif
+
+/*
+ * FUNCTION: cert_ProcessingParamsSetKeyAndCertUsage
+ * DESCRIPTION:
+ *
+ * Converts cert usage to pkix KU type and sets
+ * converted data into PKIX_ProcessingParams object. It also sets
+ * proper cert usage into nsscontext object.
+ *
+ * PARAMETERS:
+ * "procParams"
+ * Pointer to PKIX_ProcessingParams used during validation.
+ * "requiredCertUsage"
+ * Required certificate usages the certificate and chain is built and
+ * validated for.
+ * "requiredKeyUsage"
+ * Request additional key usages the certificate should be validated for.
+ * "plContext"
+ * Platform-specific context pointer.
+ * THREAD SAFETY:
+ * Thread Safe (see Thread Safety Definitions in Programmer's Guide)
+ * RETURNS:
+ * Returns NULL if the function succeeds.
+ * Returns a Cert Verify Error if the function fails in an unrecoverable way.
+ * Returns a Fatal Error if the function fails in an unrecoverable way.
+ */
+static PKIX_Error *
+cert_ProcessingParamsSetKeyAndCertUsage(
+ PKIX_ProcessingParams *procParams,
+ SECCertUsage requiredCertUsage,
+ PRUint32 requiredKeyUsages,
+ void *plContext)
+{
+ PKIX_CertSelector *certSelector = NULL;
+ PKIX_ComCertSelParams *certSelParams = NULL;
+ PKIX_PL_NssContext *nssContext = (PKIX_PL_NssContext *)plContext;
+
+ PKIX_ENTER(CERTVFYPKIX, "cert_ProcessingParamsSetKeyAndCertUsage");
+ PKIX_NULLCHECK_TWO(procParams, nssContext);
+
+ PKIX_CHECK(
+ pkix_pl_NssContext_SetCertUsage(
+ ((SECCertificateUsage)1) << requiredCertUsage, nssContext),
+ PKIX_NSSCONTEXTSETCERTUSAGEFAILED);
+
+ if (requiredKeyUsages) {
+ PKIX_CHECK(
+ PKIX_ProcessingParams_GetTargetCertConstraints(procParams,
+ &certSelector, plContext),
+ PKIX_PROCESSINGPARAMSGETTARGETCERTCONSTRAINTSFAILED);
+
+ PKIX_CHECK(
+ PKIX_CertSelector_GetCommonCertSelectorParams(certSelector,
+ &certSelParams, plContext),
+ PKIX_CERTSELECTORGETCOMMONCERTSELECTORPARAMSFAILED);
+
+ PKIX_CHECK(
+ PKIX_ComCertSelParams_SetKeyUsage(certSelParams, requiredKeyUsages,
+ plContext),
+ PKIX_COMCERTSELPARAMSSETKEYUSAGEFAILED);
+ }
+cleanup:
+ PKIX_DECREF(certSelector);
+ PKIX_DECREF(certSelParams);
+
+ PKIX_RETURN(CERTVFYPKIX);
+}
+
+/*
+ * Unused parameters:
+ *
+ * CERTCertList *initialChain,
+ * CERTCertStores certStores,
+ * CERTCertRevCheckers certRevCheckers,
+ * CERTCertChainCheckers certChainCheckers,
+ * SECItem *initPolicies,
+ * PRBool policyQualifierRejected,
+ * PRBool anyPolicyInhibited,
+ * PRBool reqExplicitPolicy,
+ * PRBool policyMappingInhibited,
+ * PKIX_CertSelector certConstraints,
+ */
+
+/*
+ * FUNCTION: cert_CreatePkixProcessingParams
+ * DESCRIPTION:
+ *
+ * Creates and fills in PKIX_ProcessingParams structure to be used
+ * for certificate chain building.
+ *
+ * PARAMETERS:
+ * "cert"
+ * Pointer to the CERTCertificate: the leaf certificate of a chain.
+ * "time"
+ * Validity time.
+ * "wincx"
+ * Nss db password token.
+ * "useArena"
+ * Flags to use arena for data allocation during chain building process.
+ * "pprocParams"
+ * Address to return created processing parameters.
+ * "plContext"
+ * Platform-specific context pointer.
+ * THREAD SAFETY:
+ * Thread Safe (see Thread Safety Definitions in Programmer's Guide)
+ * RETURNS:
+ * Returns NULL if the function succeeds.
+ * Returns a Cert Verify Error if the function fails in an unrecoverable way.
+ * Returns a Fatal Error if the function fails in an unrecoverable way.
+ */
+static PKIX_Error *
+cert_CreatePkixProcessingParams(
+ CERTCertificate *cert,
+ PRBool checkSig,
+ PRTime time,
+ void *wincx,
+ PRBool useArena,
+ PRBool disableOCSPRemoteFetching,
+ PKIX_ProcessingParams **pprocParams,
+ void **pplContext)
+{
+ PKIX_List *anchors = NULL;
+ PKIX_PL_Cert *targetCert = NULL;
+ PKIX_PL_Date *date = NULL;
+ PKIX_ProcessingParams *procParams = NULL;
+ PKIX_CertSelector *certSelector = NULL;
+ PKIX_ComCertSelParams *certSelParams = NULL;
+ PKIX_CertStore *certStore = NULL;
+ PKIX_List *certStores = NULL;
+ PKIX_RevocationChecker *revChecker = NULL;
+ PKIX_UInt32 methodFlags = 0;
+ void *plContext = NULL;
+ CERTStatusConfig *statusConfig = NULL;
+
+ PKIX_ENTER(CERTVFYPKIX, "cert_CreatePkixProcessingParams");
+ PKIX_NULLCHECK_TWO(cert, pprocParams);
+
+ PKIX_CHECK(
+ PKIX_PL_NssContext_Create(0, useArena, wincx, &plContext),
+ PKIX_NSSCONTEXTCREATEFAILED);
+
+ *pplContext = plContext;
+
+ /* Functions should be implemented in patch for 390532 */
+ PKIX_CHECK(
+ pkix_pl_NssContext_SetCertSignatureCheck(checkSig,
+ (PKIX_PL_NssContext *)plContext),
+ PKIX_NSSCONTEXTSETCERTSIGNCHECKFAILED);
+
+ PKIX_CHECK(
+ PKIX_ProcessingParams_Create(&procParams, plContext),
+ PKIX_PROCESSINGPARAMSCREATEFAILED);
+
+ PKIX_CHECK(
+ PKIX_ComCertSelParams_Create(&certSelParams, plContext),
+ PKIX_COMCERTSELPARAMSCREATEFAILED);
+
+ PKIX_CHECK(
+ PKIX_PL_Cert_CreateFromCERTCertificate(cert, &targetCert, plContext),
+ PKIX_CERTCREATEWITHNSSCERTFAILED);
+
+ PKIX_CHECK(
+ PKIX_ComCertSelParams_SetCertificate(certSelParams,
+ targetCert, plContext),
+ PKIX_COMCERTSELPARAMSSETCERTIFICATEFAILED);
+
+ PKIX_CHECK(
+ PKIX_CertSelector_Create(NULL, NULL, &certSelector, plContext),
+ PKIX_COULDNOTCREATECERTSELECTOROBJECT);
+
+ PKIX_CHECK(
+ PKIX_CertSelector_SetCommonCertSelectorParams(certSelector,
+ certSelParams, plContext),
+ PKIX_CERTSELECTORSETCOMMONCERTSELECTORPARAMSFAILED);
+
+ PKIX_CHECK(
+ PKIX_ProcessingParams_SetTargetCertConstraints(procParams,
+ certSelector, plContext),
+ PKIX_PROCESSINGPARAMSSETTARGETCERTCONSTRAINTSFAILED);
+
+ /* Turn off quialification of target cert since leaf cert is
+ * already check for date validity, key usages and extended
+ * key usages. */
+ PKIX_CHECK(
+ PKIX_ProcessingParams_SetQualifyTargetCert(procParams, PKIX_FALSE,
+ plContext),
+ PKIX_PROCESSINGPARAMSSETQUALIFYTARGETCERTFLAGFAILED);
+
+ PKIX_CHECK(
+ PKIX_PL_Pk11CertStore_Create(&certStore, plContext),
+ PKIX_PK11CERTSTORECREATEFAILED);
+
+ PKIX_CHECK(
+ PKIX_List_Create(&certStores, plContext),
+ PKIX_UNABLETOCREATELIST);
+
+ PKIX_CHECK(
+ PKIX_List_AppendItem(certStores, (PKIX_PL_Object *)certStore,
+ plContext),
+ PKIX_LISTAPPENDITEMFAILED);
+
+ PKIX_CHECK(
+ PKIX_ProcessingParams_SetCertStores(procParams, certStores,
+ plContext),
+ PKIX_PROCESSINGPARAMSADDCERTSTOREFAILED);
+
+ PKIX_CHECK(
+ PKIX_PL_Date_CreateFromPRTime(time, &date, plContext),
+ PKIX_DATECREATEFROMPRTIMEFAILED);
+
+ PKIX_CHECK(
+ PKIX_ProcessingParams_SetDate(procParams, date, plContext),
+ PKIX_PROCESSINGPARAMSSETDATEFAILED);
+
+ PKIX_CHECK(
+ PKIX_RevocationChecker_Create(
+ PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST |
+ PKIX_REV_MI_NO_OVERALL_INFO_REQUIREMENT,
+ PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST |
+ PKIX_REV_MI_NO_OVERALL_INFO_REQUIREMENT,
+ &revChecker, plContext),
+ PKIX_REVOCATIONCHECKERCREATEFAILED);
+
+ PKIX_CHECK(
+ PKIX_ProcessingParams_SetRevocationChecker(procParams, revChecker,
+ plContext),
+ PKIX_PROCESSINGPARAMSSETREVOCATIONCHECKERFAILED);
+
+ /* CRL method flags */
+ methodFlags =
+ PKIX_REV_M_TEST_USING_THIS_METHOD |
+ PKIX_REV_M_FORBID_NETWORK_FETCHING |
+ PKIX_REV_M_SKIP_TEST_ON_MISSING_SOURCE | /* 0 */
+ PKIX_REV_M_IGNORE_MISSING_FRESH_INFO | /* 0 */
+ PKIX_REV_M_CONTINUE_TESTING_ON_FRESH_INFO;
+
+ /* add CRL revocation method to check the leaf certificate */
+ PKIX_CHECK(
+ PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams,
+ PKIX_RevocationMethod_CRL, methodFlags,
+ 0, NULL, PKIX_TRUE, plContext),
+ PKIX_REVOCATIONCHECKERADDMETHODFAILED);
+
+ /* add CRL revocation method for other certs in the chain. */
+ PKIX_CHECK(
+ PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams,
+ PKIX_RevocationMethod_CRL, methodFlags,
+ 0, NULL, PKIX_FALSE, plContext),
+ PKIX_REVOCATIONCHECKERADDMETHODFAILED);
+
+ /* For compatibility with the old code, need to check that
+ * statusConfig is set in the db handle and status checker
+ * is defined befor allow ocsp status check on the leaf cert.*/
+ statusConfig = CERT_GetStatusConfig(CERT_GetDefaultCertDB());
+ if (statusConfig != NULL && statusConfig->statusChecker != NULL) {
+
+ /* Enable OCSP revocation checking for the leaf cert. */
+ /* OCSP method flags */
+ methodFlags =
+ PKIX_REV_M_TEST_USING_THIS_METHOD |
+ PKIX_REV_M_ALLOW_NETWORK_FETCHING | /* 0 */
+ PKIX_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE | /* 0 */
+ PKIX_REV_M_SKIP_TEST_ON_MISSING_SOURCE | /* 0 */
+ PKIX_REV_M_IGNORE_MISSING_FRESH_INFO | /* 0 */
+ PKIX_REV_M_CONTINUE_TESTING_ON_FRESH_INFO;
+
+ /* Disabling ocsp fetching when checking the status
+ * of ocsp response signer. Here and in the next if,
+ * adjust flags for ocsp signer cert validation case. */
+ if (disableOCSPRemoteFetching) {
+ methodFlags |= PKIX_REV_M_FORBID_NETWORK_FETCHING;
+ }
+
+ if (ocsp_FetchingFailureIsVerificationFailure() &&
+ !disableOCSPRemoteFetching) {
+ methodFlags |=
+ PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO;
+ }
+
+ /* add OCSP revocation method to check only the leaf certificate.*/
+ PKIX_CHECK(
+ PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams,
+ PKIX_RevocationMethod_OCSP, methodFlags,
+ 1, NULL, PKIX_TRUE, plContext),
+ PKIX_REVOCATIONCHECKERADDMETHODFAILED);
+ }
+
+ PKIX_CHECK(
+ PKIX_ProcessingParams_SetAnyPolicyInhibited(procParams, PR_FALSE,
+ plContext),
+ PKIX_PROCESSINGPARAMSSETANYPOLICYINHIBITED);
+
+ PKIX_CHECK(
+ PKIX_ProcessingParams_SetExplicitPolicyRequired(procParams, PR_FALSE,
+ plContext),
+ PKIX_PROCESSINGPARAMSSETEXPLICITPOLICYREQUIRED);
+
+ PKIX_CHECK(
+ PKIX_ProcessingParams_SetPolicyMappingInhibited(procParams, PR_FALSE,
+ plContext),
+ PKIX_PROCESSINGPARAMSSETPOLICYMAPPINGINHIBITED);
+
+ *pprocParams = procParams;
+ procParams = NULL;
+
+cleanup:
+ PKIX_DECREF(anchors);
+ PKIX_DECREF(targetCert);
+ PKIX_DECREF(date);
+ PKIX_DECREF(certSelector);
+ PKIX_DECREF(certSelParams);
+ PKIX_DECREF(certStore);
+ PKIX_DECREF(certStores);
+ PKIX_DECREF(procParams);
+ PKIX_DECREF(revChecker);
+
+ PKIX_RETURN(CERTVFYPKIX);
+}
+
+/*
+ * FUNCTION: cert_PkixToNssCertsChain
+ * DESCRIPTION:
+ *
+ * Converts pkix cert list into nss cert list.
+ *
+ * PARAMETERS:
+ * "pkixCertChain"
+ * Pkix certificate list.
+ * "pvalidChain"
+ * An address of returned nss certificate list.
+ * "plContext"
+ * Platform-specific context pointer.
+ * THREAD SAFETY:
+ * Thread Safe (see Thread Safety Definitions in Programmer's Guide)
+ * RETURNS:
+ * Returns NULL if the function succeeds.
+ * Returns a Cert Verify Error if the function fails in an unrecoverable way.
+ * Returns a Fatal Error if the function fails in an unrecoverable way.
+ */
+static PKIX_Error *
+cert_PkixToNssCertsChain(
+ PKIX_List *pkixCertChain,
+ CERTCertList **pvalidChain,
+ void *plContext)
+{
+ PLArenaPool *arena = NULL;
+ CERTCertificate *nssCert = NULL;
+ CERTCertList *validChain = NULL;
+ PKIX_PL_Object *certItem = NULL;
+ PKIX_UInt32 length = 0;
+ PKIX_UInt32 i = 0;
+
+ PKIX_ENTER(CERTVFYPKIX, "cert_PkixToNssCertsChain");
+ PKIX_NULLCHECK_ONE(pvalidChain);
+
+ if (pkixCertChain == NULL) {
+ goto cleanup;
+ }
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ PKIX_ERROR(PKIX_OUTOFMEMORY);
+ }
+ validChain = (CERTCertList *)PORT_ArenaZAlloc(arena, sizeof(CERTCertList));
+ if (validChain == NULL) {
+ PKIX_ERROR(PKIX_PORTARENAALLOCFAILED);
+ }
+ PR_INIT_CLIST(&validChain->list);
+ validChain->arena = arena;
+ arena = NULL;
+
+ PKIX_CHECK(
+ PKIX_List_GetLength(pkixCertChain, &length, plContext),
+ PKIX_LISTGETLENGTHFAILED);
+
+ for (i = 0; i < length; i++) {
+ CERTCertListNode *node = NULL;
+
+ PKIX_CHECK(
+ PKIX_List_GetItem(pkixCertChain, i, &certItem, plContext),
+ PKIX_LISTGETITEMFAILED);
+
+ PKIX_CHECK(
+ PKIX_PL_Cert_GetCERTCertificate((PKIX_PL_Cert *)certItem, &nssCert,
+ plContext),
+ PKIX_CERTGETCERTCERTIFICATEFAILED);
+
+ node =
+ (CERTCertListNode *)PORT_ArenaZAlloc(validChain->arena,
+ sizeof(CERTCertListNode));
+ if (node == NULL) {
+ PKIX_ERROR(PKIX_PORTARENAALLOCFAILED);
+ }
+
+ PR_INSERT_BEFORE(&node->links, &validChain->list);
+
+ node->cert = nssCert;
+ nssCert = NULL;
+
+ PKIX_DECREF(certItem);
+ }
+
+ *pvalidChain = validChain;
+
+cleanup:
+ if (PKIX_ERROR_RECEIVED) {
+ if (validChain) {
+ CERT_DestroyCertList(validChain);
+ } else if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ if (nssCert) {
+ CERT_DestroyCertificate(nssCert);
+ }
+ }
+ PKIX_DECREF(certItem);
+
+ PKIX_RETURN(CERTVFYPKIX);
+}
+
+/*
+ * FUNCTION: cert_BuildAndValidateChain
+ * DESCRIPTION:
+ *
+ * The function builds and validates a cert chain based on certificate
+ * selection criterias from procParams. This function call PKIX_BuildChain
+ * to accomplish chain building. If PKIX_BuildChain returns with incomplete
+ * IO, the function waits with PR_Poll until the blocking IO is finished and
+ * return control back to PKIX_BuildChain.
+ *
+ * PARAMETERS:
+ * "procParams"
+ * Processing parameters to be used during chain building.
+ * "pResult"
+ * Returned build result.
+ * "pVerifyNode"
+ * Returned pointed to verify node structure: the tree-like structure
+ * that reports points of chain building failures.
+ * "plContext"
+ * Platform-specific context pointer.
+ * THREAD SAFETY:
+ * Thread Safe (see Thread Safety Definitions in Programmer's Guide)
+ * RETURNS:
+ * Returns NULL if the function succeeds.
+ * Returns a Cert Verify Error if the function fails in an unrecoverable way.
+ * Returns a Fatal Error if the function fails in an unrecoverable way.
+ */
+static PKIX_Error *
+cert_BuildAndValidateChain(
+ PKIX_ProcessingParams *procParams,
+ PKIX_BuildResult **pResult,
+ PKIX_VerifyNode **pVerifyNode,
+ void *plContext)
+{
+ PKIX_BuildResult *result = NULL;
+ PKIX_VerifyNode *verifyNode = NULL;
+ void *nbioContext = NULL;
+ void *state = NULL;
+
+ PKIX_ENTER(CERTVFYPKIX, "cert_BuildAndVerifyChain");
+ PKIX_NULLCHECK_TWO(procParams, pResult);
+
+ do {
+ if (nbioContext && state) {
+ /* PKIX-XXX: need to test functionality of NBIO handling in libPkix.
+ * See bug 391180 */
+ PRInt32 filesReady = 0;
+ PRPollDesc *pollDesc = (PRPollDesc *)nbioContext;
+ filesReady = PR_Poll(pollDesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (filesReady <= 0) {
+ PKIX_ERROR(PKIX_PRPOLLRETBADFILENUM);
+ }
+ }
+
+ PKIX_CHECK(
+ PKIX_BuildChain(procParams, &nbioContext, &state,
+ &result, &verifyNode, plContext),
+ PKIX_UNABLETOBUILDCHAIN);
+
+ } while (nbioContext && state);
+
+ *pResult = result;
+
+cleanup:
+ if (pVerifyNode) {
+ *pVerifyNode = verifyNode;
+ }
+
+ PKIX_RETURN(CERTVFYPKIX);
+}
+
+/*
+ * FUNCTION: cert_PkixErrorToNssCode
+ * DESCRIPTION:
+ *
+ * Converts pkix error(PKIX_Error) structure to PR error codes.
+ *
+ * PKIX-XXX to be implemented. See 391183.
+ *
+ * PARAMETERS:
+ * "error"
+ * Pkix error that will be converted.
+ * "nssCode"
+ * Corresponding nss error code.
+ * "plContext"
+ * Platform-specific context pointer.
+ * THREAD SAFETY:
+ * Thread Safe (see Thread Safety Definitions in Programmer's Guide)
+ * RETURNS:
+ * Returns NULL if the function succeeds.
+ * Returns a Cert Verify Error if the function fails in an unrecoverable way.
+ * Returns a Fatal Error if the function fails in an unrecoverable way.
+ */
+static PKIX_Error *
+cert_PkixErrorToNssCode(
+ PKIX_Error *error,
+ SECErrorCodes *pNssErr,
+ void *plContext)
+{
+ int errLevel = 0;
+ (void)errLevel; /* Suppress unused var warning (Bug 1738028) */
+
+ PKIX_Int32 nssErr = 0;
+ PKIX_Error *errPtr = error;
+
+ PKIX_ENTER(CERTVFYPKIX, "cert_PkixErrorToNssCode");
+ PKIX_NULLCHECK_TWO(error, pNssErr);
+
+ /* Loop until we find at least one error with non-null
+ * plErr code, that is going to be nss error code. */
+ while (errPtr) {
+ if (errPtr->plErr && !nssErr) {
+ nssErr = errPtr->plErr;
+ if (!pkixLog)
+ break;
+ }
+ if (pkixLog) {
+#ifdef PKIX_ERROR_DESCRIPTION
+ PR_LOG(pkixLog, 2, ("Error at level %d: %s\n", errLevel, PKIX_ErrorText[errPtr->errCode]));
+#else
+ PR_LOG(pkixLog, 2, ("Error at level %d: Error code %d\n", errLevel, errPtr->errCode));
+#endif /* PKIX_ERROR_DESCRIPTION */
+ }
+ errPtr = errPtr->cause;
+ errLevel += 1;
+ }
+ PORT_Assert(nssErr);
+ if (!nssErr) {
+ *pNssErr = SEC_ERROR_LIBPKIX_INTERNAL;
+ } else {
+ *pNssErr = nssErr;
+ }
+
+ PKIX_RETURN(CERTVFYPKIX);
+}
+
+/*
+ * FUNCTION: cert_GetLogFromVerifyNode
+ * DESCRIPTION:
+ *
+ * Recursive function that converts verify node tree-like set of structures
+ * to CERTVerifyLog.
+ *
+ * PARAMETERS:
+ * "log"
+ * Pointed to already allocated CERTVerifyLog structure.
+ * "node"
+ * A node of PKIX_VerifyNode tree.
+ * "plContext"
+ * Platform-specific context pointer.
+ * THREAD SAFETY:
+ * Thread Safe (see Thread Safety Definitions in Programmer's Guide)
+ * RETURNS:
+ * Returns NULL if the function succeeds.
+ * Returns a Cert Verify Error if the function fails in an unrecoverable way.
+ * Returns a Fatal Error if the function fails in an unrecoverable way.
+ */
+static PKIX_Error *
+cert_GetLogFromVerifyNode(
+ CERTVerifyLog *log,
+ PKIX_VerifyNode *node,
+ void *plContext)
+{
+ PKIX_List *children = NULL;
+ PKIX_VerifyNode *childNode = NULL;
+
+ PKIX_ENTER(CERTVFYPKIX, "cert_GetLogFromVerifyNode");
+
+ children = node->children;
+
+ if (children == NULL) {
+ PKIX_ERRORCODE errCode = PKIX_ANCHORDIDNOTCHAINTOCERT;
+ if (node->error && node->error->errCode != errCode) {
+ if (log != NULL) {
+ SECErrorCodes nssErrorCode = 0;
+ CERTCertificate *cert = NULL;
+
+ cert = node->verifyCert->nssCert;
+
+ PKIX_CHECK(
+ cert_PkixErrorToNssCode(node->error, &nssErrorCode,
+ plContext),
+ PKIX_GETPKIXERRORCODEFAILED);
+
+ cert_AddToVerifyLog(log, cert, nssErrorCode, node->depth, NULL);
+ }
+ }
+ PKIX_RETURN(CERTVFYPKIX);
+ } else {
+ PRUint32 i = 0;
+ PKIX_UInt32 length = 0;
+
+ PKIX_CHECK(
+ PKIX_List_GetLength(children, &length, plContext),
+ PKIX_LISTGETLENGTHFAILED);
+
+ for (i = 0; i < length; i++) {
+
+ PKIX_CHECK(
+ PKIX_List_GetItem(children, i, (PKIX_PL_Object **)&childNode,
+ plContext),
+ PKIX_LISTGETITEMFAILED);
+
+ PKIX_CHECK(
+ cert_GetLogFromVerifyNode(log, childNode, plContext),
+ PKIX_ERRORINRECURSIVEEQUALSCALL);
+
+ PKIX_DECREF(childNode);
+ }
+ }
+
+cleanup:
+ PKIX_DECREF(childNode);
+
+ PKIX_RETURN(CERTVFYPKIX);
+}
+
+/*
+ * FUNCTION: cert_GetBuildResults
+ * DESCRIPTION:
+ *
+ * Converts pkix build results to nss results. This function is called
+ * regardless of build result.
+ *
+ * If it called after chain was successfully constructed, then it will
+ * convert:
+ * * pkix cert list that represent the chain to nss cert list
+ * * trusted root the chain was anchored to nss certificate.
+ *
+ * In case of failure it will convert:
+ * * pkix error to PR error code(will set it with PORT_SetError)
+ * * pkix validation log to nss CERTVerifyLog
+ *
+ * PARAMETERS:
+ * "buildResult"
+ * Build results returned by PKIX_BuildChain.
+ * "verifyNode"
+ * Tree-like structure of chain building/validation failures
+ * returned by PKIX_BuildChain. Ignored in case of success.
+ * "error"
+ * Final error returned by PKIX_BuildChain. Should be NULL in
+ * case of success.
+ * "log"
+ * Address of pre-allocated(if not NULL) CERTVerifyLog structure.
+ * "ptrustedRoot"
+ * Address of returned trusted root the chain was anchored to.
+ * "pvalidChain"
+ * Address of returned valid chain.
+ * "plContext"
+ * Platform-specific context pointer.
+ * THREAD SAFETY:
+ * Thread Safe (see Thread Safety Definitions in Programmer's Guide)
+ * RETURNS:
+ * Returns NULL if the function succeeds.
+ * Returns a Cert Verify Error if the function fails in an unrecoverable way.
+ * Returns a Fatal Error if the function fails in an unrecoverable way.
+ */
+static PKIX_Error *
+cert_GetBuildResults(
+ PKIX_BuildResult *buildResult,
+ PKIX_VerifyNode *verifyNode,
+ PKIX_Error *error,
+ CERTVerifyLog *log,
+ CERTCertificate **ptrustedRoot,
+ CERTCertList **pvalidChain,
+ void *plContext)
+{
+ PKIX_ValidateResult *validResult = NULL;
+ CERTCertList *validChain = NULL;
+ CERTCertificate *trustedRoot = NULL;
+ PKIX_TrustAnchor *trustAnchor = NULL;
+ PKIX_PL_Cert *trustedCert = NULL;
+ PKIX_List *pkixCertChain = NULL;
+
+ PKIX_ENTER(CERTVFYPKIX, "cert_GetBuildResults");
+ if (buildResult == NULL && error == NULL) {
+ PKIX_ERROR(PKIX_NULLARGUMENT);
+ }
+
+ if (error) {
+ SECErrorCodes nssErrorCode = 0;
+ if (verifyNode) {
+ PKIX_Error *tmpError =
+ cert_GetLogFromVerifyNode(log, verifyNode, plContext);
+ if (tmpError) {
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)tmpError, plContext);
+ }
+ }
+ cert_PkixErrorToNssCode(error, &nssErrorCode, plContext);
+ PORT_SetError(nssErrorCode);
+ goto cleanup;
+ }
+
+ if (pvalidChain) {
+ PKIX_CHECK(
+ PKIX_BuildResult_GetCertChain(buildResult, &pkixCertChain,
+ plContext),
+ PKIX_BUILDRESULTGETCERTCHAINFAILED);
+
+ PKIX_CHECK(
+ cert_PkixToNssCertsChain(pkixCertChain, &validChain, plContext),
+ PKIX_CERTCHAINTONSSCHAINFAILED);
+ }
+
+ if (ptrustedRoot) {
+ PKIX_CHECK(
+ PKIX_BuildResult_GetValidateResult(buildResult, &validResult,
+ plContext),
+ PKIX_BUILDRESULTGETVALIDATERESULTFAILED);
+
+ PKIX_CHECK(
+ PKIX_ValidateResult_GetTrustAnchor(validResult, &trustAnchor,
+ plContext),
+ PKIX_VALIDATERESULTGETTRUSTANCHORFAILED);
+
+ PKIX_CHECK(
+ PKIX_TrustAnchor_GetTrustedCert(trustAnchor, &trustedCert,
+ plContext),
+ PKIX_TRUSTANCHORGETTRUSTEDCERTFAILED);
+
+ PKIX_CHECK(
+ PKIX_PL_Cert_GetCERTCertificate(trustedCert, &trustedRoot,
+ plContext),
+ PKIX_CERTGETCERTCERTIFICATEFAILED);
+ }
+
+ PORT_Assert(!PKIX_ERROR_RECEIVED);
+
+ if (trustedRoot) {
+ *ptrustedRoot = trustedRoot;
+ }
+ if (validChain) {
+ *pvalidChain = validChain;
+ }
+
+cleanup:
+ if (PKIX_ERROR_RECEIVED) {
+ if (trustedRoot) {
+ CERT_DestroyCertificate(trustedRoot);
+ }
+ if (validChain) {
+ CERT_DestroyCertList(validChain);
+ }
+ }
+ PKIX_DECREF(trustAnchor);
+ PKIX_DECREF(trustedCert);
+ PKIX_DECREF(pkixCertChain);
+ PKIX_DECREF(validResult);
+ PKIX_DECREF(error);
+ PKIX_DECREF(verifyNode);
+ PKIX_DECREF(buildResult);
+
+ PKIX_RETURN(CERTVFYPKIX);
+}
+#endif /* NSS_DISABLE_LIBPKIX */
+
+/*
+ * FUNCTION: cert_VerifyCertChainPkix
+ * DESCRIPTION:
+ *
+ * The main wrapper function that is called from CERT_VerifyCert and
+ * CERT_VerifyCACertForUsage functions to validate cert with libpkix.
+ *
+ * PARAMETERS:
+ * "cert"
+ * Leaf certificate of a chain we want to build.
+ * "checkSig"
+ * Certificate signatures will not be verified if this
+ * flag is set to PR_FALSE.
+ * "requiredUsage"
+ * Required usage for certificate and chain.
+ * "time"
+ * Validity time.
+ * "wincx"
+ * Nss database password token.
+ * "log"
+ * Address of already allocated CERTVerifyLog structure. Not
+ * used if NULL;
+ * "pSigerror"
+ * Address of PRBool. If not NULL, returns true is cert chain
+ * was invalidated because of bad certificate signature.
+ * "pRevoked"
+ * Address of PRBool. If not NULL, returns true is cert chain
+ * was invalidated because a revoked certificate was found in
+ * the chain.
+ * THREAD SAFETY:
+ * Thread Safe (see Thread Safety Definitions in Programmer's Guide)
+ * RETURNS:
+ * SECFailure is chain building process has failed. SECSuccess otherwise.
+ */
+SECStatus
+cert_VerifyCertChainPkix(
+ CERTCertificate *cert,
+ PRBool checkSig,
+ SECCertUsage requiredUsage,
+ PRTime time,
+ void *wincx,
+ CERTVerifyLog *log,
+ PRBool *pSigerror,
+ PRBool *pRevoked)
+{
+#ifdef NSS_DISABLE_LIBPKIX
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return SECFailure;
+#else
+ PKIX_ProcessingParams *procParams = NULL;
+ PKIX_BuildResult *result = NULL;
+ PKIX_VerifyNode *verifyNode = NULL;
+ PKIX_Error *error = NULL;
+
+ SECStatus rv = SECFailure;
+ void *plContext = NULL;
+
+#ifdef PKIX_OBJECT_LEAK_TEST
+ int leakedObjNum = 0;
+ int memLeakLoopCount = 0;
+ int objCountTable[PKIX_NUMTYPES];
+ int fnInvLocalCount = 0;
+ PKIX_Boolean savedUsePkixEngFlag = usePKIXValidationEngine;
+
+ if (usePKIXValidationEngine) {
+ /* current memory leak testing implementation does not allow
+ * to run simultaneous tests one the same or a different threads.
+ * Setting the variable to false, to make additional chain
+ * validations be handled by old nss. */
+ usePKIXValidationEngine = PR_FALSE;
+ }
+ testStartFnStackPosition = 2;
+ fnStackNameArr[0] = "cert_VerifyCertChainPkix";
+ fnStackInvCountArr[0] = 0;
+ PKIX_Boolean abortOnLeak =
+ (PR_GetEnvSecure("PKIX_OBJECT_LEAK_TEST_ABORT_ON_LEAK") == NULL) ? PKIX_FALSE
+ : PKIX_TRUE;
+ runningLeakTest = PKIX_TRUE;
+
+ /* Prevent multi-threaded run of object leak test */
+ fnInvLocalCount = PR_ATOMIC_INCREMENT(&parallelFnInvocationCount);
+ PORT_Assert(fnInvLocalCount == 1);
+
+ do {
+ rv = SECFailure;
+ plContext = NULL;
+ procParams = NULL;
+ result = NULL;
+ verifyNode = NULL;
+ error = NULL;
+ errorGenerated = PKIX_FALSE;
+ stackPosition = 0;
+
+ if (leakedObjNum) {
+ pkix_pl_lifecycle_ObjectTableUpdate(objCountTable);
+ }
+ memLeakLoopCount += 1;
+#endif /* PKIX_OBJECT_LEAK_TEST */
+
+ error =
+ cert_CreatePkixProcessingParams(cert, checkSig, time, wincx,
+ PR_FALSE /*use arena*/,
+ requiredUsage == certUsageStatusResponder,
+ &procParams, &plContext);
+ if (error) {
+ goto cleanup;
+ }
+
+ error =
+ cert_ProcessingParamsSetKeyAndCertUsage(procParams, requiredUsage, 0,
+ plContext);
+ if (error) {
+ goto cleanup;
+ }
+
+ error =
+ cert_BuildAndValidateChain(procParams, &result, &verifyNode, plContext);
+ if (error) {
+ goto cleanup;
+ }
+
+ if (pRevoked) {
+ /* Currently always PR_FALSE. Will be fixed as a part of 394077 */
+ *pRevoked = PR_FALSE;
+ }
+ if (pSigerror) {
+ /* Currently always PR_FALSE. Will be fixed as a part of 394077 */
+ *pSigerror = PR_FALSE;
+ }
+ rv = SECSuccess;
+
+ cleanup:
+ error = cert_GetBuildResults(result, verifyNode, error, log, NULL, NULL,
+ plContext);
+ if (error) {
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
+ }
+ if (procParams) {
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)procParams, plContext);
+ }
+ if (plContext) {
+ PKIX_PL_NssContext_Destroy(plContext);
+ }
+
+#ifdef PKIX_OBJECT_LEAK_TEST
+ leakedObjNum =
+ pkix_pl_lifecycle_ObjectLeakCheck(leakedObjNum ? objCountTable : NULL);
+
+ if (pkixLog && leakedObjNum) {
+ PR_LOG(pkixLog, 1, ("The generated error caused an object leaks. Loop %d."
+ "Stack %s\n",
+ memLeakLoopCount, errorFnStackString));
+ }
+ PR_Free(errorFnStackString);
+ errorFnStackString = NULL;
+ if (abortOnLeak) {
+ PORT_Assert(leakedObjNum == 0);
+ }
+
+ } while (errorGenerated);
+
+ runningLeakTest = PKIX_FALSE;
+ PR_ATOMIC_DECREMENT(&parallelFnInvocationCount);
+ usePKIXValidationEngine = savedUsePkixEngFlag;
+#endif /* PKIX_OBJECT_LEAK_TEST */
+
+ return rv;
+#endif /* NSS_DISABLE_LIBPKIX */
+}
+
+#ifndef NSS_DISABLE_LIBPKIX
+PKIX_CertSelector *
+cert_GetTargetCertConstraints(CERTCertificate *target, void *plContext)
+{
+ PKIX_ComCertSelParams *certSelParams = NULL;
+ PKIX_CertSelector *certSelector = NULL;
+ PKIX_CertSelector *r = NULL;
+ PKIX_PL_Cert *eeCert = NULL;
+ PKIX_Error *error = NULL;
+
+ error = PKIX_PL_Cert_CreateFromCERTCertificate(target, &eeCert, plContext);
+ if (error != NULL)
+ goto cleanup;
+
+ error = PKIX_CertSelector_Create(NULL, NULL, &certSelector, plContext);
+ if (error != NULL)
+ goto cleanup;
+
+ error = PKIX_ComCertSelParams_Create(&certSelParams, plContext);
+ if (error != NULL)
+ goto cleanup;
+
+ error = PKIX_ComCertSelParams_SetCertificate(
+ certSelParams, eeCert, plContext);
+ if (error != NULL)
+ goto cleanup;
+
+ error = PKIX_CertSelector_SetCommonCertSelectorParams(certSelector, certSelParams, plContext);
+ if (error != NULL)
+ goto cleanup;
+
+ error = PKIX_PL_Object_IncRef((PKIX_PL_Object *)certSelector, plContext);
+ if (error == NULL)
+ r = certSelector;
+
+cleanup:
+ if (certSelParams != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)certSelParams, plContext);
+
+ if (eeCert != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)eeCert, plContext);
+
+ if (certSelector != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)certSelector, plContext);
+
+ if (error != NULL) {
+ SECErrorCodes nssErr;
+
+ cert_PkixErrorToNssCode(error, &nssErr, plContext);
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
+ PORT_SetError(nssErr);
+ }
+
+ return r;
+}
+
+static PKIX_List *
+cert_GetCertStores(void *plContext)
+{
+ PKIX_CertStore *certStore = NULL;
+ PKIX_List *certStores = NULL;
+ PKIX_List *r = NULL;
+ PKIX_Error *error = NULL;
+
+ error = PKIX_PL_Pk11CertStore_Create(&certStore, plContext);
+ if (error != NULL)
+ goto cleanup;
+
+ error = PKIX_List_Create(&certStores, plContext);
+ if (error != NULL)
+ goto cleanup;
+
+ error = PKIX_List_AppendItem(certStores,
+ (PKIX_PL_Object *)certStore, plContext);
+ if (error != NULL)
+ goto cleanup;
+
+ error = PKIX_PL_Object_IncRef((PKIX_PL_Object *)certStores, plContext);
+ if (error == NULL)
+ r = certStores;
+
+cleanup:
+ if (certStores != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)certStores, plContext);
+
+ if (certStore != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)certStore, plContext);
+
+ if (error != NULL) {
+ SECErrorCodes nssErr;
+
+ cert_PkixErrorToNssCode(error, &nssErr, plContext);
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
+ PORT_SetError(nssErr);
+ }
+
+ return r;
+}
+
+struct fake_PKIX_PL_CertStruct {
+ CERTCertificate *nssCert;
+};
+
+/* This needs to be part of the PKIX_PL_* */
+/* This definitely needs to go away, and be replaced with
+ a real accessor function in PKIX */
+static CERTCertificate *
+cert_NSSCertFromPKIXCert(const PKIX_PL_Cert *pkix_cert)
+{
+ struct fake_PKIX_PL_CertStruct *fcert = NULL;
+
+ fcert = (struct fake_PKIX_PL_CertStruct *)pkix_cert;
+
+ return CERT_DupCertificate(fcert->nssCert);
+}
+
+PKIX_List *
+cert_PKIXMakeOIDList(const SECOidTag *oids, int oidCount, void *plContext)
+{
+ PKIX_List *r = NULL;
+ PKIX_List *policyList = NULL;
+ PKIX_PL_OID *policyOID = NULL;
+ PKIX_Error *error = NULL;
+ int i;
+
+ error = PKIX_List_Create(&policyList, plContext);
+ if (error != NULL) {
+ goto cleanup;
+ }
+
+ for (i = 0; i < oidCount; i++) {
+ error = PKIX_PL_OID_Create(oids[i], &policyOID, plContext);
+ if (error) {
+ goto cleanup;
+ }
+ error = PKIX_List_AppendItem(policyList,
+ (PKIX_PL_Object *)policyOID, plContext);
+ if (error != NULL) {
+ goto cleanup;
+ }
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyOID, plContext);
+ policyOID = NULL;
+ }
+
+ error = PKIX_List_SetImmutable(policyList, plContext);
+ if (error != NULL)
+ goto cleanup;
+
+ error = PKIX_PL_Object_IncRef((PKIX_PL_Object *)policyList, plContext);
+ if (error == NULL)
+ r = policyList;
+
+cleanup:
+ if (policyOID != NULL) {
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyOID, plContext);
+ }
+ if (policyList != NULL) {
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyList, plContext);
+ }
+ if (error != NULL) {
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
+ }
+
+ return r;
+}
+
+CERTValOutParam *
+cert_pkix_FindOutputParam(CERTValOutParam *params, const CERTValParamOutType t)
+{
+ CERTValOutParam *i;
+ if (params == NULL) {
+ return NULL;
+ }
+ for (i = params; i->type != cert_po_end; i++) {
+ if (i->type == t) {
+ return i;
+ }
+ }
+ return NULL;
+}
+
+static PKIX_Error *
+setRevocationMethod(PKIX_RevocationChecker *revChecker,
+ PKIX_ProcessingParams *procParams,
+ const CERTRevocationTests *revTest,
+ CERTRevocationMethodIndex certRevMethod,
+ PKIX_RevocationMethodType pkixRevMethod,
+ PKIX_Boolean verifyResponderUsages,
+ PKIX_Boolean isLeafTest,
+ void *plContext)
+{
+ PKIX_UInt32 methodFlags = 0;
+ PKIX_Error *error = NULL;
+ PKIX_UInt32 priority = 0;
+
+ if (revTest->number_of_defined_methods <= (PRUint32)certRevMethod) {
+ return NULL;
+ }
+ if (revTest->preferred_methods) {
+ unsigned int i = 0;
+ for (; i < revTest->number_of_preferred_methods; i++) {
+ if (revTest->preferred_methods[i] == certRevMethod)
+ break;
+ }
+ priority = i;
+ }
+ methodFlags = revTest->cert_rev_flags_per_method[certRevMethod];
+ if (verifyResponderUsages &&
+ pkixRevMethod == PKIX_RevocationMethod_OCSP) {
+ methodFlags |= PKIX_REV_M_FORBID_NETWORK_FETCHING;
+ }
+ error =
+ PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams,
+ pkixRevMethod, methodFlags,
+ priority, NULL,
+ isLeafTest, plContext);
+ return error;
+}
+
+SECStatus
+cert_pkixSetParam(PKIX_ProcessingParams *procParams,
+ const CERTValInParam *param, void *plContext)
+{
+ PKIX_Error *error = NULL;
+ SECStatus r = SECSuccess;
+ PKIX_PL_Date *date = NULL;
+ PKIX_List *policyOIDList = NULL;
+ PKIX_List *certListPkix = NULL;
+ const CERTRevocationFlags *flags;
+ SECErrorCodes errCode = SEC_ERROR_INVALID_ARGS;
+ const CERTCertList *certList = NULL;
+ CERTCertListNode *node;
+ PKIX_PL_Cert *certPkix = NULL;
+ PKIX_TrustAnchor *trustAnchor = NULL;
+ PKIX_RevocationChecker *revChecker = NULL;
+ PKIX_PL_NssContext *nssContext = (PKIX_PL_NssContext *)plContext;
+
+ /* XXX we need a way to map generic PKIX error to generic NSS errors */
+
+ switch (param->type) {
+
+ case cert_pi_policyOID:
+
+ /* needed? */
+ error = PKIX_ProcessingParams_SetExplicitPolicyRequired(
+ procParams, PKIX_TRUE, plContext);
+
+ if (error != NULL) {
+ break;
+ }
+
+ policyOIDList = cert_PKIXMakeOIDList(param->value.array.oids,
+ param->value.arraySize, plContext);
+ if (policyOIDList == NULL) {
+ r = SECFailure;
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ break;
+ }
+
+ error = PKIX_ProcessingParams_SetInitialPolicies(
+ procParams, policyOIDList, plContext);
+ break;
+
+ case cert_pi_date:
+ if (param->value.scalar.time == 0) {
+ error = PKIX_PL_Date_Create_UTCTime(NULL, &date, plContext);
+ if (error != NULL) {
+ errCode = SEC_ERROR_INVALID_TIME;
+ break;
+ }
+ } else {
+ error = pkix_pl_Date_CreateFromPRTime(param->value.scalar.time,
+ &date, plContext);
+ if (error != NULL) {
+ errCode = SEC_ERROR_INVALID_TIME;
+ break;
+ }
+ }
+
+ error = PKIX_ProcessingParams_SetDate(procParams, date, plContext);
+ if (error != NULL) {
+ errCode = SEC_ERROR_INVALID_TIME;
+ }
+ break;
+
+ case cert_pi_revocationFlags: {
+ PKIX_UInt32 leafIMFlags = 0;
+ PKIX_UInt32 chainIMFlags = 0;
+ PKIX_Boolean validatingResponderCert = PKIX_FALSE;
+
+ flags = param->value.pointer.revocation;
+ if (!flags) {
+ PORT_SetError(errCode);
+ r = SECFailure;
+ break;
+ }
+
+ leafIMFlags =
+ flags->leafTests.cert_rev_method_independent_flags;
+ chainIMFlags =
+ flags->chainTests.cert_rev_method_independent_flags;
+
+ error =
+ PKIX_RevocationChecker_Create(leafIMFlags, chainIMFlags,
+ &revChecker, plContext);
+ if (error) {
+ break;
+ }
+
+ error =
+ PKIX_ProcessingParams_SetRevocationChecker(procParams,
+ revChecker, plContext);
+ if (error) {
+ break;
+ }
+
+ if (((PKIX_PL_NssContext *)plContext)->certificateUsage &
+ certificateUsageStatusResponder) {
+ validatingResponderCert = PKIX_TRUE;
+ }
+
+ error = setRevocationMethod(revChecker,
+ procParams, &flags->leafTests,
+ cert_revocation_method_crl,
+ PKIX_RevocationMethod_CRL,
+ validatingResponderCert,
+ PKIX_TRUE, plContext);
+ if (error) {
+ break;
+ }
+
+ error = setRevocationMethod(revChecker,
+ procParams, &flags->leafTests,
+ cert_revocation_method_ocsp,
+ PKIX_RevocationMethod_OCSP,
+ validatingResponderCert,
+ PKIX_TRUE, plContext);
+ if (error) {
+ break;
+ }
+
+ error = setRevocationMethod(revChecker,
+ procParams, &flags->chainTests,
+ cert_revocation_method_crl,
+ PKIX_RevocationMethod_CRL,
+ validatingResponderCert,
+ PKIX_FALSE, plContext);
+ if (error) {
+ break;
+ }
+
+ error = setRevocationMethod(revChecker,
+ procParams, &flags->chainTests,
+ cert_revocation_method_ocsp,
+ PKIX_RevocationMethod_OCSP,
+ validatingResponderCert,
+ PKIX_FALSE, plContext);
+ if (error) {
+ break;
+ }
+
+ } break;
+
+ case cert_pi_trustAnchors:
+ certList = param->value.pointer.chain;
+ if (!certList) {
+ PORT_SetError(errCode);
+ r = SECFailure;
+ break;
+ }
+ error = PKIX_List_Create(&certListPkix, plContext);
+ if (error != NULL) {
+ break;
+ }
+ for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
+ node = CERT_LIST_NEXT(node)) {
+ error = PKIX_PL_Cert_CreateFromCERTCertificate(node->cert,
+ &certPkix, plContext);
+ if (error) {
+ break;
+ }
+ error = PKIX_TrustAnchor_CreateWithCert(certPkix, &trustAnchor,
+ plContext);
+ if (error) {
+ break;
+ }
+ error = PKIX_List_AppendItem(certListPkix,
+ (PKIX_PL_Object *)trustAnchor, plContext);
+ if (error) {
+ break;
+ }
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchor, plContext);
+ trustAnchor = NULL;
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)certPkix, plContext);
+ certPkix = NULL;
+ }
+ error =
+ PKIX_ProcessingParams_SetTrustAnchors(procParams, certListPkix,
+ plContext);
+ break;
+
+ case cert_pi_useAIACertFetch:
+ error =
+ PKIX_ProcessingParams_SetUseAIAForCertFetching(procParams,
+ (PRBool)(param->value.scalar.b !=
+ 0),
+ plContext);
+ break;
+
+ case cert_pi_chainVerifyCallback: {
+ const CERTChainVerifyCallback *chainVerifyCallback =
+ param->value.pointer.chainVerifyCallback;
+ if (!chainVerifyCallback || !chainVerifyCallback->isChainValid) {
+ PORT_SetError(errCode);
+ r = SECFailure;
+ break;
+ }
+
+ nssContext->chainVerifyCallback = *chainVerifyCallback;
+ } break;
+
+ case cert_pi_useOnlyTrustAnchors:
+ error =
+ PKIX_ProcessingParams_SetUseOnlyTrustAnchors(procParams,
+ (PRBool)(param->value.scalar.b !=
+ 0),
+ plContext);
+ break;
+
+ default:
+ PORT_SetError(errCode);
+ r = SECFailure;
+ break;
+ }
+
+ if (policyOIDList != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyOIDList, plContext);
+
+ if (date != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)date, plContext);
+
+ if (revChecker != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)revChecker, plContext);
+
+ if (certListPkix)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)certListPkix, plContext);
+
+ if (trustAnchor)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchor, plContext);
+
+ if (certPkix)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)certPkix, plContext);
+
+ if (error != NULL) {
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
+ PORT_SetError(errCode);
+ r = SECFailure;
+ }
+
+ return r;
+}
+
+void
+cert_pkixDestroyValOutParam(CERTValOutParam *params)
+{
+ CERTValOutParam *i;
+
+ if (params == NULL) {
+ return;
+ }
+ for (i = params; i->type != cert_po_end; i++) {
+ switch (i->type) {
+ case cert_po_trustAnchor:
+ if (i->value.pointer.cert) {
+ CERT_DestroyCertificate(i->value.pointer.cert);
+ i->value.pointer.cert = NULL;
+ }
+ break;
+
+ case cert_po_certList:
+ if (i->value.pointer.chain) {
+ CERT_DestroyCertList(i->value.pointer.chain);
+ i->value.pointer.chain = NULL;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_LeafFlags[2] = {
+ /* crl */
+ CERT_REV_M_TEST_USING_THIS_METHOD |
+ CERT_REV_M_FORBID_NETWORK_FETCHING |
+ CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
+ /* ocsp */
+ CERT_REV_M_TEST_USING_THIS_METHOD
+};
+
+static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_ChainFlags[2] = {
+ /* crl */
+ CERT_REV_M_TEST_USING_THIS_METHOD |
+ CERT_REV_M_FORBID_NETWORK_FETCHING |
+ CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
+ /* ocsp */
+ 0
+};
+
+static CERTRevocationMethodIndex
+ certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_Method_Preference = {
+ cert_revocation_method_crl
+ };
+
+static const CERTRevocationFlags certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy = {
+ { /* leafTests */
+ 2,
+ certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_LeafFlags,
+ 1,
+ &certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_Method_Preference,
+ 0 },
+ { /* chainTests */
+ 2,
+ certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_ChainFlags,
+ 0,
+ 0,
+ 0 }
+};
+#endif /* NSS_DISABLE_LIBPKIX */
+
+extern const CERTRevocationFlags *
+CERT_GetClassicOCSPEnabledSoftFailurePolicy()
+{
+#ifdef NSS_DISABLE_LIBPKIX
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return NULL;
+#else
+ return &certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy;
+#endif /* NSS_DISABLE_LIBPKIX */
+}
+
+#ifndef NSS_DISABLE_LIBPKIX
+static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_LeafFlags[2] = {
+ /* crl */
+ CERT_REV_M_TEST_USING_THIS_METHOD |
+ CERT_REV_M_FORBID_NETWORK_FETCHING |
+ CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
+ /* ocsp */
+ CERT_REV_M_TEST_USING_THIS_METHOD |
+ CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO
+};
+
+static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_ChainFlags[2] = {
+ /* crl */
+ CERT_REV_M_TEST_USING_THIS_METHOD |
+ CERT_REV_M_FORBID_NETWORK_FETCHING |
+ CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
+ /* ocsp */
+ 0
+};
+
+static CERTRevocationMethodIndex
+ certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_Method_Preference = {
+ cert_revocation_method_crl
+ };
+
+static const CERTRevocationFlags certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy = {
+ { /* leafTests */
+ 2,
+ certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_LeafFlags,
+ 1,
+ &certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_Method_Preference,
+ 0 },
+ { /* chainTests */
+ 2,
+ certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_ChainFlags,
+ 0,
+ 0,
+ 0 }
+};
+#endif /* NSS_DISABLE_LIBPKIX */
+
+extern const CERTRevocationFlags *
+CERT_GetClassicOCSPEnabledHardFailurePolicy()
+{
+#ifdef NSS_DISABLE_LIBPKIX
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return NULL;
+#else
+ return &certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy;
+#endif /* NSS_DISABLE_LIBPKIX */
+}
+
+#ifndef NSS_DISABLE_LIBPKIX
+static PRUint64 certRev_NSS_3_11_Ocsp_Disabled_Policy_LeafFlags[2] = {
+ /* crl */
+ CERT_REV_M_TEST_USING_THIS_METHOD |
+ CERT_REV_M_FORBID_NETWORK_FETCHING |
+ CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
+ /* ocsp */
+ 0
+};
+
+static PRUint64 certRev_NSS_3_11_Ocsp_Disabled_Policy_ChainFlags[2] = {
+ /* crl */
+ CERT_REV_M_TEST_USING_THIS_METHOD |
+ CERT_REV_M_FORBID_NETWORK_FETCHING |
+ CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
+ /* ocsp */
+ 0
+};
+
+static const CERTRevocationFlags certRev_NSS_3_11_Ocsp_Disabled_Policy = {
+ { /* leafTests */
+ 2,
+ certRev_NSS_3_11_Ocsp_Disabled_Policy_LeafFlags,
+ 0,
+ 0,
+ 0 },
+ { /* chainTests */
+ 2,
+ certRev_NSS_3_11_Ocsp_Disabled_Policy_ChainFlags,
+ 0,
+ 0,
+ 0 }
+};
+#endif /* NSS_DISABLE_LIBPKIX */
+
+extern const CERTRevocationFlags *
+CERT_GetClassicOCSPDisabledPolicy()
+{
+#ifdef NSS_DISABLE_LIBPKIX
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return NULL;
+#else
+ return &certRev_NSS_3_11_Ocsp_Disabled_Policy;
+#endif /* NSS_DISABLE_LIBPKIX */
+}
+
+#ifndef NSS_DISABLE_LIBPKIX
+static PRUint64 certRev_PKIX_Verify_Nist_Policy_LeafFlags[2] = {
+ /* crl */
+ CERT_REV_M_TEST_USING_THIS_METHOD |
+ CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO |
+ CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE,
+ /* ocsp */
+ 0
+};
+
+static PRUint64 certRev_PKIX_Verify_Nist_Policy_ChainFlags[2] = {
+ /* crl */
+ CERT_REV_M_TEST_USING_THIS_METHOD |
+ CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO |
+ CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE,
+ /* ocsp */
+ 0
+};
+
+static const CERTRevocationFlags certRev_PKIX_Verify_Nist_Policy = {
+ { /* leafTests */
+ 2,
+ certRev_PKIX_Verify_Nist_Policy_LeafFlags,
+ 0,
+ 0,
+ 0 },
+ { /* chainTests */
+ 2,
+ certRev_PKIX_Verify_Nist_Policy_ChainFlags,
+ 0,
+ 0,
+ 0 }
+};
+#endif /* NSS_DISABLE_LIBPKIX */
+
+extern const CERTRevocationFlags *
+CERT_GetPKIXVerifyNistRevocationPolicy()
+{
+#ifdef NSS_DISABLE_LIBPKIX
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return NULL;
+#else
+ return &certRev_PKIX_Verify_Nist_Policy;
+#endif /* NSS_DISABLE_LIBPKIX */
+}
+
+CERTRevocationFlags *
+CERT_AllocCERTRevocationFlags(
+ PRUint32 number_leaf_methods, PRUint32 number_leaf_pref_methods,
+ PRUint32 number_chain_methods, PRUint32 number_chain_pref_methods)
+{
+#ifdef NSS_DISABLE_LIBPKIX
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return NULL;
+#else
+ CERTRevocationFlags *flags;
+
+ flags = PORT_New(CERTRevocationFlags);
+ if (!flags)
+ return (NULL);
+
+ flags->leafTests.number_of_defined_methods = number_leaf_methods;
+ flags->leafTests.cert_rev_flags_per_method =
+ PORT_NewArray(PRUint64, number_leaf_methods);
+
+ flags->leafTests.number_of_preferred_methods = number_leaf_pref_methods;
+ flags->leafTests.preferred_methods =
+ PORT_NewArray(CERTRevocationMethodIndex, number_leaf_pref_methods);
+
+ flags->chainTests.number_of_defined_methods = number_chain_methods;
+ flags->chainTests.cert_rev_flags_per_method =
+ PORT_NewArray(PRUint64, number_chain_methods);
+
+ flags->chainTests.number_of_preferred_methods = number_chain_pref_methods;
+ flags->chainTests.preferred_methods =
+ PORT_NewArray(CERTRevocationMethodIndex, number_chain_pref_methods);
+
+ if (!flags->leafTests.cert_rev_flags_per_method ||
+ !flags->leafTests.preferred_methods ||
+ !flags->chainTests.cert_rev_flags_per_method ||
+ !flags->chainTests.preferred_methods) {
+ CERT_DestroyCERTRevocationFlags(flags);
+ return (NULL);
+ }
+
+ return flags;
+#endif /* NSS_DISABLE_LIBPKIX */
+}
+
+void
+CERT_DestroyCERTRevocationFlags(CERTRevocationFlags *flags)
+{
+#ifdef NSS_DISABLE_LIBPKIX
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return;
+#else
+ if (!flags)
+ return;
+
+ if (flags->leafTests.cert_rev_flags_per_method)
+ PORT_Free(flags->leafTests.cert_rev_flags_per_method);
+
+ if (flags->leafTests.preferred_methods)
+ PORT_Free(flags->leafTests.preferred_methods);
+
+ if (flags->chainTests.cert_rev_flags_per_method)
+ PORT_Free(flags->chainTests.cert_rev_flags_per_method);
+
+ if (flags->chainTests.preferred_methods)
+ PORT_Free(flags->chainTests.preferred_methods);
+
+ PORT_Free(flags);
+#endif /* NSS_DISABLE_LIBPKIX */
+}
+
+/*
+ * CERT_PKIXVerifyCert
+ *
+ * Verify a Certificate using the PKIX library.
+ *
+ * Parameters:
+ * cert - the target certificate to verify. Must be non-null
+ * params - an array of type/value parameters which can be
+ * used to modify the behavior of the validation
+ * algorithm, or supply additional constraints.
+ *
+ * outputTrustAnchor - the trust anchor which the certificate
+ * chains to. The caller is responsible
+ * for freeing this.
+ *
+ * Example Usage:
+ * CERTValParam args[3];
+ * args[0].type = cvpt_policyOID;
+ * args[0].value.si = oid;
+ * args[1].type = revCheckRequired;
+ * args[1].value.b = PR_TRUE;
+ * args[2].type = cvpt_end;
+ *
+ * CERT_PKIXVerifyCert(cert, &output, args
+ */
+SECStatus
+CERT_PKIXVerifyCert(
+ CERTCertificate *cert,
+ SECCertificateUsage usages,
+ CERTValInParam *paramsIn,
+ CERTValOutParam *paramsOut,
+ void *wincx)
+{
+#ifdef NSS_DISABLE_LIBPKIX
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return SECFailure;
+#else
+ SECStatus r = SECFailure;
+ PKIX_Error *error = NULL;
+ PKIX_ProcessingParams *procParams = NULL;
+ PKIX_BuildResult *buildResult = NULL;
+ void *nbioContext = NULL; /* for non-blocking IO */
+ void *buildState = NULL; /* for non-blocking IO */
+ PKIX_CertSelector *certSelector = NULL;
+ PKIX_List *certStores = NULL;
+ PKIX_ValidateResult *valResult = NULL;
+ PKIX_VerifyNode *verifyNode = NULL;
+ PKIX_TrustAnchor *trustAnchor = NULL;
+ PKIX_PL_Cert *trustAnchorCert = NULL;
+ PKIX_List *builtCertList = NULL;
+ CERTValOutParam *oparam = NULL;
+ int i = 0;
+
+ void *plContext = NULL;
+
+#ifdef PKIX_OBJECT_LEAK_TEST
+ int leakedObjNum = 0;
+ int memLeakLoopCount = 0;
+ int objCountTable[PKIX_NUMTYPES];
+ int fnInvLocalCount = 0;
+ PKIX_Boolean savedUsePkixEngFlag = usePKIXValidationEngine;
+
+ if (usePKIXValidationEngine) {
+ /* current memory leak testing implementation does not allow
+ * to run simultaneous tests one the same or a different threads.
+ * Setting the variable to false, to make additional chain
+ * validations be handled by old nss. */
+ usePKIXValidationEngine = PR_FALSE;
+ }
+ testStartFnStackPosition = 1;
+ fnStackNameArr[0] = "CERT_PKIXVerifyCert";
+ fnStackInvCountArr[0] = 0;
+ PKIX_Boolean abortOnLeak =
+ (PR_GetEnvSecure("PKIX_OBJECT_LEAK_TEST_ABORT_ON_LEAK") == NULL) ? PKIX_FALSE
+ : PKIX_TRUE;
+ runningLeakTest = PKIX_TRUE;
+
+ /* Prevent multi-threaded run of object leak test */
+ fnInvLocalCount = PR_ATOMIC_INCREMENT(&parallelFnInvocationCount);
+ PORT_Assert(fnInvLocalCount == 1);
+
+ do {
+ r = SECFailure;
+ error = NULL;
+ procParams = NULL;
+ buildResult = NULL;
+ nbioContext = NULL; /* for non-blocking IO */
+ buildState = NULL; /* for non-blocking IO */
+ certSelector = NULL;
+ certStores = NULL;
+ valResult = NULL;
+ verifyNode = NULL;
+ trustAnchor = NULL;
+ trustAnchorCert = NULL;
+ builtCertList = NULL;
+ oparam = NULL;
+ i = 0;
+ errorGenerated = PKIX_FALSE;
+ stackPosition = 0;
+
+ if (leakedObjNum) {
+ pkix_pl_lifecycle_ObjectTableUpdate(objCountTable);
+ }
+ memLeakLoopCount += 1;
+#endif /* PKIX_OBJECT_LEAK_TEST */
+
+ error = PKIX_PL_NssContext_Create(
+ 0, PR_FALSE /*use arena*/, wincx, &plContext);
+ if (error != NULL) { /* need pkix->nss error map */
+ PORT_SetError(SEC_ERROR_CERT_NOT_VALID);
+ goto cleanup;
+ }
+
+ error = pkix_pl_NssContext_SetCertUsage(usages, plContext);
+ if (error != NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto cleanup;
+ }
+
+ error = PKIX_ProcessingParams_Create(&procParams, plContext);
+ if (error != NULL) { /* need pkix->nss error map */
+ PORT_SetError(SEC_ERROR_CERT_NOT_VALID);
+ goto cleanup;
+ }
+
+ /* local cert store should be set into procParams before
+ * filling in revocation settings. */
+ certStores = cert_GetCertStores(plContext);
+ if (certStores == NULL) {
+ goto cleanup;
+ }
+ error = PKIX_ProcessingParams_SetCertStores(procParams, certStores, plContext);
+ if (error != NULL) {
+ goto cleanup;
+ }
+
+ /* now process the extensible input parameters structure */
+ if (paramsIn != NULL) {
+ i = 0;
+ while (paramsIn[i].type != cert_pi_end) {
+ if (paramsIn[i].type >= cert_pi_max) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto cleanup;
+ }
+ if (cert_pkixSetParam(procParams,
+ &paramsIn[i], plContext) !=
+ SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto cleanup;
+ }
+ i++;
+ }
+ }
+
+ certSelector = cert_GetTargetCertConstraints(cert, plContext);
+ if (certSelector == NULL) {
+ goto cleanup;
+ }
+ error = PKIX_ProcessingParams_SetTargetCertConstraints(procParams, certSelector, plContext);
+ if (error != NULL) {
+ goto cleanup;
+ }
+
+ error = PKIX_BuildChain(procParams, &nbioContext,
+ &buildState, &buildResult, &verifyNode,
+ plContext);
+ if (error != NULL) {
+ goto cleanup;
+ }
+
+ error = PKIX_BuildResult_GetValidateResult(buildResult, &valResult,
+ plContext);
+ if (error != NULL) {
+ goto cleanup;
+ }
+
+ error = PKIX_ValidateResult_GetTrustAnchor(valResult, &trustAnchor,
+ plContext);
+ if (error != NULL) {
+ goto cleanup;
+ }
+
+ if (trustAnchor != NULL) {
+ error = PKIX_TrustAnchor_GetTrustedCert(trustAnchor, &trustAnchorCert,
+ plContext);
+ if (error != NULL) {
+ goto cleanup;
+ }
+ }
+
+#ifdef PKIX_OBJECT_LEAK_TEST
+ /* Can not continue if error was generated but not returned.
+ * Jumping to cleanup. */
+ if (errorGenerated)
+ goto cleanup;
+#endif /* PKIX_OBJECT_LEAK_TEST */
+
+ oparam = cert_pkix_FindOutputParam(paramsOut, cert_po_trustAnchor);
+ if (oparam != NULL) {
+ if (trustAnchorCert != NULL) {
+ oparam->value.pointer.cert =
+ cert_NSSCertFromPKIXCert(trustAnchorCert);
+ } else {
+ oparam->value.pointer.cert = NULL;
+ }
+ }
+
+ error = PKIX_BuildResult_GetCertChain(buildResult, &builtCertList,
+ plContext);
+ if (error != NULL) {
+ goto cleanup;
+ }
+
+ oparam = cert_pkix_FindOutputParam(paramsOut, cert_po_certList);
+ if (oparam != NULL) {
+ error = cert_PkixToNssCertsChain(builtCertList,
+ &oparam->value.pointer.chain,
+ plContext);
+ if (error)
+ goto cleanup;
+ }
+
+ r = SECSuccess;
+
+ cleanup:
+ if (verifyNode) {
+ /* Return validation log only upon error. */
+ oparam = cert_pkix_FindOutputParam(paramsOut, cert_po_errorLog);
+#ifdef PKIX_OBJECT_LEAK_TEST
+ if (!errorGenerated)
+#endif /* PKIX_OBJECT_LEAK_TEST */
+ if (r && oparam != NULL) {
+ PKIX_Error *tmpError =
+ cert_GetLogFromVerifyNode(oparam->value.pointer.log,
+ verifyNode, plContext);
+ if (tmpError) {
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)tmpError, plContext);
+ }
+ }
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)verifyNode, plContext);
+ }
+
+ if (procParams != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)procParams, plContext);
+
+ if (trustAnchorCert != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchorCert, plContext);
+
+ if (trustAnchor != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchor, plContext);
+
+ if (valResult != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)valResult, plContext);
+
+ if (buildResult != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)buildResult, plContext);
+
+ if (certStores != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)certStores, plContext);
+
+ if (certSelector != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)certSelector, plContext);
+
+ if (builtCertList != NULL)
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)builtCertList, plContext);
+
+ if (error != NULL) {
+ SECErrorCodes nssErrorCode = 0;
+
+ cert_PkixErrorToNssCode(error, &nssErrorCode, plContext);
+ cert_pkixDestroyValOutParam(paramsOut);
+ PORT_SetError(nssErrorCode);
+ PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
+ }
+
+ PKIX_PL_NssContext_Destroy(plContext);
+
+#ifdef PKIX_OBJECT_LEAK_TEST
+ leakedObjNum =
+ pkix_pl_lifecycle_ObjectLeakCheck(leakedObjNum ? objCountTable : NULL);
+
+ if (pkixLog && leakedObjNum) {
+ PR_LOG(pkixLog, 1, ("The generated error caused an object leaks. Loop %d."
+ "Stack %s\n",
+ memLeakLoopCount, errorFnStackString));
+ }
+ PR_Free(errorFnStackString);
+ errorFnStackString = NULL;
+ if (abortOnLeak) {
+ PORT_Assert(leakedObjNum == 0);
+ }
+
+ } while (errorGenerated);
+
+ runningLeakTest = PKIX_FALSE;
+ PR_ATOMIC_DECREMENT(&parallelFnInvocationCount);
+ usePKIXValidationEngine = savedUsePkixEngFlag;
+#endif /* PKIX_OBJECT_LEAK_TEST */
+
+ return r;
+#endif /* NSS_DISABLE_LIBPKIX */
+}
diff --git a/security/nss/lib/certhigh/crlv2.c b/security/nss/lib/certhigh/crlv2.c
new file mode 100644
index 0000000000..d58d4e083b
--- /dev/null
+++ b/security/nss/lib/certhigh/crlv2.c
@@ -0,0 +1,160 @@
+/* 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 x.509 v3 crl and crl entries extensions.
+ */
+
+#include "cert.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "secoidt.h"
+#include "secder.h"
+#include "secasn1.h"
+#include "certxutl.h"
+
+SECStatus
+CERT_FindCRLExtensionByOID(CERTCrl *crl, SECItem *oid, SECItem *value)
+{
+ return (cert_FindExtensionByOID(crl->extensions, oid, value));
+}
+
+SECStatus
+CERT_FindCRLExtension(CERTCrl *crl, int tag, SECItem *value)
+{
+ return (cert_FindExtension(crl->extensions, tag, value));
+}
+
+/* Callback to set extensions and adjust verison */
+static void
+SetCrlExts(void *object, CERTCertExtension **exts)
+{
+ CERTCrl *crl = (CERTCrl *)object;
+
+ crl->extensions = exts;
+ DER_SetUInteger(crl->arena, &crl->version, SEC_CRL_VERSION_2);
+}
+
+void *
+CERT_StartCRLExtensions(CERTCrl *crl)
+{
+ return (cert_StartExtensions((void *)crl, crl->arena, SetCrlExts));
+}
+
+static void
+SetCrlEntryExts(void *object, CERTCertExtension **exts)
+{
+ CERTCrlEntry *crlEntry = (CERTCrlEntry *)object;
+
+ crlEntry->extensions = exts;
+}
+
+void *
+CERT_StartCRLEntryExtensions(CERTCrl *crl, CERTCrlEntry *entry)
+{
+ return (cert_StartExtensions(entry, crl->arena, SetCrlEntryExts));
+}
+
+SECStatus
+CERT_FindCRLNumberExten(PLArenaPool *arena, CERTCrl *crl,
+ SECItem *value)
+{
+ SECItem encodedExtenValue;
+ SECItem *tmpItem = NULL;
+ SECStatus rv;
+ void *mark = NULL;
+
+ encodedExtenValue.data = NULL;
+ encodedExtenValue.len = 0;
+
+ rv = cert_FindExtension(crl->extensions, SEC_OID_X509_CRL_NUMBER,
+ &encodedExtenValue);
+ if (rv != SECSuccess)
+ return (rv);
+
+ mark = PORT_ArenaMark(arena);
+
+ tmpItem = SECITEM_ArenaDupItem(arena, &encodedExtenValue);
+ if (tmpItem) {
+ rv = SEC_QuickDERDecodeItem(arena, value,
+ SEC_ASN1_GET(SEC_IntegerTemplate),
+ tmpItem);
+ } else {
+ rv = SECFailure;
+ }
+
+ PORT_Free(encodedExtenValue.data);
+ if (rv == SECFailure) {
+ PORT_ArenaRelease(arena, mark);
+ } else {
+ PORT_ArenaUnmark(arena, mark);
+ }
+ return (rv);
+}
+
+SECStatus
+CERT_FindCRLEntryReasonExten(CERTCrlEntry *crlEntry,
+ CERTCRLEntryReasonCode *value)
+{
+ SECItem wrapperItem = { siBuffer, 0 };
+ SECItem tmpItem = { siBuffer, 0 };
+ SECStatus rv;
+ PLArenaPool *arena = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena) {
+ return (SECFailure);
+ }
+
+ rv = cert_FindExtension(crlEntry->extensions, SEC_OID_X509_REASON_CODE,
+ &wrapperItem);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = SEC_QuickDERDecodeItem(arena, &tmpItem,
+ SEC_ASN1_GET(SEC_EnumeratedTemplate),
+ &wrapperItem);
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ *value = (CERTCRLEntryReasonCode)DER_GetInteger(&tmpItem);
+
+loser:
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ if (wrapperItem.data) {
+ PORT_Free(wrapperItem.data);
+ }
+
+ return (rv);
+}
+
+SECStatus
+CERT_FindInvalidDateExten(CERTCrl *crl, PRTime *value)
+{
+ SECItem encodedExtenValue;
+ SECItem decodedExtenValue = { siBuffer, 0 };
+ SECStatus rv;
+
+ encodedExtenValue.data = decodedExtenValue.data = NULL;
+ encodedExtenValue.len = decodedExtenValue.len = 0;
+
+ rv = cert_FindExtension(crl->extensions, SEC_OID_X509_INVALID_DATE, &encodedExtenValue);
+ if (rv != SECSuccess)
+ return (rv);
+
+ rv = SEC_ASN1DecodeItem(NULL, &decodedExtenValue,
+ SEC_ASN1_GET(SEC_GeneralizedTimeTemplate),
+ &encodedExtenValue);
+ if (rv == SECSuccess)
+ rv = DER_GeneralizedTimeToTime(value, &encodedExtenValue);
+ PORT_Free(decodedExtenValue.data);
+ PORT_Free(encodedExtenValue.data);
+ return (rv);
+}
diff --git a/security/nss/lib/certhigh/exports.gyp b/security/nss/lib/certhigh/exports.gyp
new file mode 100644
index 0000000000..b8e2f3ebca
--- /dev/null
+++ b/security/nss/lib/certhigh/exports.gyp
@@ -0,0 +1,33 @@
+# 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_certhigh_exports',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'files': [
+ 'ocsp.h',
+ 'ocspt.h'
+ ],
+ 'destination': '<(nss_public_dist_dir)/<(module)'
+ },
+ {
+ 'files': [
+ 'ocspi.h',
+ 'ocspti.h'
+ ],
+ 'destination': '<(nss_private_dist_dir)/<(module)'
+ }
+ ]
+ }
+ ],
+ 'variables': {
+ 'module': 'nss'
+ }
+}
diff --git a/security/nss/lib/certhigh/manifest.mn b/security/nss/lib/certhigh/manifest.mn
new file mode 100644
index 0000000000..b843e06869
--- /dev/null
+++ b/security/nss/lib/certhigh/manifest.mn
@@ -0,0 +1,35 @@
+#
+# 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 = \
+ ocsp.h \
+ ocspt.h \
+ $(NULL)
+
+PRIVATE_EXPORTS = \
+ ocspti.h \
+ ocspi.h \
+ $(NULL)
+
+MODULE = nss
+
+CSRCS = \
+ certhtml.c \
+ certreq.c \
+ crlv2.c \
+ ocsp.c \
+ ocspsig.c \
+ certhigh.c \
+ certvfy.c \
+ certvfypkix.c \
+ xcrldist.c \
+ $(NULL)
+
+LIBRARY_NAME = certhi
+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/certhigh/ocsp.c b/security/nss/lib/certhigh/ocsp.c
new file mode 100644
index 0000000000..4fccbc9012
--- /dev/null
+++ b/security/nss/lib/certhigh/ocsp.c
@@ -0,0 +1,6119 @@
+/* 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/. */
+
+/*
+ * Implementation of OCSP services, for both client and server.
+ * (XXX, really, mostly just for client right now, but intended to do both.)
+ */
+
+#include "prerror.h"
+#include "prprf.h"
+#include "plarena.h"
+#include "prnetdb.h"
+
+#include "seccomon.h"
+#include "secitem.h"
+#include "secoidt.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "cert.h"
+#include "certi.h"
+#include "xconst.h"
+#include "secerr.h"
+#include "secoid.h"
+#include "hasht.h"
+#include "sechash.h"
+#include "secasn1.h"
+#include "plbase64.h"
+#include "keyhi.h"
+#include "cryptohi.h"
+#include "ocsp.h"
+#include "ocspti.h"
+#include "ocspi.h"
+#include "genname.h"
+#include "certxutl.h"
+#include "pk11func.h" /* for PK11_HashBuf */
+#include <stdarg.h>
+#include <plhash.h>
+
+#define DEFAULT_OCSP_CACHE_SIZE 1000
+#define DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT 1 * 60 * 60L
+#define DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT 24 * 60 * 60L
+#define DEFAULT_OSCP_TIMEOUT_SECONDS 60
+#define MICROSECONDS_PER_SECOND 1000000L
+
+typedef struct OCSPCacheItemStr OCSPCacheItem;
+typedef struct OCSPCacheDataStr OCSPCacheData;
+
+struct OCSPCacheItemStr {
+ /* LRU linking */
+ OCSPCacheItem *moreRecent;
+ OCSPCacheItem *lessRecent;
+
+ /* key */
+ CERTOCSPCertID *certID;
+ /* CertID's arena also used to allocate "this" cache item */
+
+ /* cache control information */
+ PRTime nextFetchAttemptTime;
+
+ /* Cached contents. Use a separate arena, because lifetime is different */
+ PLArenaPool *certStatusArena; /* NULL means: no cert status cached */
+ ocspCertStatus certStatus;
+
+ /* This may contain an error code when no OCSP response is available. */
+ SECErrorCodes missingResponseError;
+
+ PRPackedBool haveThisUpdate;
+ PRPackedBool haveNextUpdate;
+ PRTime thisUpdate;
+ PRTime nextUpdate;
+};
+
+struct OCSPCacheDataStr {
+ PLHashTable *entries;
+ PRUint32 numberOfEntries;
+ OCSPCacheItem *MRUitem; /* most recently used cache item */
+ OCSPCacheItem *LRUitem; /* least recently used cache item */
+};
+
+static struct OCSPGlobalStruct {
+ PRMonitor *monitor;
+ const SEC_HttpClientFcn *defaultHttpClientFcn;
+ PRInt32 maxCacheEntries;
+ PRUint32 minimumSecondsToNextFetchAttempt;
+ PRUint32 maximumSecondsToNextFetchAttempt;
+ PRUint32 timeoutSeconds;
+ OCSPCacheData cache;
+ SEC_OcspFailureMode ocspFailureMode;
+ CERT_StringFromCertFcn alternateOCSPAIAFcn;
+ PRBool forcePost;
+} OCSP_Global = { NULL,
+ NULL,
+ DEFAULT_OCSP_CACHE_SIZE,
+ DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT,
+ DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT,
+ DEFAULT_OSCP_TIMEOUT_SECONDS,
+ { NULL, 0, NULL, NULL },
+ ocspMode_FailureIsVerificationFailure,
+ NULL,
+ PR_FALSE };
+
+/* Forward declarations */
+static SECItem *
+ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena,
+ CERTOCSPRequest *request,
+ const char *location,
+ const char *method,
+ PRTime time,
+ PRBool addServiceLocator,
+ void *pwArg,
+ CERTOCSPRequest **pRequest);
+static SECStatus
+ocsp_GetOCSPStatusFromNetwork(CERTCertDBHandle *handle,
+ CERTOCSPCertID *certID,
+ CERTCertificate *cert,
+ PRTime time,
+ void *pwArg,
+ PRBool *certIDWasConsumed,
+ SECStatus *rv_ocsp);
+
+static SECStatus
+ocsp_GetDecodedVerifiedSingleResponseForID(CERTCertDBHandle *handle,
+ CERTOCSPCertID *certID,
+ CERTCertificate *cert,
+ PRTime time,
+ void *pwArg,
+ const SECItem *encodedResponse,
+ CERTOCSPResponse **pDecodedResponse,
+ CERTOCSPSingleResponse **pSingle);
+
+static SECStatus
+ocsp_CertRevokedAfter(ocspRevokedInfo *revokedInfo, PRTime time);
+
+static CERTOCSPCertID *
+cert_DupOCSPCertID(const CERTOCSPCertID *src);
+
+#ifndef DEBUG
+#define OCSP_TRACE(msg)
+#define OCSP_TRACE_TIME(msg, time)
+#define OCSP_TRACE_CERT(cert)
+#define OCSP_TRACE_CERTID(certid)
+#else
+#define OCSP_TRACE(msg) ocsp_Trace msg
+#define OCSP_TRACE_TIME(msg, time) ocsp_dumpStringWithTime(msg, time)
+#define OCSP_TRACE_CERT(cert) dumpCertificate(cert)
+#define OCSP_TRACE_CERTID(certid) dumpCertID(certid)
+
+#if defined(XP_UNIX) || defined(XP_WIN32) || defined(XP_MACOSX)
+#define NSS_HAVE_GETENV 1
+#endif
+
+static PRBool
+wantOcspTrace(void)
+{
+ static PRBool firstTime = PR_TRUE;
+ static PRBool wantTrace = PR_FALSE;
+
+#ifdef NSS_HAVE_GETENV
+ if (firstTime) {
+ char *ev = PR_GetEnvSecure("NSS_TRACE_OCSP");
+ if (ev && ev[0]) {
+ wantTrace = PR_TRUE;
+ }
+ firstTime = PR_FALSE;
+ }
+#endif
+ return wantTrace;
+}
+
+static void
+ocsp_Trace(const char *format, ...)
+{
+ char buf[2000];
+ va_list args;
+
+ if (!wantOcspTrace())
+ return;
+ va_start(args, format);
+ PR_vsnprintf(buf, sizeof(buf), format, args);
+ va_end(args);
+ PR_LogPrint("%s", buf);
+}
+
+static void
+ocsp_dumpStringWithTime(const char *str, PRTime time)
+{
+ PRExplodedTime timePrintable;
+ char timestr[256];
+
+ if (!wantOcspTrace())
+ return;
+ PR_ExplodeTime(time, PR_GMTParameters, &timePrintable);
+ if (PR_FormatTime(timestr, 256, "%a %b %d %H:%M:%S %Y", &timePrintable)) {
+ ocsp_Trace("OCSP %s %s\n", str, timestr);
+ }
+}
+
+static void
+printHexString(const char *prefix, SECItem *hexval)
+{
+ unsigned int i;
+ char *hexbuf = NULL;
+
+ for (i = 0; i < hexval->len; i++) {
+ if (i != hexval->len - 1) {
+ hexbuf = PR_sprintf_append(hexbuf, "%02x:", hexval->data[i]);
+ } else {
+ hexbuf = PR_sprintf_append(hexbuf, "%02x", hexval->data[i]);
+ }
+ }
+ if (hexbuf) {
+ ocsp_Trace("%s %s\n", prefix, hexbuf);
+ PR_smprintf_free(hexbuf);
+ }
+}
+
+static void
+dumpCertificate(CERTCertificate *cert)
+{
+ if (!wantOcspTrace())
+ return;
+
+ ocsp_Trace("OCSP ----------------\n");
+ ocsp_Trace("OCSP ## SUBJECT: %s\n", cert->subjectName);
+ {
+ PRTime timeBefore, timeAfter;
+ PRExplodedTime beforePrintable, afterPrintable;
+ char beforestr[256], afterstr[256];
+ PRStatus rv1, rv2;
+ DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore);
+ DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter);
+ PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable);
+ PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable);
+ rv1 = PR_FormatTime(beforestr, 256, "%a %b %d %H:%M:%S %Y",
+ &beforePrintable);
+ rv2 = PR_FormatTime(afterstr, 256, "%a %b %d %H:%M:%S %Y",
+ &afterPrintable);
+ ocsp_Trace("OCSP ## VALIDITY: %s to %s\n", rv1 ? beforestr : "",
+ rv2 ? afterstr : "");
+ }
+ ocsp_Trace("OCSP ## ISSUER: %s\n", cert->issuerName);
+ printHexString("OCSP ## SERIAL NUMBER:", &cert->serialNumber);
+}
+
+static void
+dumpCertID(CERTOCSPCertID *certID)
+{
+ if (!wantOcspTrace())
+ return;
+
+ printHexString("OCSP certID issuer", &certID->issuerNameHash);
+ printHexString("OCSP certID serial", &certID->serialNumber);
+}
+#endif
+
+SECStatus
+SEC_RegisterDefaultHttpClient(const SEC_HttpClientFcn *fcnTable)
+{
+ if (!OCSP_Global.monitor) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ OCSP_Global.defaultHttpClientFcn = fcnTable;
+ PR_ExitMonitor(OCSP_Global.monitor);
+
+ return SECSuccess;
+}
+
+SECStatus
+CERT_RegisterAlternateOCSPAIAInfoCallBack(
+ CERT_StringFromCertFcn newCallback,
+ CERT_StringFromCertFcn *oldCallback)
+{
+ CERT_StringFromCertFcn old;
+
+ if (!OCSP_Global.monitor) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ old = OCSP_Global.alternateOCSPAIAFcn;
+ OCSP_Global.alternateOCSPAIAFcn = newCallback;
+ PR_ExitMonitor(OCSP_Global.monitor);
+ if (oldCallback)
+ *oldCallback = old;
+ return SECSuccess;
+}
+
+static PLHashNumber PR_CALLBACK
+ocsp_CacheKeyHashFunction(const void *key)
+{
+ CERTOCSPCertID *cid = (CERTOCSPCertID *)key;
+ PLHashNumber hash = 0;
+ unsigned int i;
+ unsigned char *walk;
+
+ /* a very simple hash calculation for the initial coding phase */
+ walk = (unsigned char *)cid->issuerNameHash.data;
+ for (i = 0; i < cid->issuerNameHash.len; ++i, ++walk) {
+ hash += *walk;
+ }
+ walk = (unsigned char *)cid->issuerKeyHash.data;
+ for (i = 0; i < cid->issuerKeyHash.len; ++i, ++walk) {
+ hash += *walk;
+ }
+ walk = (unsigned char *)cid->serialNumber.data;
+ for (i = 0; i < cid->serialNumber.len; ++i, ++walk) {
+ hash += *walk;
+ }
+ return hash;
+}
+
+static PRIntn PR_CALLBACK
+ocsp_CacheKeyCompareFunction(const void *v1, const void *v2)
+{
+ CERTOCSPCertID *cid1 = (CERTOCSPCertID *)v1;
+ CERTOCSPCertID *cid2 = (CERTOCSPCertID *)v2;
+
+ return (SECEqual == SECITEM_CompareItem(&cid1->issuerNameHash,
+ &cid2->issuerNameHash) &&
+ SECEqual == SECITEM_CompareItem(&cid1->issuerKeyHash,
+ &cid2->issuerKeyHash) &&
+ SECEqual == SECITEM_CompareItem(&cid1->serialNumber,
+ &cid2->serialNumber));
+}
+
+static SECStatus
+ocsp_CopyRevokedInfo(PLArenaPool *arena, ocspCertStatus *dest,
+ ocspRevokedInfo *src)
+{
+ SECStatus rv = SECFailure;
+ void *mark;
+
+ mark = PORT_ArenaMark(arena);
+
+ dest->certStatusInfo.revokedInfo =
+ (ocspRevokedInfo *)PORT_ArenaZAlloc(arena, sizeof(ocspRevokedInfo));
+ if (!dest->certStatusInfo.revokedInfo) {
+ goto loser;
+ }
+
+ rv = SECITEM_CopyItem(arena,
+ &dest->certStatusInfo.revokedInfo->revocationTime,
+ &src->revocationTime);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ if (src->revocationReason) {
+ dest->certStatusInfo.revokedInfo->revocationReason =
+ SECITEM_ArenaDupItem(arena, src->revocationReason);
+ if (!dest->certStatusInfo.revokedInfo->revocationReason) {
+ goto loser;
+ }
+ } else {
+ dest->certStatusInfo.revokedInfo->revocationReason = NULL;
+ }
+
+ PORT_ArenaUnmark(arena, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(arena, mark);
+ return SECFailure;
+}
+
+static SECStatus
+ocsp_CopyCertStatus(PLArenaPool *arena, ocspCertStatus *dest,
+ ocspCertStatus *src)
+{
+ SECStatus rv = SECFailure;
+ dest->certStatusType = src->certStatusType;
+
+ switch (src->certStatusType) {
+ case ocspCertStatus_good:
+ dest->certStatusInfo.goodInfo =
+ SECITEM_ArenaDupItem(arena, src->certStatusInfo.goodInfo);
+ if (dest->certStatusInfo.goodInfo != NULL) {
+ rv = SECSuccess;
+ }
+ break;
+ case ocspCertStatus_revoked:
+ rv = ocsp_CopyRevokedInfo(arena, dest,
+ src->certStatusInfo.revokedInfo);
+ break;
+ case ocspCertStatus_unknown:
+ dest->certStatusInfo.unknownInfo =
+ SECITEM_ArenaDupItem(arena, src->certStatusInfo.unknownInfo);
+ if (dest->certStatusInfo.unknownInfo != NULL) {
+ rv = SECSuccess;
+ }
+ break;
+ case ocspCertStatus_other:
+ default:
+ PORT_Assert(src->certStatusType == ocspCertStatus_other);
+ dest->certStatusInfo.otherInfo =
+ SECITEM_ArenaDupItem(arena, src->certStatusInfo.otherInfo);
+ if (dest->certStatusInfo.otherInfo != NULL) {
+ rv = SECSuccess;
+ }
+ break;
+ }
+ return rv;
+}
+
+static void
+ocsp_AddCacheItemToLinkedList(OCSPCacheData *cache, OCSPCacheItem *new_most_recent)
+{
+ PR_EnterMonitor(OCSP_Global.monitor);
+
+ if (!cache->LRUitem) {
+ cache->LRUitem = new_most_recent;
+ }
+ new_most_recent->lessRecent = cache->MRUitem;
+ new_most_recent->moreRecent = NULL;
+
+ if (cache->MRUitem) {
+ cache->MRUitem->moreRecent = new_most_recent;
+ }
+ cache->MRUitem = new_most_recent;
+
+ PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+static void
+ocsp_RemoveCacheItemFromLinkedList(OCSPCacheData *cache, OCSPCacheItem *item)
+{
+ PR_EnterMonitor(OCSP_Global.monitor);
+
+ if (!item->lessRecent && !item->moreRecent) {
+ /*
+ * Fail gracefully on attempts to remove an item from the list,
+ * which is currently not part of the list.
+ * But check for the edge case it is the single entry in the list.
+ */
+ if (item == cache->LRUitem &&
+ item == cache->MRUitem) {
+ /* remove the single entry */
+ PORT_Assert(cache->numberOfEntries == 1);
+ PORT_Assert(item->moreRecent == NULL);
+ cache->MRUitem = NULL;
+ cache->LRUitem = NULL;
+ }
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return;
+ }
+
+ PORT_Assert(cache->numberOfEntries > 1);
+
+ if (item == cache->LRUitem) {
+ PORT_Assert(item != cache->MRUitem);
+ PORT_Assert(item->lessRecent == NULL);
+ PORT_Assert(item->moreRecent != NULL);
+ PORT_Assert(item->moreRecent->lessRecent == item);
+ cache->LRUitem = item->moreRecent;
+ cache->LRUitem->lessRecent = NULL;
+ } else if (item == cache->MRUitem) {
+ PORT_Assert(item->moreRecent == NULL);
+ PORT_Assert(item->lessRecent != NULL);
+ PORT_Assert(item->lessRecent->moreRecent == item);
+ cache->MRUitem = item->lessRecent;
+ cache->MRUitem->moreRecent = NULL;
+ } else {
+ /* remove an entry in the middle of the list */
+ PORT_Assert(item->moreRecent != NULL);
+ PORT_Assert(item->lessRecent != NULL);
+ PORT_Assert(item->lessRecent->moreRecent == item);
+ PORT_Assert(item->moreRecent->lessRecent == item);
+ item->moreRecent->lessRecent = item->lessRecent;
+ item->lessRecent->moreRecent = item->moreRecent;
+ }
+
+ item->lessRecent = NULL;
+ item->moreRecent = NULL;
+
+ PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+static void
+ocsp_MakeCacheEntryMostRecent(OCSPCacheData *cache, OCSPCacheItem *new_most_recent)
+{
+ OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent THREADID %p\n",
+ PR_GetCurrentThread()));
+ PR_EnterMonitor(OCSP_Global.monitor);
+ if (cache->MRUitem == new_most_recent) {
+ OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent ALREADY MOST\n"));
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return;
+ }
+ OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent NEW entry\n"));
+ ocsp_RemoveCacheItemFromLinkedList(cache, new_most_recent);
+ ocsp_AddCacheItemToLinkedList(cache, new_most_recent);
+ PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+static PRBool
+ocsp_IsCacheDisabled(void)
+{
+ /*
+ * maxCacheEntries == 0 means unlimited cache entries
+ * maxCacheEntries < 0 means cache is disabled
+ */
+ PRBool retval;
+ PR_EnterMonitor(OCSP_Global.monitor);
+ retval = (OCSP_Global.maxCacheEntries < 0);
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return retval;
+}
+
+static OCSPCacheItem *
+ocsp_FindCacheEntry(OCSPCacheData *cache, CERTOCSPCertID *certID)
+{
+ OCSPCacheItem *found_ocsp_item = NULL;
+ OCSP_TRACE(("OCSP ocsp_FindCacheEntry\n"));
+ OCSP_TRACE_CERTID(certID);
+ PR_EnterMonitor(OCSP_Global.monitor);
+ if (ocsp_IsCacheDisabled())
+ goto loser;
+
+ found_ocsp_item = (OCSPCacheItem *)PL_HashTableLookup(
+ cache->entries, certID);
+ if (!found_ocsp_item)
+ goto loser;
+
+ OCSP_TRACE(("OCSP ocsp_FindCacheEntry FOUND!\n"));
+ ocsp_MakeCacheEntryMostRecent(cache, found_ocsp_item);
+
+loser:
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return found_ocsp_item;
+}
+
+static void
+ocsp_FreeCacheItem(OCSPCacheItem *item)
+{
+ OCSP_TRACE(("OCSP ocsp_FreeCacheItem\n"));
+ if (item->certStatusArena) {
+ PORT_FreeArena(item->certStatusArena, PR_FALSE);
+ }
+ if (item->certID->poolp) {
+ /* freeing this poolp arena will also free item */
+ PORT_FreeArena(item->certID->poolp, PR_FALSE);
+ }
+}
+
+static void
+ocsp_RemoveCacheItem(OCSPCacheData *cache, OCSPCacheItem *item)
+{
+ /* The item we're removing could be either the least recently used item,
+ * or it could be an item that couldn't get updated with newer status info
+ * because of an allocation failure, or it could get removed because we're
+ * cleaning up.
+ */
+ OCSP_TRACE(("OCSP ocsp_RemoveCacheItem, THREADID %p\n", PR_GetCurrentThread()));
+ PR_EnterMonitor(OCSP_Global.monitor);
+
+ ocsp_RemoveCacheItemFromLinkedList(cache, item);
+#ifdef DEBUG
+ {
+ PRBool couldRemoveFromHashTable = PL_HashTableRemove(cache->entries,
+ item->certID);
+ PORT_Assert(couldRemoveFromHashTable);
+ }
+#else
+ PL_HashTableRemove(cache->entries, item->certID);
+#endif
+ --cache->numberOfEntries;
+ ocsp_FreeCacheItem(item);
+ PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+static void
+ocsp_CheckCacheSize(OCSPCacheData *cache)
+{
+ OCSP_TRACE(("OCSP ocsp_CheckCacheSize\n"));
+ PR_EnterMonitor(OCSP_Global.monitor);
+ if (OCSP_Global.maxCacheEntries > 0) {
+ /* Cache is not disabled. Number of cache entries is limited.
+ * The monitor ensures that maxCacheEntries remains positive.
+ */
+ while (cache->numberOfEntries >
+ (PRUint32)OCSP_Global.maxCacheEntries) {
+ ocsp_RemoveCacheItem(cache, cache->LRUitem);
+ }
+ }
+ PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+SECStatus
+CERT_ClearOCSPCache(void)
+{
+ OCSP_TRACE(("OCSP CERT_ClearOCSPCache\n"));
+ PR_EnterMonitor(OCSP_Global.monitor);
+ while (OCSP_Global.cache.numberOfEntries > 0) {
+ ocsp_RemoveCacheItem(&OCSP_Global.cache,
+ OCSP_Global.cache.LRUitem);
+ }
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return SECSuccess;
+}
+
+static SECStatus
+ocsp_CreateCacheItemAndConsumeCertID(OCSPCacheData *cache,
+ CERTOCSPCertID *certID,
+ OCSPCacheItem **pCacheItem)
+{
+ PLArenaPool *arena;
+ void *mark;
+ PLHashEntry *new_hash_entry;
+ OCSPCacheItem *item;
+
+ PORT_Assert(pCacheItem != NULL);
+ *pCacheItem = NULL;
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ arena = certID->poolp;
+ mark = PORT_ArenaMark(arena);
+
+ /* ZAlloc will init all Bools to False and all Pointers to NULL
+ and all error codes to zero/good. */
+ item = (OCSPCacheItem *)PORT_ArenaZAlloc(certID->poolp,
+ sizeof(OCSPCacheItem));
+ if (!item) {
+ goto loser;
+ }
+ item->certID = certID;
+ new_hash_entry = PL_HashTableAdd(cache->entries, item->certID,
+ item);
+ if (!new_hash_entry) {
+ goto loser;
+ }
+ ++cache->numberOfEntries;
+ PORT_ArenaUnmark(arena, mark);
+ ocsp_AddCacheItemToLinkedList(cache, item);
+ *pCacheItem = item;
+
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(arena, mark);
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return SECFailure;
+}
+
+static SECStatus
+ocsp_SetCacheItemResponse(OCSPCacheItem *item,
+ const CERTOCSPSingleResponse *response)
+{
+ if (item->certStatusArena) {
+ PORT_FreeArena(item->certStatusArena, PR_FALSE);
+ item->certStatusArena = NULL;
+ }
+ item->haveThisUpdate = item->haveNextUpdate = PR_FALSE;
+ if (response) {
+ SECStatus rv;
+ item->certStatusArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (item->certStatusArena == NULL) {
+ return SECFailure;
+ }
+ rv = ocsp_CopyCertStatus(item->certStatusArena, &item->certStatus,
+ response->certStatus);
+ if (rv != SECSuccess) {
+ PORT_FreeArena(item->certStatusArena, PR_FALSE);
+ item->certStatusArena = NULL;
+ return rv;
+ }
+ item->missingResponseError = 0;
+ rv = DER_GeneralizedTimeToTime(&item->thisUpdate,
+ &response->thisUpdate);
+ item->haveThisUpdate = (rv == SECSuccess);
+ if (response->nextUpdate) {
+ rv = DER_GeneralizedTimeToTime(&item->nextUpdate,
+ response->nextUpdate);
+ item->haveNextUpdate = (rv == SECSuccess);
+ } else {
+ item->haveNextUpdate = PR_FALSE;
+ }
+ }
+ return SECSuccess;
+}
+
+static void
+ocsp_FreshenCacheItemNextFetchAttemptTime(OCSPCacheItem *cacheItem)
+{
+ PRTime now;
+ PRTime earliestAllowedNextFetchAttemptTime;
+ PRTime latestTimeWhenResponseIsConsideredFresh;
+
+ OCSP_TRACE(("OCSP ocsp_FreshenCacheItemNextFetchAttemptTime\n"));
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+
+ now = PR_Now();
+ OCSP_TRACE_TIME("now:", now);
+
+ if (cacheItem->haveThisUpdate) {
+ OCSP_TRACE_TIME("thisUpdate:", cacheItem->thisUpdate);
+ latestTimeWhenResponseIsConsideredFresh = cacheItem->thisUpdate +
+ OCSP_Global.maximumSecondsToNextFetchAttempt *
+ MICROSECONDS_PER_SECOND;
+ OCSP_TRACE_TIME("latestTimeWhenResponseIsConsideredFresh:",
+ latestTimeWhenResponseIsConsideredFresh);
+ } else {
+ latestTimeWhenResponseIsConsideredFresh = now +
+ OCSP_Global.minimumSecondsToNextFetchAttempt *
+ MICROSECONDS_PER_SECOND;
+ OCSP_TRACE_TIME("no thisUpdate, "
+ "latestTimeWhenResponseIsConsideredFresh:",
+ latestTimeWhenResponseIsConsideredFresh);
+ }
+
+ if (cacheItem->haveNextUpdate) {
+ OCSP_TRACE_TIME("have nextUpdate:", cacheItem->nextUpdate);
+ }
+
+ if (cacheItem->haveNextUpdate &&
+ cacheItem->nextUpdate < latestTimeWhenResponseIsConsideredFresh) {
+ latestTimeWhenResponseIsConsideredFresh = cacheItem->nextUpdate;
+ OCSP_TRACE_TIME("nextUpdate is smaller than latestFresh, setting "
+ "latestTimeWhenResponseIsConsideredFresh:",
+ latestTimeWhenResponseIsConsideredFresh);
+ }
+
+ earliestAllowedNextFetchAttemptTime = now +
+ OCSP_Global.minimumSecondsToNextFetchAttempt *
+ MICROSECONDS_PER_SECOND;
+ OCSP_TRACE_TIME("earliestAllowedNextFetchAttemptTime:",
+ earliestAllowedNextFetchAttemptTime);
+
+ if (latestTimeWhenResponseIsConsideredFresh <
+ earliestAllowedNextFetchAttemptTime) {
+ latestTimeWhenResponseIsConsideredFresh =
+ earliestAllowedNextFetchAttemptTime;
+ OCSP_TRACE_TIME("latest < earliest, setting latest to:",
+ latestTimeWhenResponseIsConsideredFresh);
+ }
+
+ cacheItem->nextFetchAttemptTime =
+ latestTimeWhenResponseIsConsideredFresh;
+ OCSP_TRACE_TIME("nextFetchAttemptTime",
+ latestTimeWhenResponseIsConsideredFresh);
+
+ PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+static PRBool
+ocsp_IsCacheItemFresh(OCSPCacheItem *cacheItem)
+{
+ PRTime now;
+ PRBool fresh;
+
+ now = PR_Now();
+
+ fresh = cacheItem->nextFetchAttemptTime > now;
+
+ /* Work around broken OCSP responders that return unknown responses for
+ * certificates, especially certificates that were just recently issued.
+ */
+ if (fresh && cacheItem->certStatusArena &&
+ cacheItem->certStatus.certStatusType == ocspCertStatus_unknown) {
+ fresh = PR_FALSE;
+ }
+
+ OCSP_TRACE(("OCSP ocsp_IsCacheItemFresh: %d\n", fresh));
+
+ return fresh;
+}
+
+/*
+ * Status in *certIDWasConsumed will always be correct, regardless of
+ * return value.
+ * If the caller is unable to transfer ownership of certID,
+ * then the caller must set certIDWasConsumed to NULL,
+ * and this function will potentially duplicate the certID object.
+ */
+static SECStatus
+ocsp_CreateOrUpdateCacheEntry(OCSPCacheData *cache,
+ CERTOCSPCertID *certID,
+ CERTOCSPSingleResponse *single,
+ PRBool *certIDWasConsumed)
+{
+ SECStatus rv;
+ OCSPCacheItem *cacheItem;
+ OCSP_TRACE(("OCSP ocsp_CreateOrUpdateCacheEntry\n"));
+
+ if (certIDWasConsumed)
+ *certIDWasConsumed = PR_FALSE;
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ PORT_Assert(OCSP_Global.maxCacheEntries >= 0);
+
+ cacheItem = ocsp_FindCacheEntry(cache, certID);
+
+ /* Don't replace an unknown or revoked entry with an error entry, even if
+ * the existing entry is expired. Instead, we'll continue to use the
+ * existing (possibly expired) cache entry until we receive a valid signed
+ * response to replace it.
+ */
+ if (!single && cacheItem && cacheItem->certStatusArena &&
+ (cacheItem->certStatus.certStatusType == ocspCertStatus_revoked ||
+ cacheItem->certStatus.certStatusType == ocspCertStatus_unknown)) {
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return SECSuccess;
+ }
+
+ if (!cacheItem) {
+ CERTOCSPCertID *myCertID;
+ if (certIDWasConsumed) {
+ myCertID = certID;
+ *certIDWasConsumed = PR_TRUE;
+ } else {
+ myCertID = cert_DupOCSPCertID(certID);
+ if (!myCertID) {
+ PR_ExitMonitor(OCSP_Global.monitor);
+ PORT_SetError(PR_OUT_OF_MEMORY_ERROR);
+ return SECFailure;
+ }
+ }
+
+ rv = ocsp_CreateCacheItemAndConsumeCertID(cache, myCertID,
+ &cacheItem);
+ if (rv != SECSuccess) {
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return rv;
+ }
+ }
+ if (single) {
+ PRTime thisUpdate;
+ rv = DER_GeneralizedTimeToTime(&thisUpdate, &single->thisUpdate);
+
+ if (!cacheItem->haveThisUpdate ||
+ (rv == SECSuccess && cacheItem->thisUpdate < thisUpdate)) {
+ rv = ocsp_SetCacheItemResponse(cacheItem, single);
+ if (rv != SECSuccess) {
+ ocsp_RemoveCacheItem(cache, cacheItem);
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return rv;
+ }
+ } else {
+ OCSP_TRACE(("Not caching response because the response is not "
+ "newer than the cache"));
+ }
+ } else {
+ cacheItem->missingResponseError = PORT_GetError();
+ if (cacheItem->certStatusArena) {
+ PORT_FreeArena(cacheItem->certStatusArena, PR_FALSE);
+ cacheItem->certStatusArena = NULL;
+ }
+ }
+ ocsp_FreshenCacheItemNextFetchAttemptTime(cacheItem);
+ ocsp_CheckCacheSize(cache);
+
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return SECSuccess;
+}
+
+extern SECStatus
+CERT_SetOCSPFailureMode(SEC_OcspFailureMode ocspFailureMode)
+{
+ switch (ocspFailureMode) {
+ case ocspMode_FailureIsVerificationFailure:
+ case ocspMode_FailureIsNotAVerificationFailure:
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ OCSP_Global.ocspFailureMode = ocspFailureMode;
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return SECSuccess;
+}
+
+SECStatus
+CERT_OCSPCacheSettings(PRInt32 maxCacheEntries,
+ PRUint32 minimumSecondsToNextFetchAttempt,
+ PRUint32 maximumSecondsToNextFetchAttempt)
+{
+ if (minimumSecondsToNextFetchAttempt > maximumSecondsToNextFetchAttempt ||
+ maxCacheEntries < -1) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+
+ if (maxCacheEntries < 0) {
+ OCSP_Global.maxCacheEntries = -1; /* disable cache */
+ } else if (maxCacheEntries == 0) {
+ OCSP_Global.maxCacheEntries = 0; /* unlimited cache entries */
+ } else {
+ OCSP_Global.maxCacheEntries = maxCacheEntries;
+ }
+
+ if (minimumSecondsToNextFetchAttempt <
+ OCSP_Global.minimumSecondsToNextFetchAttempt ||
+ maximumSecondsToNextFetchAttempt <
+ OCSP_Global.maximumSecondsToNextFetchAttempt) {
+ /*
+ * Ensure our existing cache entries are not used longer than the
+ * new settings allow, we're lazy and just clear the cache
+ */
+ CERT_ClearOCSPCache();
+ }
+
+ OCSP_Global.minimumSecondsToNextFetchAttempt =
+ minimumSecondsToNextFetchAttempt;
+ OCSP_Global.maximumSecondsToNextFetchAttempt =
+ maximumSecondsToNextFetchAttempt;
+ ocsp_CheckCacheSize(&OCSP_Global.cache);
+
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return SECSuccess;
+}
+
+SECStatus
+CERT_SetOCSPTimeout(PRUint32 seconds)
+{
+ /* no locking, see bug 406120 */
+ OCSP_Global.timeoutSeconds = seconds;
+ return SECSuccess;
+}
+
+/* this function is called at NSS initialization time */
+SECStatus
+OCSP_InitGlobal(void)
+{
+ SECStatus rv = SECFailure;
+
+ if (OCSP_Global.monitor == NULL) {
+ OCSP_Global.monitor = PR_NewMonitor();
+ }
+ if (!OCSP_Global.monitor)
+ return SECFailure;
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ if (!OCSP_Global.cache.entries) {
+ OCSP_Global.cache.entries =
+ PL_NewHashTable(0,
+ ocsp_CacheKeyHashFunction,
+ ocsp_CacheKeyCompareFunction,
+ PL_CompareValues,
+ NULL,
+ NULL);
+ OCSP_Global.ocspFailureMode = ocspMode_FailureIsVerificationFailure;
+ OCSP_Global.cache.numberOfEntries = 0;
+ OCSP_Global.cache.MRUitem = NULL;
+ OCSP_Global.cache.LRUitem = NULL;
+ } else {
+ /*
+ * NSS might call this function twice while attempting to init.
+ * But it's not allowed to call this again after any activity.
+ */
+ PORT_Assert(OCSP_Global.cache.numberOfEntries == 0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ }
+ if (OCSP_Global.cache.entries)
+ rv = SECSuccess;
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return rv;
+}
+
+SECStatus
+OCSP_ShutdownGlobal(void)
+{
+ if (!OCSP_Global.monitor)
+ return SECSuccess;
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ if (OCSP_Global.cache.entries) {
+ CERT_ClearOCSPCache();
+ PL_HashTableDestroy(OCSP_Global.cache.entries);
+ OCSP_Global.cache.entries = NULL;
+ }
+ PORT_Assert(OCSP_Global.cache.numberOfEntries == 0);
+ OCSP_Global.cache.MRUitem = NULL;
+ OCSP_Global.cache.LRUitem = NULL;
+
+ OCSP_Global.defaultHttpClientFcn = NULL;
+ OCSP_Global.maxCacheEntries = DEFAULT_OCSP_CACHE_SIZE;
+ OCSP_Global.minimumSecondsToNextFetchAttempt =
+ DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT;
+ OCSP_Global.maximumSecondsToNextFetchAttempt =
+ DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT;
+ OCSP_Global.ocspFailureMode =
+ ocspMode_FailureIsVerificationFailure;
+ PR_ExitMonitor(OCSP_Global.monitor);
+
+ PR_DestroyMonitor(OCSP_Global.monitor);
+ OCSP_Global.monitor = NULL;
+ return SECSuccess;
+}
+
+/*
+ * A return value of NULL means:
+ * The application did not register it's own HTTP client.
+ */
+const SEC_HttpClientFcn *
+SEC_GetRegisteredHttpClient(void)
+{
+ const SEC_HttpClientFcn *retval;
+
+ if (!OCSP_Global.monitor) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return NULL;
+ }
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ retval = OCSP_Global.defaultHttpClientFcn;
+ PR_ExitMonitor(OCSP_Global.monitor);
+
+ return retval;
+}
+
+/*
+ * The following structure is only used internally. It is allocated when
+ * someone turns on OCSP checking, and hangs off of the status-configuration
+ * structure in the certdb structure. We use it to keep configuration
+ * information specific to OCSP checking.
+ */
+typedef struct ocspCheckingContextStr {
+ PRBool useDefaultResponder;
+ char *defaultResponderURI;
+ char *defaultResponderNickname;
+ CERTCertificate *defaultResponderCert;
+} ocspCheckingContext;
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+SEC_ASN1_MKSUB(SEC_IntegerTemplate)
+SEC_ASN1_MKSUB(SEC_NullTemplate)
+SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
+SEC_ASN1_MKSUB(SEC_PointerToAnyTemplate)
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+SEC_ASN1_MKSUB(SEC_SequenceOfAnyTemplate)
+SEC_ASN1_MKSUB(SEC_PointerToGeneralizedTimeTemplate)
+SEC_ASN1_MKSUB(SEC_PointerToEnumeratedTemplate)
+
+/*
+ * Forward declarations of sub-types, so I can lay out the types in the
+ * same order as the ASN.1 is laid out in the OCSP spec itself.
+ *
+ * These are in alphabetical order (case-insensitive); please keep it that way!
+ */
+extern const SEC_ASN1Template ocsp_CertIDTemplate[];
+extern const SEC_ASN1Template ocsp_PointerToSignatureTemplate[];
+extern const SEC_ASN1Template ocsp_PointerToResponseBytesTemplate[];
+extern const SEC_ASN1Template ocsp_ResponseDataTemplate[];
+extern const SEC_ASN1Template ocsp_RevokedInfoTemplate[];
+extern const SEC_ASN1Template ocsp_SingleRequestTemplate[];
+extern const SEC_ASN1Template ocsp_SingleResponseTemplate[];
+extern const SEC_ASN1Template ocsp_TBSRequestTemplate[];
+
+/*
+ * Request-related templates...
+ */
+
+/*
+ * OCSPRequest ::= SEQUENCE {
+ * tbsRequest TBSRequest,
+ * optionalSignature [0] EXPLICIT Signature OPTIONAL }
+ */
+static const SEC_ASN1Template ocsp_OCSPRequestTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTOCSPRequest) },
+ { SEC_ASN1_POINTER,
+ offsetof(CERTOCSPRequest, tbsRequest),
+ ocsp_TBSRequestTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(CERTOCSPRequest, optionalSignature),
+ ocsp_PointerToSignatureTemplate },
+ { 0 }
+};
+
+/*
+ * TBSRequest ::= SEQUENCE {
+ * version [0] EXPLICIT Version DEFAULT v1,
+ * requestorName [1] EXPLICIT GeneralName OPTIONAL,
+ * requestList SEQUENCE OF Request,
+ * requestExtensions [2] EXPLICIT Extensions OPTIONAL }
+ *
+ * Version ::= INTEGER { v1(0) }
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_TBSRequestTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(ocspTBSRequest) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | /* XXX DER_DEFAULT */
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(ocspTBSRequest, version),
+ SEC_ASN1_SUB(SEC_IntegerTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
+ offsetof(ocspTBSRequest, derRequestorName),
+ SEC_ASN1_SUB(SEC_PointerToAnyTemplate) },
+ { SEC_ASN1_SEQUENCE_OF,
+ offsetof(ocspTBSRequest, requestList),
+ ocsp_SingleRequestTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2,
+ offsetof(ocspTBSRequest, requestExtensions),
+ CERT_SequenceOfCertExtensionTemplate },
+ { 0 }
+};
+
+/*
+ * Signature ::= SEQUENCE {
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING,
+ * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ */
+static const SEC_ASN1Template ocsp_SignatureTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(ocspSignature) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(ocspSignature, signatureAlgorithm),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_BIT_STRING,
+ offsetof(ocspSignature, signature) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(ocspSignature, derCerts),
+ SEC_ASN1_SUB(SEC_SequenceOfAnyTemplate) },
+ { 0 }
+};
+
+/*
+ * This template is just an extra level to use in an explicitly-tagged
+ * reference to a Signature.
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_PointerToSignatureTemplate[] = {
+ { SEC_ASN1_POINTER, 0, ocsp_SignatureTemplate }
+};
+
+/*
+ * Request ::= SEQUENCE {
+ * reqCert CertID,
+ * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_SingleRequestTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(ocspSingleRequest) },
+ { SEC_ASN1_POINTER,
+ offsetof(ocspSingleRequest, reqCert),
+ ocsp_CertIDTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(ocspSingleRequest, singleRequestExtensions),
+ CERT_SequenceOfCertExtensionTemplate },
+ { 0 }
+};
+
+/*
+ * This data structure and template (CertID) is used by both OCSP
+ * requests and responses. It is the only one that is shared.
+ *
+ * CertID ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * issuerNameHash OCTET STRING, -- Hash of Issuer DN
+ * issuerKeyHash OCTET STRING, -- Hash of Issuer public key
+ * serialNumber CertificateSerialNumber }
+ *
+ * CertificateSerialNumber ::= INTEGER
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_CertIDTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTOCSPCertID) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(CERTOCSPCertID, hashAlgorithm),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(CERTOCSPCertID, issuerNameHash) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(CERTOCSPCertID, issuerKeyHash) },
+ { SEC_ASN1_INTEGER,
+ offsetof(CERTOCSPCertID, serialNumber) },
+ { 0 }
+};
+
+/*
+ * Response-related templates...
+ */
+
+/*
+ * OCSPResponse ::= SEQUENCE {
+ * responseStatus OCSPResponseStatus,
+ * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
+ */
+const SEC_ASN1Template ocsp_OCSPResponseTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTOCSPResponse) },
+ { SEC_ASN1_ENUMERATED,
+ offsetof(CERTOCSPResponse, responseStatus) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(CERTOCSPResponse, responseBytes),
+ ocsp_PointerToResponseBytesTemplate },
+ { 0 }
+};
+
+/*
+ * ResponseBytes ::= SEQUENCE {
+ * responseType OBJECT IDENTIFIER,
+ * response OCTET STRING }
+ */
+const SEC_ASN1Template ocsp_ResponseBytesTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(ocspResponseBytes) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(ocspResponseBytes, responseType) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(ocspResponseBytes, response) },
+ { 0 }
+};
+
+/*
+ * This template is just an extra level to use in an explicitly-tagged
+ * reference to a ResponseBytes.
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_PointerToResponseBytesTemplate[] = {
+ { SEC_ASN1_POINTER, 0, ocsp_ResponseBytesTemplate }
+};
+
+/*
+ * BasicOCSPResponse ::= SEQUENCE {
+ * tbsResponseData ResponseData,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING,
+ * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ */
+static const SEC_ASN1Template ocsp_BasicOCSPResponseTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(ocspBasicOCSPResponse) },
+ { SEC_ASN1_ANY | SEC_ASN1_SAVE,
+ offsetof(ocspBasicOCSPResponse, tbsResponseDataDER) },
+ { SEC_ASN1_POINTER,
+ offsetof(ocspBasicOCSPResponse, tbsResponseData),
+ ocsp_ResponseDataTemplate },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(ocspBasicOCSPResponse, responseSignature.signatureAlgorithm),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_BIT_STRING,
+ offsetof(ocspBasicOCSPResponse, responseSignature.signature) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(ocspBasicOCSPResponse, responseSignature.derCerts),
+ SEC_ASN1_SUB(SEC_SequenceOfAnyTemplate) },
+ { 0 }
+};
+
+/*
+ * ResponseData ::= SEQUENCE {
+ * version [0] EXPLICIT Version DEFAULT v1,
+ * responderID ResponderID,
+ * producedAt GeneralizedTime,
+ * responses SEQUENCE OF SingleResponse,
+ * responseExtensions [1] EXPLICIT Extensions OPTIONAL }
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_ResponseDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(ocspResponseData) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | /* XXX DER_DEFAULT */
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(ocspResponseData, version),
+ SEC_ASN1_SUB(SEC_IntegerTemplate) },
+ { SEC_ASN1_ANY,
+ offsetof(ocspResponseData, derResponderID) },
+ { SEC_ASN1_GENERALIZED_TIME,
+ offsetof(ocspResponseData, producedAt) },
+ { SEC_ASN1_SEQUENCE_OF,
+ offsetof(ocspResponseData, responses),
+ ocsp_SingleResponseTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(ocspResponseData, responseExtensions),
+ CERT_SequenceOfCertExtensionTemplate },
+ { 0 }
+};
+
+/*
+ * ResponderID ::= CHOICE {
+ * byName [1] EXPLICIT Name,
+ * byKey [2] EXPLICIT KeyHash }
+ *
+ * KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
+ * (excluding the tag and length fields)
+ *
+ * XXX Because the ASN.1 encoder and decoder currently do not provide
+ * a way to automatically handle a CHOICE, we need to do it in two
+ * steps, looking at the type tag and feeding the exact choice back
+ * to the ASN.1 code. Hopefully that will change someday and this
+ * can all be simplified down into a single template. Anyway, for
+ * now we list each choice as its own template:
+ */
+const SEC_ASN1Template ocsp_ResponderIDByNameTemplate[] = {
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(ocspResponderID, responderIDValue.name),
+ CERT_NameTemplate }
+};
+const SEC_ASN1Template ocsp_ResponderIDByKeyTemplate[] = {
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 2,
+ offsetof(ocspResponderID, responderIDValue.keyHash),
+ SEC_ASN1_SUB(SEC_OctetStringTemplate) }
+};
+static const SEC_ASN1Template ocsp_ResponderIDOtherTemplate[] = {
+ { SEC_ASN1_ANY,
+ offsetof(ocspResponderID, responderIDValue.other) }
+};
+
+/* Decode choice container, but leave x509 name object encoded */
+static const SEC_ASN1Template ocsp_ResponderIDDerNameTemplate[] = {
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 1,
+ 0, SEC_ASN1_SUB(SEC_AnyTemplate) }
+};
+
+/*
+ * SingleResponse ::= SEQUENCE {
+ * certID CertID,
+ * certStatus CertStatus,
+ * thisUpdate GeneralizedTime,
+ * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
+ * singleExtensions [1] EXPLICIT Extensions OPTIONAL }
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_SingleResponseTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTOCSPSingleResponse) },
+ { SEC_ASN1_POINTER,
+ offsetof(CERTOCSPSingleResponse, certID),
+ ocsp_CertIDTemplate },
+ { SEC_ASN1_ANY,
+ offsetof(CERTOCSPSingleResponse, derCertStatus) },
+ { SEC_ASN1_GENERALIZED_TIME,
+ offsetof(CERTOCSPSingleResponse, thisUpdate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(CERTOCSPSingleResponse, nextUpdate),
+ SEC_ASN1_SUB(SEC_PointerToGeneralizedTimeTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(CERTOCSPSingleResponse, singleExtensions),
+ CERT_SequenceOfCertExtensionTemplate },
+ { 0 }
+};
+
+/*
+ * CertStatus ::= CHOICE {
+ * good [0] IMPLICIT NULL,
+ * revoked [1] IMPLICIT RevokedInfo,
+ * unknown [2] IMPLICIT UnknownInfo }
+ *
+ * Because the ASN.1 encoder and decoder currently do not provide
+ * a way to automatically handle a CHOICE, we need to do it in two
+ * steps, looking at the type tag and feeding the exact choice back
+ * to the ASN.1 code. Hopefully that will change someday and this
+ * can all be simplified down into a single template. Anyway, for
+ * now we list each choice as its own template:
+ */
+static const SEC_ASN1Template ocsp_CertStatusGoodTemplate[] = {
+ { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(ocspCertStatus, certStatusInfo.goodInfo),
+ SEC_ASN1_SUB(SEC_NullTemplate) }
+};
+static const SEC_ASN1Template ocsp_CertStatusRevokedTemplate[] = {
+ { SEC_ASN1_POINTER | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(ocspCertStatus, certStatusInfo.revokedInfo),
+ ocsp_RevokedInfoTemplate }
+};
+static const SEC_ASN1Template ocsp_CertStatusUnknownTemplate[] = {
+ { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2,
+ offsetof(ocspCertStatus, certStatusInfo.unknownInfo),
+ SEC_ASN1_SUB(SEC_NullTemplate) }
+};
+static const SEC_ASN1Template ocsp_CertStatusOtherTemplate[] = {
+ { SEC_ASN1_POINTER | SEC_ASN1_XTRN,
+ offsetof(ocspCertStatus, certStatusInfo.otherInfo),
+ SEC_ASN1_SUB(SEC_AnyTemplate) }
+};
+
+/*
+ * RevokedInfo ::= SEQUENCE {
+ * revocationTime GeneralizedTime,
+ * revocationReason [0] EXPLICIT CRLReason OPTIONAL }
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_RevokedInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(ocspRevokedInfo) },
+ { SEC_ASN1_GENERALIZED_TIME,
+ offsetof(ocspRevokedInfo, revocationTime) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 0,
+ offsetof(ocspRevokedInfo, revocationReason),
+ SEC_ASN1_SUB(SEC_PointerToEnumeratedTemplate) },
+ { 0 }
+};
+
+/*
+ * OCSP-specific extension templates:
+ */
+
+/*
+ * ServiceLocator ::= SEQUENCE {
+ * issuer Name,
+ * locator AuthorityInfoAccessSyntax OPTIONAL }
+ */
+static const SEC_ASN1Template ocsp_ServiceLocatorTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(ocspServiceLocator) },
+ { SEC_ASN1_POINTER,
+ offsetof(ocspServiceLocator, issuer),
+ CERT_NameTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
+ offsetof(ocspServiceLocator, locator) },
+ { 0 }
+};
+
+/*
+ * REQUEST SUPPORT FUNCTIONS (encode/create/decode/destroy):
+ */
+
+/*
+ * FUNCTION: CERT_EncodeOCSPRequest
+ * DER encodes an OCSP Request, possibly adding a signature as well.
+ * XXX Signing is not yet supported, however; see comments in code.
+ * INPUTS:
+ * PLArenaPool *arena
+ * The return value is allocated from here.
+ * If a NULL is passed in, allocation is done from the heap instead.
+ * CERTOCSPRequest *request
+ * The request to be encoded.
+ * void *pwArg
+ * Pointer to argument for password prompting, if needed. (Definitely
+ * not needed if not signing.)
+ * RETURN:
+ * Returns a NULL on error and a pointer to the SECItem with the
+ * encoded value otherwise. Any error is likely to be low-level
+ * (e.g. no memory).
+ */
+SECItem *
+CERT_EncodeOCSPRequest(PLArenaPool *arena, CERTOCSPRequest *request,
+ void *pwArg)
+{
+ SECStatus rv;
+
+ /* XXX All of these should generate errors if they fail. */
+ PORT_Assert(request);
+ PORT_Assert(request->tbsRequest);
+
+ if (request->tbsRequest->extensionHandle != NULL) {
+ rv = CERT_FinishExtensions(request->tbsRequest->extensionHandle);
+ request->tbsRequest->extensionHandle = NULL;
+ if (rv != SECSuccess)
+ return NULL;
+ }
+
+ /*
+ * XXX When signed requests are supported and request->optionalSignature
+ * is not NULL:
+ * - need to encode tbsRequest->requestorName
+ * - need to encode tbsRequest
+ * - need to sign that encoded result (using cert in sig), filling in the
+ * request->optionalSignature structure with the result, the signing
+ * algorithm and (perhaps?) the cert (and its chain?) in derCerts
+ */
+
+ return SEC_ASN1EncodeItem(arena, NULL, request, ocsp_OCSPRequestTemplate);
+}
+
+/*
+ * FUNCTION: CERT_DecodeOCSPRequest
+ * Decode a DER encoded OCSP Request.
+ * INPUTS:
+ * SECItem *src
+ * Pointer to a SECItem holding DER encoded OCSP Request.
+ * RETURN:
+ * Returns a pointer to a CERTOCSPRequest containing the decoded request.
+ * On error, returns NULL. Most likely error is trouble decoding
+ * (SEC_ERROR_OCSP_MALFORMED_REQUEST), or low-level problem (no memory).
+ */
+CERTOCSPRequest *
+CERT_DecodeOCSPRequest(const SECItem *src)
+{
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECFailure;
+ CERTOCSPRequest *dest = NULL;
+ int i;
+ SECItem newSrc;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+ dest = (CERTOCSPRequest *)PORT_ArenaZAlloc(arena,
+ sizeof(CERTOCSPRequest));
+ if (dest == NULL) {
+ goto loser;
+ }
+ dest->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, &newSrc, src);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = SEC_QuickDERDecodeItem(arena, dest, ocsp_OCSPRequestTemplate, &newSrc);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_BAD_DER)
+ PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST);
+ goto loser;
+ }
+
+ /*
+ * XXX I would like to find a way to get rid of the necessity
+ * of doing this copying of the arena pointer.
+ */
+ for (i = 0; dest->tbsRequest->requestList[i] != NULL; i++) {
+ dest->tbsRequest->requestList[i]->arena = arena;
+ }
+
+ return dest;
+
+loser:
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+SECStatus
+CERT_DestroyOCSPCertID(CERTOCSPCertID *certID)
+{
+ if (certID && certID->poolp) {
+ PORT_FreeArena(certID->poolp, PR_FALSE);
+ return SECSuccess;
+ }
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+}
+
+/*
+ * Digest data using the specified algorithm.
+ * The necessary storage for the digest data is allocated. If "fill" is
+ * non-null, the data is put there, otherwise a SECItem is allocated.
+ * Allocation from "arena" if it is non-null, heap otherwise. Any problem
+ * results in a NULL being returned (and an appropriate error set).
+ */
+
+SECItem *
+ocsp_DigestValue(PLArenaPool *arena, SECOidTag digestAlg,
+ SECItem *fill, const SECItem *src)
+{
+ const SECHashObject *digestObject;
+ SECItem *result = NULL;
+ void *mark = NULL;
+ void *digestBuff = NULL;
+
+ if (arena != NULL) {
+ mark = PORT_ArenaMark(arena);
+ }
+
+ digestObject = HASH_GetHashObjectByOidTag(digestAlg);
+ if (digestObject == NULL) {
+ goto loser;
+ }
+
+ if (fill == NULL || fill->data == NULL) {
+ result = SECITEM_AllocItem(arena, fill, digestObject->length);
+ if (result == NULL) {
+ goto loser;
+ }
+ digestBuff = result->data;
+ } else {
+ if (fill->len < digestObject->length) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+ digestBuff = fill->data;
+ }
+
+ if (PK11_HashBuf(digestAlg, digestBuff,
+ src->data, src->len) != SECSuccess) {
+ goto loser;
+ }
+
+ if (arena != NULL) {
+ PORT_ArenaUnmark(arena, mark);
+ }
+
+ if (result == NULL) {
+ result = fill;
+ }
+ return result;
+
+loser:
+ if (arena != NULL) {
+ PORT_ArenaRelease(arena, mark);
+ } else {
+ if (result != NULL) {
+ SECITEM_FreeItem(result, (fill == NULL) ? PR_TRUE : PR_FALSE);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Digest the cert's subject public key using the specified algorithm.
+ * The necessary storage for the digest data is allocated. If "fill" is
+ * non-null, the data is put there, otherwise a SECItem is allocated.
+ * Allocation from "arena" if it is non-null, heap otherwise. Any problem
+ * results in a NULL being returned (and an appropriate error set).
+ */
+SECItem *
+CERT_GetSubjectPublicKeyDigest(PLArenaPool *arena, const CERTCertificate *cert,
+ SECOidTag digestAlg, SECItem *fill)
+{
+ SECItem spk;
+
+ /*
+ * Copy just the length and data pointer (nothing needs to be freed)
+ * of the subject public key so we can convert the length from bits
+ * to bytes, which is what the digest function expects.
+ */
+ spk = cert->subjectPublicKeyInfo.subjectPublicKey;
+ DER_ConvertBitString(&spk);
+
+ return ocsp_DigestValue(arena, digestAlg, fill, &spk);
+}
+
+/*
+ * Digest the cert's subject name using the specified algorithm.
+ */
+SECItem *
+CERT_GetSubjectNameDigest(PLArenaPool *arena, const CERTCertificate *cert,
+ SECOidTag digestAlg, SECItem *fill)
+{
+ SECItem name;
+
+ /*
+ * Copy just the length and data pointer (nothing needs to be freed)
+ * of the subject name
+ */
+ name = cert->derSubject;
+
+ return ocsp_DigestValue(arena, digestAlg, fill, &name);
+}
+
+/*
+ * Create and fill-in a CertID. This function fills in the hash values
+ * (issuerNameHash and issuerKeyHash), and is hardwired to use SHA1.
+ * Someday it might need to be more flexible about hash algorithm, but
+ * for now we have no intention/need to create anything else.
+ *
+ * Error causes a null to be returned; most likely cause is trouble
+ * finding the certificate issuer (SEC_ERROR_UNKNOWN_ISSUER).
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ */
+static CERTOCSPCertID *
+ocsp_CreateCertID(PLArenaPool *arena, CERTCertificate *cert, PRTime time)
+{
+ CERTOCSPCertID *certID;
+ CERTCertificate *issuerCert = NULL;
+ void *mark = PORT_ArenaMark(arena);
+ SECStatus rv;
+
+ PORT_Assert(arena != NULL);
+
+ certID = PORT_ArenaZNew(arena, CERTOCSPCertID);
+ if (certID == NULL) {
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(arena, &certID->hashAlgorithm, SEC_OID_SHA1,
+ NULL);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ issuerCert = CERT_FindCertIssuer(cert, time, certUsageAnyCA);
+ if (issuerCert == NULL) {
+ goto loser;
+ }
+
+ if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_SHA1,
+ &(certID->issuerNameHash)) == NULL) {
+ goto loser;
+ }
+ certID->issuerSHA1NameHash.data = certID->issuerNameHash.data;
+ certID->issuerSHA1NameHash.len = certID->issuerNameHash.len;
+
+ if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD5,
+ &(certID->issuerMD5NameHash)) == NULL) {
+ goto loser;
+ }
+
+ if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD2,
+ &(certID->issuerMD2NameHash)) == NULL) {
+ goto loser;
+ }
+
+ if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_SHA1,
+ &certID->issuerKeyHash) == NULL) {
+ goto loser;
+ }
+ certID->issuerSHA1KeyHash.data = certID->issuerKeyHash.data;
+ certID->issuerSHA1KeyHash.len = certID->issuerKeyHash.len;
+ /* cache the other two hash algorithms as well */
+ if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD5,
+ &certID->issuerMD5KeyHash) == NULL) {
+ goto loser;
+ }
+ if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD2,
+ &certID->issuerMD2KeyHash) == NULL) {
+ goto loser;
+ }
+
+ /* now we are done with issuerCert */
+ CERT_DestroyCertificate(issuerCert);
+ issuerCert = NULL;
+
+ rv = SECITEM_CopyItem(arena, &certID->serialNumber, &cert->serialNumber);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(arena, mark);
+ return certID;
+
+loser:
+ if (issuerCert != NULL) {
+ CERT_DestroyCertificate(issuerCert);
+ }
+ PORT_ArenaRelease(arena, mark);
+ return NULL;
+}
+
+CERTOCSPCertID *
+CERT_CreateOCSPCertID(CERTCertificate *cert, PRTime time)
+{
+ PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ CERTOCSPCertID *certID;
+ PORT_Assert(arena != NULL);
+ if (!arena)
+ return NULL;
+
+ certID = ocsp_CreateCertID(arena, cert, time);
+ if (!certID) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+ certID->poolp = arena;
+ return certID;
+}
+
+static CERTOCSPCertID *
+cert_DupOCSPCertID(const CERTOCSPCertID *src)
+{
+ CERTOCSPCertID *dest;
+ PLArenaPool *arena = NULL;
+
+ if (!src) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena)
+ goto loser;
+
+ dest = PORT_ArenaZNew(arena, CERTOCSPCertID);
+ if (!dest)
+ goto loser;
+
+#define DUPHELP(element) \
+ if (src->element.data && \
+ SECITEM_CopyItem(arena, &dest->element, &src->element) != \
+ SECSuccess) { \
+ goto loser; \
+ }
+
+ DUPHELP(hashAlgorithm.algorithm)
+ DUPHELP(hashAlgorithm.parameters)
+ DUPHELP(issuerNameHash)
+ DUPHELP(issuerKeyHash)
+ DUPHELP(serialNumber)
+ DUPHELP(issuerSHA1NameHash)
+ DUPHELP(issuerMD5NameHash)
+ DUPHELP(issuerMD2NameHash)
+ DUPHELP(issuerSHA1KeyHash)
+ DUPHELP(issuerMD5KeyHash)
+ DUPHELP(issuerMD2KeyHash)
+
+ dest->poolp = arena;
+ return dest;
+
+loser:
+ if (arena)
+ PORT_FreeArena(arena, PR_FALSE);
+ PORT_SetError(PR_OUT_OF_MEMORY_ERROR);
+ return NULL;
+}
+
+/*
+ * Callback to set Extensions in request object
+ */
+void
+SetSingleReqExts(void *object, CERTCertExtension **exts)
+{
+ ocspSingleRequest *singleRequest =
+ (ocspSingleRequest *)object;
+
+ singleRequest->singleRequestExtensions = exts;
+}
+
+/*
+ * Add the Service Locator extension to the singleRequestExtensions
+ * for the given singleRequest.
+ *
+ * All errors are internal or low-level problems (e.g. no memory).
+ */
+static SECStatus
+ocsp_AddServiceLocatorExtension(ocspSingleRequest *singleRequest,
+ CERTCertificate *cert)
+{
+ ocspServiceLocator *serviceLocator = NULL;
+ void *extensionHandle = NULL;
+ SECStatus rv = SECFailure;
+
+ serviceLocator = PORT_ZNew(ocspServiceLocator);
+ if (serviceLocator == NULL)
+ goto loser;
+
+ /*
+ * Normally it would be a bad idea to do a direct reference like
+ * this rather than allocate and copy the name *or* at least dup
+ * a reference of the cert. But all we need is to be able to read
+ * the issuer name during the encoding we are about to do, so a
+ * copy is just a waste of time.
+ */
+ serviceLocator->issuer = &cert->issuer;
+
+ rv = CERT_FindCertExtension(cert, SEC_OID_X509_AUTH_INFO_ACCESS,
+ &serviceLocator->locator);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND)
+ goto loser;
+ }
+
+ /* prepare for following loser gotos */
+ rv = SECFailure;
+ PORT_SetError(0);
+
+ extensionHandle = cert_StartExtensions(singleRequest,
+ singleRequest->arena, SetSingleReqExts);
+ if (extensionHandle == NULL)
+ goto loser;
+
+ rv = CERT_EncodeAndAddExtension(extensionHandle,
+ SEC_OID_PKIX_OCSP_SERVICE_LOCATOR,
+ serviceLocator, PR_FALSE,
+ ocsp_ServiceLocatorTemplate);
+
+loser:
+ if (extensionHandle != NULL) {
+ /*
+ * Either way we have to finish out the extension context (so it gets
+ * freed). But careful not to override any already-set bad status.
+ */
+ SECStatus tmprv = CERT_FinishExtensions(extensionHandle);
+ if (rv == SECSuccess)
+ rv = tmprv;
+ }
+
+ /*
+ * Finally, free the serviceLocator structure itself and we are done.
+ */
+ if (serviceLocator != NULL) {
+ if (serviceLocator->locator.data != NULL)
+ SECITEM_FreeItem(&serviceLocator->locator, PR_FALSE);
+ PORT_Free(serviceLocator);
+ }
+
+ return rv;
+}
+
+/*
+ * Creates an array of ocspSingleRequest based on a list of certs.
+ * Note that the code which later compares the request list with the
+ * response expects this array to be in the exact same order as the
+ * certs are found in the list. It would be harder to change that
+ * order than preserve it, but since the requirement is not obvious,
+ * it deserves to be mentioned.
+ *
+ * Any problem causes a null return and error set:
+ * SEC_ERROR_UNKNOWN_ISSUER
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ */
+static ocspSingleRequest **
+ocsp_CreateSingleRequestList(PLArenaPool *arena, CERTCertList *certList,
+ PRTime time, PRBool includeLocator)
+{
+ ocspSingleRequest **requestList = NULL;
+ CERTCertListNode *node = NULL;
+ int i, count;
+ void *mark = PORT_ArenaMark(arena);
+
+ node = CERT_LIST_HEAD(certList);
+ for (count = 0; !CERT_LIST_END(node, certList); count++) {
+ node = CERT_LIST_NEXT(node);
+ }
+
+ if (count == 0)
+ goto loser;
+
+ requestList = PORT_ArenaNewArray(arena, ocspSingleRequest *, count + 1);
+ if (requestList == NULL)
+ goto loser;
+
+ node = CERT_LIST_HEAD(certList);
+ for (i = 0; !CERT_LIST_END(node, certList); i++) {
+ requestList[i] = PORT_ArenaZNew(arena, ocspSingleRequest);
+ if (requestList[i] == NULL)
+ goto loser;
+
+ OCSP_TRACE(("OCSP CERT_CreateOCSPRequest %s\n", node->cert->subjectName));
+ requestList[i]->arena = arena;
+ requestList[i]->reqCert = ocsp_CreateCertID(arena, node->cert, time);
+ if (requestList[i]->reqCert == NULL)
+ goto loser;
+
+ if (includeLocator == PR_TRUE) {
+ SECStatus rv;
+
+ rv = ocsp_AddServiceLocatorExtension(requestList[i], node->cert);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ node = CERT_LIST_NEXT(node);
+ }
+
+ PORT_Assert(i == count);
+
+ PORT_ArenaUnmark(arena, mark);
+ requestList[i] = NULL;
+ return requestList;
+
+loser:
+ PORT_ArenaRelease(arena, mark);
+ return NULL;
+}
+
+static ocspSingleRequest **
+ocsp_CreateRequestFromCert(PLArenaPool *arena,
+ CERTOCSPCertID *certID,
+ CERTCertificate *singleCert,
+ PRTime time,
+ PRBool includeLocator)
+{
+ ocspSingleRequest **requestList = NULL;
+ void *mark = PORT_ArenaMark(arena);
+ PORT_Assert(certID != NULL && singleCert != NULL);
+
+ /* meaning of value 2: one entry + one end marker */
+ requestList = PORT_ArenaNewArray(arena, ocspSingleRequest *, 2);
+ if (requestList == NULL)
+ goto loser;
+ requestList[0] = PORT_ArenaZNew(arena, ocspSingleRequest);
+ if (requestList[0] == NULL)
+ goto loser;
+ requestList[0]->arena = arena;
+ /* certID will live longer than the request */
+ requestList[0]->reqCert = certID;
+
+ if (includeLocator == PR_TRUE) {
+ SECStatus rv;
+ rv = ocsp_AddServiceLocatorExtension(requestList[0], singleCert);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(arena, mark);
+ requestList[1] = NULL;
+ return requestList;
+
+loser:
+ PORT_ArenaRelease(arena, mark);
+ return NULL;
+}
+
+static CERTOCSPRequest *
+ocsp_prepareEmptyOCSPRequest(void)
+{
+ PLArenaPool *arena = NULL;
+ CERTOCSPRequest *request = NULL;
+ ocspTBSRequest *tbsRequest = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+ request = PORT_ArenaZNew(arena, CERTOCSPRequest);
+ if (request == NULL) {
+ goto loser;
+ }
+ request->arena = arena;
+
+ tbsRequest = PORT_ArenaZNew(arena, ocspTBSRequest);
+ if (tbsRequest == NULL) {
+ goto loser;
+ }
+ request->tbsRequest = tbsRequest;
+ /* version 1 is the default, so we need not fill in a version number */
+ return request;
+
+loser:
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+CERTOCSPRequest *
+cert_CreateSingleCertOCSPRequest(CERTOCSPCertID *certID,
+ CERTCertificate *singleCert,
+ PRTime time,
+ PRBool addServiceLocator,
+ CERTCertificate *signerCert)
+{
+ CERTOCSPRequest *request;
+ OCSP_TRACE(("OCSP cert_CreateSingleCertOCSPRequest %s\n", singleCert->subjectName));
+
+ /* XXX Support for signerCert may be implemented later,
+ * see also the comment in CERT_CreateOCSPRequest.
+ */
+ if (signerCert != NULL) {
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return NULL;
+ }
+
+ request = ocsp_prepareEmptyOCSPRequest();
+ if (!request)
+ return NULL;
+ /*
+ * Version 1 is the default, so we need not fill in a version number.
+ * Now create the list of single requests, one for each cert.
+ */
+ request->tbsRequest->requestList =
+ ocsp_CreateRequestFromCert(request->arena,
+ certID,
+ singleCert,
+ time,
+ addServiceLocator);
+ if (request->tbsRequest->requestList == NULL) {
+ PORT_FreeArena(request->arena, PR_FALSE);
+ return NULL;
+ }
+ return request;
+}
+
+/*
+ * FUNCTION: CERT_CreateOCSPRequest
+ * Creates a CERTOCSPRequest, requesting the status of the certs in
+ * the given list.
+ * INPUTS:
+ * CERTCertList *certList
+ * A list of certs for which status will be requested.
+ * Note that all of these certificates should have the same issuer,
+ * or it's expected the response will be signed by a trusted responder.
+ * If the certs need to be broken up into multiple requests, that
+ * must be handled by the caller (and thus by having multiple calls
+ * to this routine), who knows about where the request(s) are being
+ * sent and whether there are any trusted responders in place.
+ * PRTime time
+ * Indicates the time for which the certificate status is to be
+ * determined -- this may be used in the search for the cert's issuer
+ * but has no effect on the request itself.
+ * PRBool addServiceLocator
+ * If true, the Service Locator extension should be added to the
+ * single request(s) for each cert.
+ * CERTCertificate *signerCert
+ * If non-NULL, means sign the request using this cert. Otherwise,
+ * do not sign.
+ * XXX note that request signing is not yet supported; see comment in code
+ * RETURN:
+ * A pointer to a CERTOCSPRequest structure containing an OCSP request
+ * for the cert list. On error, null is returned, with an error set
+ * indicating the reason. This is likely SEC_ERROR_UNKNOWN_ISSUER.
+ * (The issuer is needed to create a request for the certificate.)
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ */
+CERTOCSPRequest *
+CERT_CreateOCSPRequest(CERTCertList *certList, PRTime time,
+ PRBool addServiceLocator,
+ CERTCertificate *signerCert)
+{
+ CERTOCSPRequest *request = NULL;
+
+ if (!certList) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ /*
+ * XXX When we are prepared to put signing of requests back in,
+ * we will need to allocate a signature
+ * structure for the request, fill in the "derCerts" field in it,
+ * save the signerCert there, as well as fill in the "requestorName"
+ * field of the tbsRequest.
+ */
+ if (signerCert != NULL) {
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return NULL;
+ }
+ request = ocsp_prepareEmptyOCSPRequest();
+ if (!request)
+ return NULL;
+ /*
+ * Now create the list of single requests, one for each cert.
+ */
+ request->tbsRequest->requestList =
+ ocsp_CreateSingleRequestList(request->arena,
+ certList,
+ time,
+ addServiceLocator);
+ if (request->tbsRequest->requestList == NULL) {
+ PORT_FreeArena(request->arena, PR_FALSE);
+ return NULL;
+ }
+ return request;
+}
+
+/*
+ * FUNCTION: CERT_AddOCSPAcceptableResponses
+ * Add the AcceptableResponses extension to an OCSP Request.
+ * INPUTS:
+ * CERTOCSPRequest *request
+ * The request to which the extension should be added.
+ * ...
+ * A list (of one or more) of SECOidTag -- each of the response types
+ * to be added. The last OID *must* be SEC_OID_PKIX_OCSP_BASIC_RESPONSE.
+ * (This marks the end of the list, and it must be specified because a
+ * client conforming to the OCSP standard is required to handle the basic
+ * response type.) The OIDs are not checked in any way.
+ * RETURN:
+ * SECSuccess if the extension is added; SECFailure if anything goes wrong.
+ * All errors are internal or low-level problems (e.g. no memory).
+ */
+
+void
+SetRequestExts(void *object, CERTCertExtension **exts)
+{
+ CERTOCSPRequest *request = (CERTOCSPRequest *)object;
+
+ request->tbsRequest->requestExtensions = exts;
+}
+
+#if defined(__GNUC__) && !defined(NSS_NO_GCC48)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wvarargs"
+#endif
+SECStatus
+CERT_AddOCSPAcceptableResponses(CERTOCSPRequest *request,
+ SECOidTag responseType0, ...)
+{
+ void *extHandle;
+ va_list ap;
+ int i, count;
+ SECOidTag responseType;
+ SECOidData *responseOid;
+ SECItem **acceptableResponses = NULL;
+ SECStatus rv = SECFailure;
+
+ extHandle = request->tbsRequest->extensionHandle;
+ if (extHandle == NULL) {
+ extHandle = cert_StartExtensions(request, request->arena, SetRequestExts);
+ if (extHandle == NULL)
+ goto loser;
+ }
+
+ /* Count number of OIDS going into the extension value. */
+ count = 1;
+ if (responseType0 != SEC_OID_PKIX_OCSP_BASIC_RESPONSE) {
+ va_start(ap, responseType0);
+ do {
+ count++;
+ responseType = va_arg(ap, SECOidTag);
+ } while (responseType != SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
+ va_end(ap);
+ }
+
+ acceptableResponses = PORT_NewArray(SECItem *, count + 1);
+ if (acceptableResponses == NULL)
+ goto loser;
+
+ i = 0;
+ responseOid = SECOID_FindOIDByTag(responseType0);
+ acceptableResponses[i++] = &(responseOid->oid);
+ if (count > 1) {
+ va_start(ap, responseType0);
+ for (; i < count; i++) {
+ responseType = va_arg(ap, SECOidTag);
+ responseOid = SECOID_FindOIDByTag(responseType);
+ acceptableResponses[i] = &(responseOid->oid);
+ }
+ va_end(ap);
+ }
+ acceptableResponses[i] = NULL;
+
+ rv = CERT_EncodeAndAddExtension(extHandle, SEC_OID_PKIX_OCSP_RESPONSE,
+ &acceptableResponses, PR_FALSE,
+ SEC_ASN1_GET(SEC_SequenceOfObjectIDTemplate));
+ if (rv != SECSuccess)
+ goto loser;
+
+ PORT_Free(acceptableResponses);
+ if (request->tbsRequest->extensionHandle == NULL)
+ request->tbsRequest->extensionHandle = extHandle;
+ return SECSuccess;
+
+loser:
+ if (acceptableResponses != NULL)
+ PORT_Free(acceptableResponses);
+ if (extHandle != NULL)
+ (void)CERT_FinishExtensions(extHandle);
+ return rv;
+}
+#if defined(__GNUC__) && !defined(NSS_NO_GCC48)
+#pragma GCC diagnostic pop
+#endif
+
+/*
+ * FUNCTION: CERT_DestroyOCSPRequest
+ * Frees an OCSP Request structure.
+ * INPUTS:
+ * CERTOCSPRequest *request
+ * Pointer to CERTOCSPRequest to be freed.
+ * RETURN:
+ * No return value; no errors.
+ */
+void
+CERT_DestroyOCSPRequest(CERTOCSPRequest *request)
+{
+ if (request == NULL)
+ return;
+
+ if (request->tbsRequest != NULL) {
+ if (request->tbsRequest->requestorName != NULL)
+ CERT_DestroyGeneralNameList(request->tbsRequest->requestorName);
+ if (request->tbsRequest->extensionHandle != NULL)
+ (void)CERT_FinishExtensions(request->tbsRequest->extensionHandle);
+ }
+
+ if (request->optionalSignature != NULL) {
+ if (request->optionalSignature->cert != NULL)
+ CERT_DestroyCertificate(request->optionalSignature->cert);
+
+ /*
+ * XXX Need to free derCerts? Or do they come out of arena?
+ * (Currently we never fill in derCerts, which is why the
+ * answer is not obvious. Once we do, add any necessary code
+ * here and remove this comment.)
+ */
+ }
+
+ /*
+ * We should actually never have a request without an arena,
+ * but check just in case. (If there isn't one, there is not
+ * much we can do about it...)
+ */
+ PORT_Assert(request->arena != NULL);
+ if (request->arena != NULL)
+ PORT_FreeArena(request->arena, PR_FALSE);
+}
+
+/*
+ * RESPONSE SUPPORT FUNCTIONS (encode/create/decode/destroy):
+ */
+
+/*
+ * Helper function for encoding or decoding a ResponderID -- based on the
+ * given type, return the associated template for that choice.
+ */
+static const SEC_ASN1Template *
+ocsp_ResponderIDTemplateByType(CERTOCSPResponderIDType responderIDType)
+{
+ const SEC_ASN1Template *responderIDTemplate;
+
+ switch (responderIDType) {
+ case ocspResponderID_byName:
+ responderIDTemplate = ocsp_ResponderIDByNameTemplate;
+ break;
+ case ocspResponderID_byKey:
+ responderIDTemplate = ocsp_ResponderIDByKeyTemplate;
+ break;
+ case ocspResponderID_other:
+ default:
+ PORT_Assert(responderIDType == ocspResponderID_other);
+ responderIDTemplate = ocsp_ResponderIDOtherTemplate;
+ break;
+ }
+
+ return responderIDTemplate;
+}
+
+/*
+ * Helper function for encoding or decoding a CertStatus -- based on the
+ * given type, return the associated template for that choice.
+ */
+static const SEC_ASN1Template *
+ocsp_CertStatusTemplateByType(ocspCertStatusType certStatusType)
+{
+ const SEC_ASN1Template *certStatusTemplate;
+
+ switch (certStatusType) {
+ case ocspCertStatus_good:
+ certStatusTemplate = ocsp_CertStatusGoodTemplate;
+ break;
+ case ocspCertStatus_revoked:
+ certStatusTemplate = ocsp_CertStatusRevokedTemplate;
+ break;
+ case ocspCertStatus_unknown:
+ certStatusTemplate = ocsp_CertStatusUnknownTemplate;
+ break;
+ case ocspCertStatus_other:
+ default:
+ PORT_Assert(certStatusType == ocspCertStatus_other);
+ certStatusTemplate = ocsp_CertStatusOtherTemplate;
+ break;
+ }
+
+ return certStatusTemplate;
+}
+
+/*
+ * Helper function for decoding a certStatus -- turn the actual DER tag
+ * into our local translation.
+ */
+static ocspCertStatusType
+ocsp_CertStatusTypeByTag(int derTag)
+{
+ ocspCertStatusType certStatusType;
+
+ switch (derTag) {
+ case 0:
+ certStatusType = ocspCertStatus_good;
+ break;
+ case 1:
+ certStatusType = ocspCertStatus_revoked;
+ break;
+ case 2:
+ certStatusType = ocspCertStatus_unknown;
+ break;
+ default:
+ certStatusType = ocspCertStatus_other;
+ break;
+ }
+
+ return certStatusType;
+}
+
+/*
+ * Helper function for decoding SingleResponses -- they each contain
+ * a status which is encoded as CHOICE, which needs to be decoded "by hand".
+ *
+ * Note -- on error, this routine does not release the memory it may
+ * have allocated; it expects its caller to do that.
+ */
+static SECStatus
+ocsp_FinishDecodingSingleResponses(PLArenaPool *reqArena,
+ CERTOCSPSingleResponse **responses)
+{
+ ocspCertStatus *certStatus;
+ ocspCertStatusType certStatusType;
+ const SEC_ASN1Template *certStatusTemplate;
+ int derTag;
+ int i;
+ SECStatus rv = SECFailure;
+
+ if (!reqArena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (responses == NULL) /* nothing to do */
+ return SECSuccess;
+
+ for (i = 0; responses[i] != NULL; i++) {
+ SECItem *newStatus;
+ /*
+ * The following assert points out internal errors (problems in
+ * the template definitions or in the ASN.1 decoder itself, etc.).
+ */
+ PORT_Assert(responses[i]->derCertStatus.data != NULL);
+
+ derTag = responses[i]->derCertStatus.data[0] & SEC_ASN1_TAGNUM_MASK;
+ certStatusType = ocsp_CertStatusTypeByTag(derTag);
+ certStatusTemplate = ocsp_CertStatusTemplateByType(certStatusType);
+
+ certStatus = PORT_ArenaZAlloc(reqArena, sizeof(ocspCertStatus));
+ if (certStatus == NULL) {
+ goto loser;
+ }
+ newStatus = SECITEM_ArenaDupItem(reqArena, &responses[i]->derCertStatus);
+ if (!newStatus) {
+ goto loser;
+ }
+ rv = SEC_QuickDERDecodeItem(reqArena, certStatus, certStatusTemplate,
+ newStatus);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_BAD_DER)
+ PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+ goto loser;
+ }
+
+ certStatus->certStatusType = certStatusType;
+ responses[i]->certStatus = certStatus;
+ }
+
+ return SECSuccess;
+
+loser:
+ return rv;
+}
+
+/*
+ * Helper function for decoding a responderID -- turn the actual DER tag
+ * into our local translation.
+ */
+static CERTOCSPResponderIDType
+ocsp_ResponderIDTypeByTag(int derTag)
+{
+ CERTOCSPResponderIDType responderIDType;
+
+ switch (derTag) {
+ case 1:
+ responderIDType = ocspResponderID_byName;
+ break;
+ case 2:
+ responderIDType = ocspResponderID_byKey;
+ break;
+ default:
+ responderIDType = ocspResponderID_other;
+ break;
+ }
+
+ return responderIDType;
+}
+
+/*
+ * Decode "src" as a BasicOCSPResponse, returning the result.
+ */
+static ocspBasicOCSPResponse *
+ocsp_DecodeBasicOCSPResponse(PLArenaPool *arena, SECItem *src)
+{
+ void *mark;
+ ocspBasicOCSPResponse *basicResponse;
+ ocspResponseData *responseData;
+ ocspResponderID *responderID;
+ CERTOCSPResponderIDType responderIDType;
+ const SEC_ASN1Template *responderIDTemplate;
+ int derTag;
+ SECStatus rv;
+ SECItem newsrc;
+
+ mark = PORT_ArenaMark(arena);
+
+ basicResponse = PORT_ArenaZAlloc(arena, sizeof(ocspBasicOCSPResponse));
+ if (basicResponse == NULL) {
+ goto loser;
+ }
+
+ /* 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, &newsrc, src);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = SEC_QuickDERDecodeItem(arena, basicResponse,
+ ocsp_BasicOCSPResponseTemplate, &newsrc);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_BAD_DER)
+ PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+ goto loser;
+ }
+
+ responseData = basicResponse->tbsResponseData;
+
+ /*
+ * The following asserts point out internal errors (problems in
+ * the template definitions or in the ASN.1 decoder itself, etc.).
+ */
+ PORT_Assert(responseData != NULL);
+ PORT_Assert(responseData->derResponderID.data != NULL);
+
+ /*
+ * XXX Because responderID is a CHOICE, which is not currently handled
+ * by our ASN.1 decoder, we have to decode it "by hand".
+ */
+ derTag = responseData->derResponderID.data[0] & SEC_ASN1_TAGNUM_MASK;
+ responderIDType = ocsp_ResponderIDTypeByTag(derTag);
+ responderIDTemplate = ocsp_ResponderIDTemplateByType(responderIDType);
+
+ responderID = PORT_ArenaZAlloc(arena, sizeof(ocspResponderID));
+ if (responderID == NULL) {
+ goto loser;
+ }
+
+ rv = SEC_QuickDERDecodeItem(arena, responderID, responderIDTemplate,
+ &responseData->derResponderID);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_BAD_DER)
+ PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+ goto loser;
+ }
+
+ responderID->responderIDType = responderIDType;
+ responseData->responderID = responderID;
+
+ /*
+ * XXX Each SingleResponse also contains a CHOICE, which has to be
+ * fixed up by hand.
+ */
+ rv = ocsp_FinishDecodingSingleResponses(arena, responseData->responses);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(arena, mark);
+ return basicResponse;
+
+loser:
+ PORT_ArenaRelease(arena, mark);
+ return NULL;
+}
+
+/*
+ * Decode the responseBytes based on the responseType found in "rbytes",
+ * leaving the resulting translated/decoded information in there as well.
+ */
+static SECStatus
+ocsp_DecodeResponseBytes(PLArenaPool *arena, ocspResponseBytes *rbytes)
+{
+ if (rbytes == NULL) {
+ PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE);
+ return SECFailure;
+ }
+
+ rbytes->responseTypeTag = SECOID_FindOIDTag(&rbytes->responseType);
+ switch (rbytes->responseTypeTag) {
+ case SEC_OID_PKIX_OCSP_BASIC_RESPONSE: {
+ ocspBasicOCSPResponse *basicResponse;
+
+ basicResponse = ocsp_DecodeBasicOCSPResponse(arena,
+ &rbytes->response);
+ if (basicResponse == NULL)
+ return SECFailure;
+
+ rbytes->decodedResponse.basic = basicResponse;
+ } break;
+
+ /*
+ * Add new/future response types here.
+ */
+
+ default:
+ PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * FUNCTION: CERT_DecodeOCSPResponse
+ * Decode a DER encoded OCSP Response.
+ * INPUTS:
+ * SECItem *src
+ * Pointer to a SECItem holding DER encoded OCSP Response.
+ * RETURN:
+ * Returns a pointer to a CERTOCSPResponse (the decoded OCSP Response);
+ * the caller is responsible for destroying it. Or NULL if error (either
+ * response could not be decoded (SEC_ERROR_OCSP_MALFORMED_RESPONSE),
+ * it was of an unexpected type (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE),
+ * or a low-level or internal error occurred).
+ */
+CERTOCSPResponse *
+CERT_DecodeOCSPResponse(const SECItem *src)
+{
+ PLArenaPool *arena = NULL;
+ CERTOCSPResponse *response = NULL;
+ SECStatus rv = SECFailure;
+ ocspResponseStatus sv;
+ SECItem newSrc;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+ response = (CERTOCSPResponse *)PORT_ArenaZAlloc(arena,
+ sizeof(CERTOCSPResponse));
+ if (response == NULL) {
+ goto loser;
+ }
+ response->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, &newSrc, src);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = SEC_QuickDERDecodeItem(arena, response, ocsp_OCSPResponseTemplate, &newSrc);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_BAD_DER)
+ PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+ goto loser;
+ }
+
+ sv = (ocspResponseStatus)DER_GetInteger(&response->responseStatus);
+ response->statusValue = sv;
+ if (sv != ocspResponse_successful) {
+ /*
+ * If the response status is anything but successful, then we
+ * are all done with decoding; the status is all there is.
+ */
+ return response;
+ }
+
+ /*
+ * A successful response contains much more information, still encoded.
+ * Now we need to decode that.
+ */
+ rv = ocsp_DecodeResponseBytes(arena, response->responseBytes);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ return response;
+
+loser:
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+/*
+ * The way an OCSPResponse is defined, there are many levels to descend
+ * before getting to the actual response information. And along the way
+ * we need to check that the response *type* is recognizable, which for
+ * now means that it is a BasicOCSPResponse, because that is the only
+ * type currently defined. Rather than force all routines to perform
+ * a bunch of sanity checking every time they want to work on a response,
+ * this function isolates that and gives back the interesting part.
+ * Note that no copying is done, this just returns a pointer into the
+ * substructure of the response which is passed in.
+ *
+ * XXX This routine only works when a valid response structure is passed
+ * into it; this is checked with many assertions. Assuming the response
+ * was creating by decoding, it wouldn't make it this far without being
+ * okay. That is a sufficient assumption since the entire OCSP interface
+ * is only used internally. When this interface is officially exported,
+ * each assertion below will need to be followed-up with setting an error
+ * and returning (null).
+ *
+ * FUNCTION: ocsp_GetResponseData
+ * Returns ocspResponseData structure and a pointer to tbs response
+ * data DER from a valid ocsp response.
+ * INPUTS:
+ * CERTOCSPResponse *response
+ * structure of a valid ocsp response
+ * RETURN:
+ * Returns a pointer to ocspResponseData structure: decoded OCSP response
+ * data, and a pointer(tbsResponseDataDER) to its undecoded data DER.
+ */
+ocspResponseData *
+ocsp_GetResponseData(CERTOCSPResponse *response, SECItem **tbsResponseDataDER)
+{
+ ocspBasicOCSPResponse *basic;
+ ocspResponseData *responseData;
+
+ PORT_Assert(response != NULL);
+
+ PORT_Assert(response->responseBytes != NULL);
+
+ PORT_Assert(response->responseBytes->responseTypeTag ==
+ SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
+
+ basic = response->responseBytes->decodedResponse.basic;
+ PORT_Assert(basic != NULL);
+
+ responseData = basic->tbsResponseData;
+ PORT_Assert(responseData != NULL);
+
+ if (tbsResponseDataDER) {
+ *tbsResponseDataDER = &basic->tbsResponseDataDER;
+
+ PORT_Assert((*tbsResponseDataDER)->data != NULL);
+ PORT_Assert((*tbsResponseDataDER)->len != 0);
+ }
+
+ return responseData;
+}
+
+/*
+ * Much like the routine above, except it returns the response signature.
+ * Again, no copy is done.
+ */
+ocspSignature *
+ocsp_GetResponseSignature(CERTOCSPResponse *response)
+{
+ ocspBasicOCSPResponse *basic;
+
+ PORT_Assert(response != NULL);
+ if (NULL == response->responseBytes) {
+ return NULL;
+ }
+ if (response->responseBytes->responseTypeTag !=
+ SEC_OID_PKIX_OCSP_BASIC_RESPONSE) {
+ return NULL;
+ }
+ basic = response->responseBytes->decodedResponse.basic;
+ PORT_Assert(basic != NULL);
+
+ return &(basic->responseSignature);
+}
+
+/*
+ * FUNCTION: CERT_DestroyOCSPResponse
+ * Frees an OCSP Response structure.
+ * INPUTS:
+ * CERTOCSPResponse *request
+ * Pointer to CERTOCSPResponse to be freed.
+ * RETURN:
+ * No return value; no errors.
+ */
+void
+CERT_DestroyOCSPResponse(CERTOCSPResponse *response)
+{
+ if (response != NULL) {
+ ocspSignature *signature = ocsp_GetResponseSignature(response);
+ if (signature && signature->cert != NULL)
+ CERT_DestroyCertificate(signature->cert);
+
+ /*
+ * We should actually never have a response without an arena,
+ * but check just in case. (If there isn't one, there is not
+ * much we can do about it...)
+ */
+ PORT_Assert(response->arena != NULL);
+ if (response->arena != NULL) {
+ PORT_FreeArena(response->arena, PR_FALSE);
+ }
+ }
+}
+
+/*
+ * OVERALL OCSP CLIENT SUPPORT (make and send a request, verify a response):
+ */
+
+/*
+ * Pick apart a URL, saving the important things in the passed-in pointers.
+ *
+ * We expect to find "http://<hostname>[:<port>]/[path]", though we will
+ * tolerate that final slash character missing, as well as beginning and
+ * trailing whitespace, and any-case-characters for "http". All of that
+ * tolerance is what complicates this routine. What we want is just to
+ * pick out the hostname, the port, and the path.
+ *
+ * On a successful return, the caller will need to free the output pieces
+ * of hostname and path, which are copies of the values found in the url.
+ */
+static SECStatus
+ocsp_ParseURL(const char *url, char **pHostname, PRUint16 *pPort, char **pPath)
+{
+ unsigned short port = 80; /* default, in case not in url */
+ char *hostname = NULL;
+ char *path = NULL;
+ const char *save;
+ char c;
+ int len;
+
+ if (url == NULL)
+ goto loser;
+
+ /*
+ * Skip beginning whitespace.
+ */
+ c = *url;
+ while ((c == ' ' || c == '\t') && c != '\0') {
+ url++;
+ c = *url;
+ }
+ if (c == '\0')
+ goto loser;
+
+ /*
+ * Confirm, then skip, protocol. (Since we only know how to do http,
+ * that is all we will accept).
+ */
+ if (PORT_Strncasecmp(url, "http://", 7) != 0)
+ goto loser;
+ url += 7;
+
+ /*
+ * Whatever comes next is the hostname (or host IP address). We just
+ * save it aside and then search for its end so we can determine its
+ * length and copy it.
+ *
+ * XXX Note that because we treat a ':' as a terminator character
+ * (and below, we expect that to mean there is a port specification
+ * immediately following), we will not handle IPv6 addresses. That is
+ * apparently an acceptable limitation, for the time being. Some day,
+ * when there is a clear way to specify a URL with an IPv6 address that
+ * can be parsed unambiguously, this code should be made to do that.
+ */
+ save = url;
+ c = *url;
+ while (c != '/' && c != ':' && c != '\0' && c != ' ' && c != '\t') {
+ url++;
+ c = *url;
+ }
+ len = url - save;
+ hostname = PORT_Alloc(len + 1);
+ if (hostname == NULL)
+ goto loser;
+ PORT_Memcpy(hostname, save, len);
+ hostname[len] = '\0';
+
+ /*
+ * Now we figure out if there was a port specified or not.
+ * If so, we need to parse it (as a number) and skip it.
+ */
+ if (c == ':') {
+ url++;
+ port = (unsigned short)PORT_Atoi(url);
+ c = *url;
+ while (c != '/' && c != '\0' && c != ' ' && c != '\t') {
+ if (c < '0' || c > '9')
+ goto loser;
+ url++;
+ c = *url;
+ }
+ }
+
+ /*
+ * Last thing to find is a path. There *should* be a slash,
+ * if nothing else -- but if there is not we provide one.
+ */
+ if (c == '/') {
+ save = url;
+ while (c != '\0' && c != ' ' && c != '\t') {
+ url++;
+ c = *url;
+ }
+ len = url - save;
+ path = PORT_Alloc(len + 1);
+ if (path == NULL)
+ goto loser;
+ PORT_Memcpy(path, save, len);
+ path[len] = '\0';
+ } else {
+ path = PORT_Strdup("/");
+ if (path == NULL)
+ goto loser;
+ }
+
+ *pHostname = hostname;
+ *pPort = port;
+ *pPath = path;
+ return SECSuccess;
+
+loser:
+ if (hostname != NULL)
+ PORT_Free(hostname);
+ PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ return SECFailure;
+}
+
+/*
+ * Open a socket to the specified host on the specified port, and return it.
+ * The host is either a hostname or an IP address.
+ */
+static PRFileDesc *
+ocsp_ConnectToHost(const char *host, PRUint16 port)
+{
+ PRFileDesc *sock = NULL;
+ PRIntervalTime timeout;
+ PRNetAddr addr;
+ char *netdbbuf = NULL;
+
+ sock = PR_NewTCPSocket();
+ if (sock == NULL)
+ goto loser;
+
+ /* XXX Some day need a way to set (and get?) the following value */
+ timeout = PR_SecondsToInterval(30);
+
+ /*
+ * If the following converts an IP address string in "dot notation"
+ * into a PRNetAddr. If it fails, we assume that is because we do not
+ * have such an address, but instead a host *name*. In that case we
+ * then lookup the host by name. Using the NSPR function this way
+ * means we do not have to have our own logic for distinguishing a
+ * valid numerical IP address from a hostname.
+ */
+ if (PR_StringToNetAddr(host, &addr) != PR_SUCCESS) {
+ PRIntn hostIndex;
+ PRHostEnt hostEntry;
+
+ netdbbuf = PORT_Alloc(PR_NETDB_BUF_SIZE);
+ if (netdbbuf == NULL)
+ goto loser;
+
+ if (PR_GetHostByName(host, netdbbuf, PR_NETDB_BUF_SIZE,
+ &hostEntry) != PR_SUCCESS)
+ goto loser;
+
+ hostIndex = 0;
+ do {
+ hostIndex = PR_EnumerateHostEnt(hostIndex, &hostEntry, port, &addr);
+ if (hostIndex <= 0)
+ goto loser;
+ } while (PR_Connect(sock, &addr, timeout) != PR_SUCCESS);
+
+ PORT_Free(netdbbuf);
+ } else {
+ /*
+ * First put the port into the address, then connect.
+ */
+ if (PR_InitializeNetAddr(PR_IpAddrNull, port, &addr) != PR_SUCCESS)
+ goto loser;
+ if (PR_Connect(sock, &addr, timeout) != PR_SUCCESS)
+ goto loser;
+ }
+
+ return sock;
+
+loser:
+ if (sock != NULL)
+ PR_Close(sock);
+ if (netdbbuf != NULL)
+ PORT_Free(netdbbuf);
+ return NULL;
+}
+
+/*
+ * Sends an encoded OCSP request to the server identified by "location",
+ * and returns the socket on which it was sent (so can listen for the reply).
+ * "location" is expected to be a valid URL -- an error parsing it produces
+ * SEC_ERROR_CERT_BAD_ACCESS_LOCATION. Other errors are likely problems
+ * connecting to it, or writing to it, or allocating memory, and the low-level
+ * errors appropriate to the problem will be set.
+ * if (encodedRequest == NULL)
+ * then location MUST already include the full request,
+ * including base64 and urlencode,
+ * and the request will be sent with GET
+ * if (encodedRequest != NULL)
+ * then the request will be sent with POST
+ */
+static PRFileDesc *
+ocsp_SendEncodedRequest(const char *location, const SECItem *encodedRequest)
+{
+ char *hostname = NULL;
+ char *path = NULL;
+ PRUint16 port;
+ SECStatus rv;
+ PRFileDesc *sock = NULL;
+ PRFileDesc *returnSock = NULL;
+ char *header = NULL;
+ char portstr[16];
+
+ /*
+ * Take apart the location, getting the hostname, port, and path.
+ */
+ rv = ocsp_ParseURL(location, &hostname, &port, &path);
+ if (rv != SECSuccess)
+ goto loser;
+
+ PORT_Assert(hostname != NULL);
+ PORT_Assert(path != NULL);
+
+ sock = ocsp_ConnectToHost(hostname, port);
+ if (sock == NULL)
+ goto loser;
+
+ portstr[0] = '\0';
+ if (port != 80) {
+ PR_snprintf(portstr, sizeof(portstr), ":%d", port);
+ }
+
+ if (!encodedRequest) {
+ header = PR_smprintf("GET %s HTTP/1.0\r\n"
+ "Host: %s%s\r\n\r\n",
+ path, hostname, portstr);
+ if (header == NULL)
+ goto loser;
+
+ /*
+ * The NSPR documentation promises that if it can, it will write the full
+ * amount; this will not return a partial value expecting us to loop.
+ */
+ if (PR_Write(sock, header, (PRInt32)PORT_Strlen(header)) < 0)
+ goto loser;
+ } else {
+ header = PR_smprintf("POST %s HTTP/1.0\r\n"
+ "Host: %s%s\r\n"
+ "Content-Type: application/ocsp-request\r\n"
+ "Content-Length: %u\r\n\r\n",
+ path, hostname, portstr, encodedRequest->len);
+ if (header == NULL)
+ goto loser;
+
+ /*
+ * The NSPR documentation promises that if it can, it will write the full
+ * amount; this will not return a partial value expecting us to loop.
+ */
+ if (PR_Write(sock, header, (PRInt32)PORT_Strlen(header)) < 0)
+ goto loser;
+
+ if (PR_Write(sock, encodedRequest->data,
+ (PRInt32)encodedRequest->len) < 0)
+ goto loser;
+ }
+
+ returnSock = sock;
+ sock = NULL;
+
+loser:
+ if (header != NULL)
+ PORT_Free(header);
+ if (sock != NULL)
+ PR_Close(sock);
+ if (path != NULL)
+ PORT_Free(path);
+ if (hostname != NULL)
+ PORT_Free(hostname);
+
+ return returnSock;
+}
+
+/*
+ * Read from "fd" into "buf" -- expect/attempt to read a given number of bytes
+ * Obviously, stop if hit end-of-stream. Timeout is passed in.
+ */
+
+static int
+ocsp_read(PRFileDesc *fd, char *buf, int toread, PRIntervalTime timeout)
+{
+ int total = 0;
+
+ while (total < toread) {
+ PRInt32 got;
+
+ got = PR_Recv(fd, buf + total, (PRInt32)(toread - total), 0, timeout);
+ if (got < 0) {
+ if (0 == total) {
+ total = -1; /* report the error if we didn't read anything yet */
+ }
+ break;
+ } else if (got == 0) { /* EOS */
+ break;
+ }
+
+ total += got;
+ }
+
+ return total;
+}
+
+#define OCSP_BUFSIZE 1024
+
+#define AbortHttpDecode(error) \
+ { \
+ if (inBuffer) \
+ PORT_Free(inBuffer); \
+ PORT_SetError(error); \
+ return NULL; \
+ }
+
+/*
+ * Reads on the given socket and returns an encoded response when received.
+ * Properly formatted HTTP/1.0 response headers are expected to be read
+ * from the socket, preceding a binary-encoded OCSP response. Problems
+ * with parsing cause the error SEC_ERROR_OCSP_BAD_HTTP_RESPONSE to be
+ * set; any other problems are likely low-level i/o or memory allocation
+ * errors.
+ */
+static SECItem *
+ocsp_GetEncodedResponse(PLArenaPool *arena, PRFileDesc *sock)
+{
+ /* first read HTTP status line and headers */
+
+ char *inBuffer = NULL;
+ PRInt32 offset = 0;
+ PRInt32 inBufsize = 0;
+ const PRInt32 bufSizeIncrement = OCSP_BUFSIZE; /* 1 KB at a time */
+ const PRInt32 maxBufSize = 8 * bufSizeIncrement; /* 8 KB max */
+ const char *CRLF = "\r\n";
+ const PRInt32 CRLFlen = strlen(CRLF);
+ const char *headerEndMark = "\r\n\r\n";
+ const PRInt32 markLen = strlen(headerEndMark);
+ const PRIntervalTime ocsptimeout =
+ PR_SecondsToInterval(30); /* hardcoded to 30s for now */
+ char *headerEnd = NULL;
+ PRBool EOS = PR_FALSE;
+ const char *httpprotocol = "HTTP/";
+ const PRInt32 httplen = strlen(httpprotocol);
+ const char *httpcode = NULL;
+ const char *contenttype = NULL;
+ PRInt32 contentlength = 0;
+ PRInt32 bytesRead = 0;
+ char *statusLineEnd = NULL;
+ char *space = NULL;
+ char *nextHeader = NULL;
+ SECItem *result = NULL;
+
+ /* read up to at least the end of the HTTP headers */
+ do {
+ inBufsize += bufSizeIncrement;
+ inBuffer = PORT_Realloc(inBuffer, inBufsize + 1);
+ if (NULL == inBuffer) {
+ AbortHttpDecode(SEC_ERROR_NO_MEMORY);
+ }
+ bytesRead = ocsp_read(sock, inBuffer + offset, bufSizeIncrement,
+ ocsptimeout);
+ if (bytesRead > 0) {
+ PRInt32 searchOffset = (offset - markLen) > 0 ? offset - markLen : 0;
+ offset += bytesRead;
+ *(inBuffer + offset) = '\0'; /* NULL termination */
+ headerEnd = strstr((const char *)inBuffer + searchOffset, headerEndMark);
+ if (bytesRead < bufSizeIncrement) {
+ /* we read less data than requested, therefore we are at
+ EOS or there was a read error */
+ EOS = PR_TRUE;
+ }
+ } else {
+ /* recv error or EOS */
+ EOS = PR_TRUE;
+ }
+ } while ((!headerEnd) && (PR_FALSE == EOS) &&
+ (inBufsize < maxBufSize));
+
+ if (!headerEnd) {
+ AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+ }
+
+ /* parse the HTTP status line */
+ statusLineEnd = strstr((const char *)inBuffer, CRLF);
+ if (!statusLineEnd) {
+ AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+ }
+ *statusLineEnd = '\0';
+
+ /* check for HTTP/ response */
+ space = strchr((const char *)inBuffer, ' ');
+ if (!space || PORT_Strncasecmp((const char *)inBuffer, httpprotocol, httplen) != 0) {
+ AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+ }
+
+ /* check the HTTP status code of 200 */
+ httpcode = space + 1;
+ space = strchr(httpcode, ' ');
+ if (!space) {
+ AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+ }
+ *space = 0;
+ if (0 != strcmp(httpcode, "200")) {
+ AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+ }
+
+ /* parse the HTTP headers in the buffer . We only care about
+ content-type and content-length
+ */
+
+ nextHeader = statusLineEnd + CRLFlen;
+ *headerEnd = '\0'; /* terminate */
+ do {
+ char *thisHeaderEnd = NULL;
+ char *value = NULL;
+ char *colon = strchr(nextHeader, ':');
+
+ if (!colon) {
+ AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+ }
+
+ *colon = '\0';
+ value = colon + 1;
+
+ /* jpierre - note : the following code will only handle the basic form
+ of HTTP/1.0 response headers, of the form "name: value" . Headers
+ split among multiple lines are not supported. This is not common
+ and should not be an issue, but it could become one in the
+ future */
+
+ if (*value != ' ') {
+ AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+ }
+
+ value++;
+ thisHeaderEnd = strstr(value, CRLF);
+ if (thisHeaderEnd) {
+ *thisHeaderEnd = '\0';
+ }
+
+ if (0 == PORT_Strcasecmp(nextHeader, "content-type")) {
+ contenttype = value;
+ } else if (0 == PORT_Strcasecmp(nextHeader, "content-length")) {
+ contentlength = atoi(value);
+ }
+
+ if (thisHeaderEnd) {
+ nextHeader = thisHeaderEnd + CRLFlen;
+ } else {
+ nextHeader = NULL;
+ }
+
+ } while (nextHeader && (nextHeader < (headerEnd + CRLFlen)));
+
+ /* check content-type */
+ if (!contenttype ||
+ (0 != PORT_Strcasecmp(contenttype, "application/ocsp-response"))) {
+ AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+ }
+
+ /* read the body of the OCSP response */
+ offset = offset - (PRInt32)(headerEnd - (const char *)inBuffer) - markLen;
+ if (offset) {
+ /* move all data to the beginning of the buffer */
+ PORT_Memmove(inBuffer, headerEnd + markLen, offset);
+ }
+
+ /* resize buffer to only what's needed to hold the current response */
+ inBufsize = (1 + (offset - 1) / bufSizeIncrement) * bufSizeIncrement;
+
+ while ((PR_FALSE == EOS) &&
+ ((contentlength == 0) || (offset < contentlength)) &&
+ (inBufsize < maxBufSize)) {
+ /* we still need to receive more body data */
+ inBufsize += bufSizeIncrement;
+ inBuffer = PORT_Realloc(inBuffer, inBufsize + 1);
+ if (NULL == inBuffer) {
+ AbortHttpDecode(SEC_ERROR_NO_MEMORY);
+ }
+ bytesRead = ocsp_read(sock, inBuffer + offset, bufSizeIncrement,
+ ocsptimeout);
+ if (bytesRead > 0) {
+ offset += bytesRead;
+ if (bytesRead < bufSizeIncrement) {
+ /* we read less data than requested, therefore we are at
+ EOS or there was a read error */
+ EOS = PR_TRUE;
+ }
+ } else {
+ /* recv error or EOS */
+ EOS = PR_TRUE;
+ }
+ }
+
+ if (0 == offset) {
+ AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+ }
+
+ /*
+ * Now allocate the item to hold the data.
+ */
+ result = SECITEM_AllocItem(arena, NULL, offset);
+ if (NULL == result) {
+ AbortHttpDecode(SEC_ERROR_NO_MEMORY);
+ }
+
+ /*
+ * And copy the data left in the buffer.
+ */
+ PORT_Memcpy(result->data, inBuffer, offset);
+
+ /* and free the temporary buffer */
+ PORT_Free(inBuffer);
+ return result;
+}
+
+SECStatus
+CERT_ParseURL(const char *url, char **pHostname, PRUint16 *pPort, char **pPath)
+{
+ return ocsp_ParseURL(url, pHostname, pPort, pPath);
+}
+
+/*
+ * Limit the size of http responses we are willing to accept.
+ */
+#define MAX_WANTED_OCSP_RESPONSE_LEN 64 * 1024
+
+/* if (encodedRequest == NULL)
+ * then location MUST already include the full request,
+ * including base64 and urlencode,
+ * and the request will be sent with GET
+ * if (encodedRequest != NULL)
+ * then the request will be sent with POST
+ */
+static SECItem *
+fetchOcspHttpClientV1(PLArenaPool *arena,
+ const SEC_HttpClientFcnV1 *hcv1,
+ const char *location,
+ const SECItem *encodedRequest)
+{
+ char *hostname = NULL;
+ char *path = NULL;
+ PRUint16 port;
+ SECItem *encodedResponse = NULL;
+ SEC_HTTP_SERVER_SESSION pServerSession = NULL;
+ SEC_HTTP_REQUEST_SESSION pRequestSession = NULL;
+ PRUint16 myHttpResponseCode;
+ const char *myHttpResponseData;
+ PRUint32 myHttpResponseDataLen;
+
+ if (ocsp_ParseURL(location, &hostname, &port, &path) == SECFailure) {
+ PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST);
+ goto loser;
+ }
+
+ PORT_Assert(hostname != NULL);
+ PORT_Assert(path != NULL);
+
+ if ((*hcv1->createSessionFcn)(
+ hostname,
+ port,
+ &pServerSession) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR);
+ goto loser;
+ }
+
+ /* We use a non-zero timeout, which means:
+ - the client will use blocking I/O
+ - TryFcn will not return WOULD_BLOCK nor a poll descriptor
+ - it's sufficient to call TryFcn once
+ No lock for accessing OCSP_Global.timeoutSeconds, bug 406120
+ */
+
+ if ((*hcv1->createFcn)(
+ pServerSession,
+ "http",
+ path,
+ encodedRequest ? "POST" : "GET",
+ PR_TicksPerSecond() * OCSP_Global.timeoutSeconds,
+ &pRequestSession) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR);
+ goto loser;
+ }
+
+ if (encodedRequest &&
+ (*hcv1->setPostDataFcn)(
+ pRequestSession,
+ (char *)encodedRequest->data,
+ encodedRequest->len,
+ "application/ocsp-request") != SECSuccess) {
+ PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR);
+ goto loser;
+ }
+
+ /* we don't want result objects larger than this: */
+ myHttpResponseDataLen = MAX_WANTED_OCSP_RESPONSE_LEN;
+
+ OCSP_TRACE(("OCSP trySendAndReceive %s\n", location));
+
+ if ((*hcv1->trySendAndReceiveFcn)(
+ pRequestSession,
+ NULL,
+ &myHttpResponseCode,
+ NULL,
+ NULL,
+ &myHttpResponseData,
+ &myHttpResponseDataLen) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR);
+ goto loser;
+ }
+
+ OCSP_TRACE(("OCSP trySendAndReceive result http %d\n", myHttpResponseCode));
+
+ if (myHttpResponseCode != 200) {
+ PORT_SetError(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+ goto loser;
+ }
+
+ encodedResponse = SECITEM_AllocItem(arena, NULL, myHttpResponseDataLen);
+
+ if (!encodedResponse) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ PORT_Memcpy(encodedResponse->data, myHttpResponseData, myHttpResponseDataLen);
+
+loser:
+ if (pRequestSession != NULL)
+ (*hcv1->freeFcn)(pRequestSession);
+ if (pServerSession != NULL)
+ (*hcv1->freeSessionFcn)(pServerSession);
+ if (path != NULL)
+ PORT_Free(path);
+ if (hostname != NULL)
+ PORT_Free(hostname);
+
+ return encodedResponse;
+}
+
+/*
+ * FUNCTION: CERT_GetEncodedOCSPResponseByMethod
+ * Creates and sends a request to an OCSP responder, then reads and
+ * returns the (encoded) response.
+ * INPUTS:
+ * PLArenaPool *arena
+ * Pointer to arena from which return value will be allocated.
+ * If NULL, result will be allocated from the heap (and thus should
+ * be freed via SECITEM_FreeItem).
+ * CERTCertList *certList
+ * A list of certs for which status will be requested.
+ * Note that all of these certificates should have the same issuer,
+ * or it's expected the response will be signed by a trusted responder.
+ * If the certs need to be broken up into multiple requests, that
+ * must be handled by the caller (and thus by having multiple calls
+ * to this routine), who knows about where the request(s) are being
+ * sent and whether there are any trusted responders in place.
+ * const char *location
+ * The location of the OCSP responder (a URL).
+ * const char *method
+ * The protocol method used when retrieving the OCSP response.
+ * Currently support: "GET" (http GET) and "POST" (http POST).
+ * Additionals methods for http or other protocols might be added
+ * in the future.
+ * PRTime time
+ * Indicates the time for which the certificate status is to be
+ * determined -- this may be used in the search for the cert's issuer
+ * but has no other bearing on the operation.
+ * PRBool addServiceLocator
+ * If true, the Service Locator extension should be added to the
+ * single request(s) for each cert.
+ * CERTCertificate *signerCert
+ * If non-NULL, means sign the request using this cert. Otherwise,
+ * do not sign.
+ * void *pwArg
+ * Pointer to argument for password prompting, if needed. (Definitely
+ * not needed if not signing.)
+ * OUTPUTS:
+ * CERTOCSPRequest **pRequest
+ * Pointer in which to store the OCSP request created for the given
+ * list of certificates. It is only filled in if the entire operation
+ * is successful and the pointer is not null -- and in that case the
+ * caller is then reponsible for destroying it.
+ * RETURN:
+ * Returns a pointer to the SECItem holding the response.
+ * On error, returns null with error set describing the reason:
+ * SEC_ERROR_UNKNOWN_ISSUER
+ * SEC_ERROR_CERT_BAD_ACCESS_LOCATION
+ * SEC_ERROR_OCSP_BAD_HTTP_RESPONSE
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ */
+SECItem *
+CERT_GetEncodedOCSPResponseByMethod(PLArenaPool *arena, CERTCertList *certList,
+ const char *location, const char *method,
+ PRTime time, PRBool addServiceLocator,
+ CERTCertificate *signerCert, void *pwArg,
+ CERTOCSPRequest **pRequest)
+{
+ CERTOCSPRequest *request;
+ request = CERT_CreateOCSPRequest(certList, time, addServiceLocator,
+ signerCert);
+ if (!request)
+ return NULL;
+ return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location,
+ method, time, addServiceLocator,
+ pwArg, pRequest);
+}
+
+/*
+ * FUNCTION: CERT_GetEncodedOCSPResponse
+ * Creates and sends a request to an OCSP responder, then reads and
+ * returns the (encoded) response.
+ *
+ * This is a legacy API that behaves identically to
+ * CERT_GetEncodedOCSPResponseByMethod using the "POST" method.
+ */
+SECItem *
+CERT_GetEncodedOCSPResponse(PLArenaPool *arena, CERTCertList *certList,
+ const char *location, PRTime time,
+ PRBool addServiceLocator,
+ CERTCertificate *signerCert, void *pwArg,
+ CERTOCSPRequest **pRequest)
+{
+ return CERT_GetEncodedOCSPResponseByMethod(arena, certList, location,
+ "POST", time, addServiceLocator,
+ signerCert, pwArg, pRequest);
+}
+
+/* URL encode a buffer that consists of base64-characters, only,
+ * which means we can use a simple encoding logic.
+ *
+ * No output buffer size checking is performed.
+ * You should call the function twice, to calculate the required buffer size.
+ *
+ * If the outpufBuf parameter is NULL, the function will calculate the
+ * required size, including the trailing zero termination char.
+ *
+ * The function returns the number of bytes calculated or produced.
+ */
+size_t
+ocsp_UrlEncodeBase64Buf(const char *base64Buf, char *outputBuf)
+{
+ const char *walkInput = NULL;
+ char *walkOutput = outputBuf;
+ size_t count = 0;
+
+ for (walkInput = base64Buf; *walkInput; ++walkInput) {
+ char c = *walkInput;
+ if (isspace(c))
+ continue;
+ switch (c) {
+ case '+':
+ if (outputBuf) {
+ strcpy(walkOutput, "%2B");
+ walkOutput += 3;
+ }
+ count += 3;
+ break;
+ case '/':
+ if (outputBuf) {
+ strcpy(walkOutput, "%2F");
+ walkOutput += 3;
+ }
+ count += 3;
+ break;
+ case '=':
+ if (outputBuf) {
+ strcpy(walkOutput, "%3D");
+ walkOutput += 3;
+ }
+ count += 3;
+ break;
+ default:
+ if (outputBuf) {
+ *walkOutput = *walkInput;
+ ++walkOutput;
+ }
+ ++count;
+ break;
+ }
+ }
+ if (outputBuf) {
+ *walkOutput = 0;
+ }
+ ++count;
+ return count;
+}
+
+enum { max_get_request_size = 255 }; /* defined by RFC2560 */
+
+static SECItem *
+cert_GetOCSPResponse(PLArenaPool *arena, const char *location,
+ const SECItem *encodedRequest);
+
+static SECItem *
+ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena,
+ CERTOCSPRequest *request,
+ const char *location,
+ const char *method,
+ PRTime time,
+ PRBool addServiceLocator,
+ void *pwArg,
+ CERTOCSPRequest **pRequest)
+{
+ SECItem *encodedRequest = NULL;
+ SECItem *encodedResponse = NULL;
+ SECStatus rv;
+
+ if (!location || !*location) /* location should be at least one byte */
+ goto loser;
+
+ rv = CERT_AddOCSPAcceptableResponses(request,
+ SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
+ if (rv != SECSuccess)
+ goto loser;
+
+ encodedRequest = CERT_EncodeOCSPRequest(NULL, request, pwArg);
+ if (encodedRequest == NULL)
+ goto loser;
+
+ if (!strcmp(method, "GET")) {
+ encodedResponse = cert_GetOCSPResponse(arena, location, encodedRequest);
+ } else if (!strcmp(method, "POST")) {
+ encodedResponse = CERT_PostOCSPRequest(arena, location, encodedRequest);
+ } else {
+ goto loser;
+ }
+
+ if (encodedResponse != NULL && pRequest != NULL) {
+ *pRequest = request;
+ request = NULL; /* avoid destroying below */
+ }
+
+loser:
+ if (request != NULL)
+ CERT_DestroyOCSPRequest(request);
+ if (encodedRequest != NULL)
+ SECITEM_FreeItem(encodedRequest, PR_TRUE);
+ return encodedResponse;
+}
+
+static SECItem *
+cert_FetchOCSPResponse(PLArenaPool *arena, const char *location,
+ const SECItem *encodedRequest);
+
+/* using HTTP GET method */
+static SECItem *
+cert_GetOCSPResponse(PLArenaPool *arena, const char *location,
+ const SECItem *encodedRequest)
+{
+ char *walkOutput = NULL;
+ char *fullGetPath = NULL;
+ size_t pathLength;
+ PRInt32 urlEncodedBufLength;
+ size_t base64size;
+ char b64ReqBuf[max_get_request_size + 1];
+ size_t slashLengthIfNeeded = 0;
+ size_t getURLLength;
+ SECItem *item;
+
+ if (!location || !*location) {
+ return NULL;
+ }
+
+ pathLength = strlen(location);
+ if (location[pathLength - 1] != '/') {
+ slashLengthIfNeeded = 1;
+ }
+
+ /* Calculation as documented by PL_Base64Encode function.
+ * Use integer conversion to avoid having to use function ceil().
+ */
+ base64size = (((encodedRequest->len + 2) / 3) * 4);
+ if (base64size > max_get_request_size) {
+ return NULL;
+ }
+ memset(b64ReqBuf, 0, sizeof(b64ReqBuf));
+ PL_Base64Encode((const char *)encodedRequest->data, encodedRequest->len,
+ b64ReqBuf);
+
+ urlEncodedBufLength = ocsp_UrlEncodeBase64Buf(b64ReqBuf, NULL);
+ getURLLength = pathLength + urlEncodedBufLength + slashLengthIfNeeded;
+
+ /* urlEncodedBufLength already contains room for the zero terminator.
+ * Add another if we must add the '/' char.
+ */
+ if (arena) {
+ fullGetPath = (char *)PORT_ArenaAlloc(arena, getURLLength);
+ } else {
+ fullGetPath = (char *)PORT_Alloc(getURLLength);
+ }
+ if (!fullGetPath) {
+ return NULL;
+ }
+
+ strcpy(fullGetPath, location);
+ walkOutput = fullGetPath + pathLength;
+
+ if (walkOutput > fullGetPath && slashLengthIfNeeded) {
+ strcpy(walkOutput, "/");
+ ++walkOutput;
+ }
+ ocsp_UrlEncodeBase64Buf(b64ReqBuf, walkOutput);
+
+ item = cert_FetchOCSPResponse(arena, fullGetPath, NULL);
+ if (!arena) {
+ PORT_Free(fullGetPath);
+ }
+ return item;
+}
+
+SECItem *
+CERT_PostOCSPRequest(PLArenaPool *arena, const char *location,
+ const SECItem *encodedRequest)
+{
+ return cert_FetchOCSPResponse(arena, location, encodedRequest);
+}
+
+SECItem *
+cert_FetchOCSPResponse(PLArenaPool *arena, const char *location,
+ const SECItem *encodedRequest)
+{
+ const SEC_HttpClientFcn *registeredHttpClient;
+ SECItem *encodedResponse = NULL;
+
+ registeredHttpClient = SEC_GetRegisteredHttpClient();
+
+ if (registeredHttpClient && registeredHttpClient->version == 1) {
+ encodedResponse = fetchOcspHttpClientV1(
+ arena,
+ &registeredHttpClient->fcnTable.ftable1,
+ location,
+ encodedRequest);
+ } else {
+ /* use internal http client */
+ PRFileDesc *sock = ocsp_SendEncodedRequest(location, encodedRequest);
+ if (sock) {
+ encodedResponse = ocsp_GetEncodedResponse(arena, sock);
+ PR_Close(sock);
+ }
+ }
+
+ return encodedResponse;
+}
+
+static SECItem *
+ocsp_GetEncodedOCSPResponseForSingleCert(PLArenaPool *arena,
+ CERTOCSPCertID *certID,
+ CERTCertificate *singleCert,
+ const char *location,
+ const char *method,
+ PRTime time,
+ PRBool addServiceLocator,
+ void *pwArg,
+ CERTOCSPRequest **pRequest)
+{
+ CERTOCSPRequest *request;
+ request = cert_CreateSingleCertOCSPRequest(certID, singleCert, time,
+ addServiceLocator, NULL);
+ if (!request)
+ return NULL;
+ return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location,
+ method, time, addServiceLocator,
+ pwArg, pRequest);
+}
+
+/* Checks a certificate for the key usage extension of OCSP signer. */
+static PRBool
+ocsp_CertIsOCSPDesignatedResponder(CERTCertificate *cert)
+{
+ SECStatus rv;
+ SECItem extItem;
+ SECItem **oids;
+ SECItem *oid;
+ SECOidTag oidTag;
+ PRBool retval;
+ CERTOidSequence *oidSeq = NULL;
+
+ 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) {
+ oid = *oids;
+
+ oidTag = SECOID_FindOIDTag(oid);
+
+ if (oidTag == SEC_OID_OCSP_RESPONDER) {
+ goto success;
+ }
+
+ oids++;
+ }
+
+loser:
+ retval = PR_FALSE;
+ PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
+ goto done;
+success:
+ retval = PR_TRUE;
+done:
+ if (extItem.data != NULL) {
+ PORT_Free(extItem.data);
+ }
+ if (oidSeq != NULL) {
+ CERT_DestroyOidSequence(oidSeq);
+ }
+
+ return (retval);
+}
+
+#ifdef LATER /* \
+ * XXX This function is not currently used, but will \
+ * be needed later when we do revocation checking of \
+ * the responder certificate. Of course, it may need \
+ * revising then, if the cert extension interface has \
+ * changed. (Hopefully it will!) \
+ */
+
+/* Checks a certificate to see if it has the OCSP no check extension. */
+static PRBool
+ocsp_CertHasNoCheckExtension(CERTCertificate *cert)
+{
+ SECStatus rv;
+
+ rv = CERT_FindCertExtension(cert, SEC_OID_PKIX_OCSP_NO_CHECK,
+ NULL);
+ if (rv == SECSuccess) {
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+#endif /* LATER */
+
+static PRBool
+ocsp_matchcert(SECItem *certIndex, CERTCertificate *testCert)
+{
+ SECItem item;
+ unsigned char buf[HASH_LENGTH_MAX];
+
+ item.data = buf;
+ item.len = SHA1_LENGTH;
+
+ if (CERT_GetSubjectPublicKeyDigest(NULL, testCert, SEC_OID_SHA1,
+ &item) == NULL) {
+ return PR_FALSE;
+ }
+ if (SECITEM_ItemsAreEqual(certIndex, &item)) {
+ return PR_TRUE;
+ }
+ if (CERT_GetSubjectPublicKeyDigest(NULL, testCert, SEC_OID_MD5,
+ &item) == NULL) {
+ return PR_FALSE;
+ }
+ if (SECITEM_ItemsAreEqual(certIndex, &item)) {
+ return PR_TRUE;
+ }
+ if (CERT_GetSubjectPublicKeyDigest(NULL, testCert, SEC_OID_MD2,
+ &item) == NULL) {
+ return PR_FALSE;
+ }
+ if (SECITEM_ItemsAreEqual(certIndex, &item)) {
+ return PR_TRUE;
+ }
+
+ return PR_FALSE;
+}
+
+static CERTCertificate *
+ocsp_CertGetDefaultResponder(CERTCertDBHandle *handle, CERTOCSPCertID *certID);
+
+CERTCertificate *
+ocsp_GetSignerCertificate(CERTCertDBHandle *handle, ocspResponseData *tbsData,
+ ocspSignature *signature, CERTCertificate *issuer)
+{
+ CERTCertificate **certs = NULL;
+ CERTCertificate *signerCert = NULL;
+ SECStatus rv = SECFailure;
+ PRBool lookupByName = PR_TRUE;
+ void *certIndex = NULL;
+ int certCount = 0;
+
+ PORT_Assert(tbsData->responderID != NULL);
+ switch (tbsData->responderID->responderIDType) {
+ case ocspResponderID_byName:
+ lookupByName = PR_TRUE;
+ certIndex = &tbsData->derResponderID;
+ break;
+ case ocspResponderID_byKey:
+ lookupByName = PR_FALSE;
+ certIndex = &tbsData->responderID->responderIDValue.keyHash;
+ break;
+ case ocspResponderID_other:
+ default:
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+ return NULL;
+ }
+
+ /*
+ * If the signature contains some certificates as well, temporarily
+ * import them in case they are needed for verification.
+ *
+ * Note that the result of this is that each cert in "certs" needs
+ * to be destroyed.
+ */
+ if (signature->derCerts != NULL) {
+ for (; signature->derCerts[certCount] != NULL; certCount++) {
+ /* just counting */
+ }
+ rv = CERT_ImportCerts(handle, certUsageStatusResponder, certCount,
+ signature->derCerts, &certs,
+ PR_FALSE, PR_FALSE, NULL);
+ if (rv != SECSuccess)
+ goto finish;
+ }
+
+ /*
+ * Now look up the certificate that did the signing.
+ * The signer can be specified either by name or by key hash.
+ */
+ if (lookupByName) {
+ SECItem *crIndex = (SECItem *)certIndex;
+ SECItem encodedName;
+ PLArenaPool *arena;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena != NULL) {
+
+ rv = SEC_QuickDERDecodeItem(arena, &encodedName,
+ ocsp_ResponderIDDerNameTemplate,
+ crIndex);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_BAD_DER)
+ PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+ } else {
+ signerCert = CERT_FindCertByName(handle, &encodedName);
+ }
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ } else {
+ /*
+ * The signer is either 1) a known issuer CA we passed in,
+ * 2) the default OCSP responder, or 3) an intermediate CA
+ * passed in the cert list to use. Figure out which it is.
+ */
+ int i;
+ CERTCertificate *responder =
+ ocsp_CertGetDefaultResponder(handle, NULL);
+ if (responder && ocsp_matchcert(certIndex, responder)) {
+ signerCert = CERT_DupCertificate(responder);
+ } else if (issuer && ocsp_matchcert(certIndex, issuer)) {
+ signerCert = CERT_DupCertificate(issuer);
+ }
+ for (i = 0; (signerCert == NULL) && (i < certCount); i++) {
+ if (ocsp_matchcert(certIndex, certs[i])) {
+ signerCert = CERT_DupCertificate(certs[i]);
+ }
+ }
+ if (signerCert == NULL) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
+ }
+ }
+
+finish:
+ if (certs != NULL) {
+ CERT_DestroyCertArray(certs, certCount);
+ }
+
+ return signerCert;
+}
+
+SECStatus
+ocsp_VerifyResponseSignature(CERTCertificate *signerCert,
+ ocspSignature *signature,
+ SECItem *tbsResponseDataDER,
+ void *pwArg)
+{
+ SECKEYPublicKey *signerKey = NULL;
+ SECStatus rv = SECFailure;
+ CERTSignedData signedData;
+
+ /*
+ * Now get the public key from the signer's certificate; we need
+ * it to perform the verification.
+ */
+ signerKey = CERT_ExtractPublicKey(signerCert);
+ if (signerKey == NULL) {
+ return SECFailure;
+ }
+
+ /*
+ * We copy the signature data *pointer* and length, so that we can
+ * modify the length without damaging the original copy. This is a
+ * simple copy, not a dup, so no destroy/free is necessary.
+ */
+ signedData.signature = signature->signature;
+ signedData.signatureAlgorithm = signature->signatureAlgorithm;
+ signedData.data = *tbsResponseDataDER;
+
+ rv = CERT_VerifySignedDataWithPublicKey(&signedData, signerKey, pwArg);
+ if (rv != SECSuccess &&
+ (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE ||
+ PORT_GetError() == SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED)) {
+ PORT_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE);
+ }
+
+ if (signerKey != NULL) {
+ SECKEY_DestroyPublicKey(signerKey);
+ }
+
+ return rv;
+}
+
+/*
+ * FUNCTION: CERT_VerifyOCSPResponseSignature
+ * Check the signature on an OCSP Response. Will also perform a
+ * verification of the signer's certificate. Note, however, that a
+ * successful verification does not make any statement about the
+ * signer's *authority* to provide status for the certificate(s),
+ * that must be checked individually for each certificate.
+ * INPUTS:
+ * CERTOCSPResponse *response
+ * Pointer to response structure with signature to be checked.
+ * CERTCertDBHandle *handle
+ * Pointer to CERTCertDBHandle for certificate DB to use for verification.
+ * void *pwArg
+ * Pointer to argument for password prompting, if needed.
+ * OUTPUTS:
+ * CERTCertificate **pSignerCert
+ * Pointer in which to store signer's certificate; only filled-in if
+ * non-null.
+ * RETURN:
+ * Returns SECSuccess when signature is valid, anything else means invalid.
+ * Possible errors set:
+ * SEC_ERROR_OCSP_MALFORMED_RESPONSE - unknown type of ResponderID
+ * SEC_ERROR_INVALID_TIME - bad format of "ProducedAt" time
+ * SEC_ERROR_UNKNOWN_SIGNER - signer's cert could not be found
+ * SEC_ERROR_BAD_SIGNATURE - the signature did not verify
+ * Other errors are any of the many possible failures in cert verification
+ * (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when
+ * verifying the signer's cert, or low-level problems (no memory, etc.)
+ */
+SECStatus
+CERT_VerifyOCSPResponseSignature(CERTOCSPResponse *response,
+ CERTCertDBHandle *handle, void *pwArg,
+ CERTCertificate **pSignerCert,
+ CERTCertificate *issuer)
+{
+ SECItem *tbsResponseDataDER;
+ CERTCertificate *signerCert = NULL;
+ SECStatus rv = SECFailure;
+ PRTime producedAt;
+
+ /* ocsp_DecodeBasicOCSPResponse will fail if asn1 decoder is unable
+ * to properly decode tbsData (see the function and
+ * ocsp_BasicOCSPResponseTemplate). Thus, tbsData can not be
+ * equal to null */
+ ocspResponseData *tbsData = ocsp_GetResponseData(response,
+ &tbsResponseDataDER);
+ ocspSignature *signature = ocsp_GetResponseSignature(response);
+
+ if (!signature) {
+ PORT_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE);
+ return SECFailure;
+ }
+
+ /*
+ * If this signature has already gone through verification, just
+ * return the cached result.
+ */
+ if (signature->wasChecked) {
+ if (signature->status == SECSuccess) {
+ if (pSignerCert != NULL)
+ *pSignerCert = CERT_DupCertificate(signature->cert);
+ } else {
+ PORT_SetError(signature->failureReason);
+ }
+ return signature->status;
+ }
+
+ signerCert = ocsp_GetSignerCertificate(handle, tbsData,
+ signature, issuer);
+ if (signerCert == NULL) {
+ rv = SECFailure;
+ if (PORT_GetError() == SEC_ERROR_UNKNOWN_CERT) {
+ /* Make the error a little more specific. */
+ PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
+ }
+ goto finish;
+ }
+
+ /*
+ * We could mark this true at the top of this function, or always
+ * below at "finish", but if the problem was just that we could not
+ * find the signer's cert, leave that as if the signature hasn't
+ * been checked in case a subsequent call might have better luck.
+ */
+ signature->wasChecked = PR_TRUE;
+
+ /*
+ * The function will also verify the signer certificate; we
+ * need to tell it *when* that certificate must be valid -- for our
+ * purposes we expect it to be valid when the response was signed.
+ * The value of "producedAt" is the signing time.
+ */
+ rv = DER_GeneralizedTimeToTime(&producedAt, &tbsData->producedAt);
+ if (rv != SECSuccess)
+ goto finish;
+
+ /*
+ * Just because we have a cert does not mean it is any good; check
+ * it for validity, trust and usage.
+ */
+ if (!ocsp_CertIsOCSPDefaultResponder(handle, signerCert)) {
+ SECCertUsage certUsage;
+ if (CERT_IsCACert(signerCert, NULL)) {
+ certUsage = certUsageAnyCA;
+ } else {
+ certUsage = certUsageStatusResponder;
+ }
+ rv = cert_VerifyCertWithFlags(handle, signerCert, PR_TRUE, certUsage,
+ producedAt, CERT_VERIFYCERT_SKIP_OCSP,
+ pwArg, NULL);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
+ goto finish;
+ }
+ }
+
+ rv = ocsp_VerifyResponseSignature(signerCert, signature,
+ tbsResponseDataDER,
+ pwArg);
+
+finish:
+ if (signature->wasChecked)
+ signature->status = rv;
+
+ if (rv != SECSuccess) {
+ signature->failureReason = PORT_GetError();
+ if (signerCert != NULL)
+ CERT_DestroyCertificate(signerCert);
+ } else {
+ /*
+ * Save signer's certificate in signature.
+ */
+ signature->cert = signerCert;
+ if (pSignerCert != NULL) {
+ /*
+ * Pass pointer to signer's certificate back to our caller,
+ * who is also now responsible for destroying it.
+ */
+ *pSignerCert = CERT_DupCertificate(signerCert);
+ }
+ }
+
+ return rv;
+}
+
+/*
+ * See if the request's certID and the single response's certID match.
+ * This can be easy or difficult, depending on whether the same hash
+ * algorithm was used.
+ */
+static PRBool
+ocsp_CertIDsMatch(CERTOCSPCertID *requestCertID,
+ CERTOCSPCertID *responseCertID)
+{
+ PRBool match = PR_FALSE;
+ SECOidTag hashAlg;
+ SECItem *keyHash = NULL;
+ SECItem *nameHash = NULL;
+
+ /*
+ * In order to match, they must have the same issuer and the same
+ * serial number.
+ *
+ * We just compare the easier things first.
+ */
+ if (SECITEM_CompareItem(&requestCertID->serialNumber,
+ &responseCertID->serialNumber) != SECEqual) {
+ goto done;
+ }
+
+ /*
+ * Make sure the "parameters" are not too bogus. Since we encoded
+ * requestCertID->hashAlgorithm, we don't need to check it.
+ */
+ if (responseCertID->hashAlgorithm.parameters.len > 2) {
+ goto done;
+ }
+ if (SECITEM_CompareItem(&requestCertID->hashAlgorithm.algorithm,
+ &responseCertID->hashAlgorithm.algorithm) ==
+ SECEqual) {
+ /*
+ * If the hash algorithms match then we can do a simple compare
+ * of the hash values themselves.
+ */
+ if ((SECITEM_CompareItem(&requestCertID->issuerNameHash,
+ &responseCertID->issuerNameHash) == SECEqual) &&
+ (SECITEM_CompareItem(&requestCertID->issuerKeyHash,
+ &responseCertID->issuerKeyHash) == SECEqual)) {
+ match = PR_TRUE;
+ }
+ goto done;
+ }
+
+ hashAlg = SECOID_FindOIDTag(&responseCertID->hashAlgorithm.algorithm);
+ switch (hashAlg) {
+ case SEC_OID_SHA1:
+ keyHash = &requestCertID->issuerSHA1KeyHash;
+ nameHash = &requestCertID->issuerSHA1NameHash;
+ break;
+ case SEC_OID_MD5:
+ keyHash = &requestCertID->issuerMD5KeyHash;
+ nameHash = &requestCertID->issuerMD5NameHash;
+ break;
+ case SEC_OID_MD2:
+ keyHash = &requestCertID->issuerMD2KeyHash;
+ nameHash = &requestCertID->issuerMD2NameHash;
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return PR_FALSE;
+ }
+
+ if ((keyHash != NULL) &&
+ (SECITEM_CompareItem(nameHash,
+ &responseCertID->issuerNameHash) == SECEqual) &&
+ (SECITEM_CompareItem(keyHash,
+ &responseCertID->issuerKeyHash) == SECEqual)) {
+ match = PR_TRUE;
+ }
+
+done:
+ return match;
+}
+
+/*
+ * Find the single response for the cert specified by certID.
+ * No copying is done; this just returns a pointer to the appropriate
+ * response within responses, if it is found (and null otherwise).
+ * This is fine, of course, since this function is internal-use only.
+ */
+static CERTOCSPSingleResponse *
+ocsp_GetSingleResponseForCertID(CERTOCSPSingleResponse **responses,
+ CERTCertDBHandle *handle,
+ CERTOCSPCertID *certID)
+{
+ CERTOCSPSingleResponse *single;
+ int i;
+
+ if (responses == NULL)
+ return NULL;
+
+ for (i = 0; responses[i] != NULL; i++) {
+ single = responses[i];
+ if (ocsp_CertIDsMatch(certID, single->certID)) {
+ return single;
+ }
+ }
+
+ /*
+ * The OCSP server should have included a response even if it knew
+ * nothing about the certificate in question. Since it did not,
+ * this will make it look as if it had.
+ *
+ * XXX Should we make this a separate error to notice the server's
+ * bad behavior?
+ */
+ PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT);
+ return NULL;
+}
+
+static ocspCheckingContext *
+ocsp_GetCheckingContext(CERTCertDBHandle *handle)
+{
+ CERTStatusConfig *statusConfig;
+ ocspCheckingContext *ocspcx = NULL;
+
+ statusConfig = CERT_GetStatusConfig(handle);
+ if (statusConfig != NULL) {
+ ocspcx = statusConfig->statusContext;
+
+ /*
+ * This is actually an internal error, because we should never
+ * have a good statusConfig without a good statusContext, too.
+ * For lack of anything better, though, we just assert and use
+ * the same error as if there were no statusConfig (set below).
+ */
+ PORT_Assert(ocspcx != NULL);
+ }
+
+ if (ocspcx == NULL)
+ PORT_SetError(SEC_ERROR_OCSP_NOT_ENABLED);
+
+ return ocspcx;
+}
+
+/*
+ * Return cert reference if the given signerCert is the default responder for
+ * the given certID. If not, or if any error, return NULL.
+ */
+static CERTCertificate *
+ocsp_CertGetDefaultResponder(CERTCertDBHandle *handle, CERTOCSPCertID *certID)
+{
+ ocspCheckingContext *ocspcx;
+
+ ocspcx = ocsp_GetCheckingContext(handle);
+ if (ocspcx == NULL)
+ goto loser;
+
+ /*
+ * Right now we have only one default responder. It applies to
+ * all certs when it is used, so the check is simple and certID
+ * has no bearing on the answer. Someday in the future we may
+ * allow configuration of different responders for different
+ * issuers, and then we would have to use the issuer specified
+ * in certID to determine if signerCert is the right one.
+ */
+ if (ocspcx->useDefaultResponder) {
+ PORT_Assert(ocspcx->defaultResponderCert != NULL);
+ return ocspcx->defaultResponderCert;
+ }
+
+loser:
+ return NULL;
+}
+
+/*
+ * Return true if the cert is one of the default responders configured for
+ * ocsp context. If not, or if any error, return false.
+ */
+PRBool
+ocsp_CertIsOCSPDefaultResponder(CERTCertDBHandle *handle, CERTCertificate *cert)
+{
+ ocspCheckingContext *ocspcx;
+
+ ocspcx = ocsp_GetCheckingContext(handle);
+ if (ocspcx == NULL)
+ return PR_FALSE;
+
+ /*
+ * Right now we have only one default responder. It applies to
+ * all certs when it is used, so the check is simple and certID
+ * has no bearing on the answer. Someday in the future we may
+ * allow configuration of different responders for different
+ * issuers, and then we would have to use the issuer specified
+ * in certID to determine if signerCert is the right one.
+ */
+ if (ocspcx->useDefaultResponder &&
+ CERT_CompareCerts(ocspcx->defaultResponderCert, cert)) {
+ return PR_TRUE;
+ }
+
+ return PR_FALSE;
+}
+
+/*
+ * Check that the given signer certificate is authorized to sign status
+ * information for the given certID. Return true if it is, false if not
+ * (or if there is any error along the way). If false is returned because
+ * the signer is not authorized, the following error will be set:
+ * SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ *
+ * There are three ways to be authorized. In the order in which we check,
+ * using the terms used in the OCSP spec, the signer must be one of:
+ * 1. A "trusted responder" -- it matches a local configuration
+ * of OCSP signing authority for the certificate in question.
+ * 2. The CA who issued the certificate in question.
+ * 3. A "CA designated responder", aka an "authorized responder" -- it
+ * must be represented by a special cert issued by the CA who issued
+ * the certificate in question.
+ */
+static PRBool
+ocsp_AuthorizedResponderForCertID(CERTCertDBHandle *handle,
+ CERTCertificate *signerCert,
+ CERTOCSPCertID *certID,
+ PRTime thisUpdate)
+{
+ CERTCertificate *issuerCert = NULL, *defRespCert;
+ SECItem *keyHash = NULL;
+ SECItem *nameHash = NULL;
+ SECOidTag hashAlg;
+ PRBool keyHashEQ = PR_FALSE, nameHashEQ = PR_FALSE;
+
+ /*
+ * Check first for a trusted responder, which overrides everything else.
+ */
+ if ((defRespCert = ocsp_CertGetDefaultResponder(handle, certID)) &&
+ CERT_CompareCerts(defRespCert, signerCert)) {
+ return PR_TRUE;
+ }
+
+ /*
+ * In the other two cases, we need to do an issuer comparison.
+ * How we do it depends on whether the signer certificate has the
+ * special extension (for a designated responder) or not.
+ *
+ * First, lets check if signer of the response is the actual issuer
+ * of the cert. For that we will use signer cert key hash and cert subj
+ * name hash and will compare them with already calculated issuer key
+ * hash and issuer name hash. The hash algorithm is picked from response
+ * certID hash to avoid second hash calculation.
+ */
+
+ hashAlg = SECOID_FindOIDTag(&certID->hashAlgorithm.algorithm);
+
+ keyHash = CERT_GetSubjectPublicKeyDigest(NULL, signerCert, hashAlg, NULL);
+ if (keyHash != NULL) {
+
+ keyHashEQ =
+ (SECITEM_CompareItem(keyHash,
+ &certID->issuerKeyHash) == SECEqual);
+ SECITEM_FreeItem(keyHash, PR_TRUE);
+ }
+ if (keyHashEQ &&
+ (nameHash = CERT_GetSubjectNameDigest(NULL, signerCert,
+ hashAlg, NULL))) {
+ nameHashEQ =
+ (SECITEM_CompareItem(nameHash,
+ &certID->issuerNameHash) == SECEqual);
+
+ SECITEM_FreeItem(nameHash, PR_TRUE);
+ if (nameHashEQ) {
+ /* The issuer of the cert is the the signer of the response */
+ return PR_TRUE;
+ }
+ }
+
+ keyHashEQ = PR_FALSE;
+ nameHashEQ = PR_FALSE;
+
+ if (!ocsp_CertIsOCSPDesignatedResponder(signerCert)) {
+ PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE);
+ return PR_FALSE;
+ }
+
+ /*
+ * The signer is a designated responder. Its issuer must match
+ * the issuer of the cert being checked.
+ */
+ issuerCert = CERT_FindCertIssuer(signerCert, thisUpdate,
+ certUsageAnyCA);
+ if (issuerCert == NULL) {
+ /*
+ * We could leave the SEC_ERROR_UNKNOWN_ISSUER error alone,
+ * but the following will give slightly more information.
+ * Once we have an error stack, things will be much better.
+ */
+ PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE);
+ return PR_FALSE;
+ }
+
+ keyHash = CERT_GetSubjectPublicKeyDigest(NULL, issuerCert, hashAlg, NULL);
+ nameHash = CERT_GetSubjectNameDigest(NULL, issuerCert, hashAlg, NULL);
+
+ CERT_DestroyCertificate(issuerCert);
+
+ if (keyHash != NULL && nameHash != NULL) {
+ keyHashEQ =
+ (SECITEM_CompareItem(keyHash,
+ &certID->issuerKeyHash) == SECEqual);
+
+ nameHashEQ =
+ (SECITEM_CompareItem(nameHash,
+ &certID->issuerNameHash) == SECEqual);
+ }
+
+ if (keyHash) {
+ SECITEM_FreeItem(keyHash, PR_TRUE);
+ }
+ if (nameHash) {
+ SECITEM_FreeItem(nameHash, PR_TRUE);
+ }
+
+ if (keyHashEQ && nameHashEQ) {
+ return PR_TRUE;
+ }
+
+ PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE);
+ return PR_FALSE;
+}
+
+/*
+ * We need to check that a responder gives us "recent" information.
+ * Since a responder can pre-package responses, we need to pick an amount
+ * of time that is acceptable to us, and reject any response that is
+ * older than that.
+ *
+ * XXX This *should* be based on some configuration parameter, so that
+ * different usages could specify exactly what constitutes "sufficiently
+ * recent". But that is not going to happen right away. For now, we
+ * want something from within the last 24 hours. This macro defines that
+ * number in seconds.
+ */
+#define OCSP_ALLOWABLE_LAPSE_SECONDS (24L * 60L * 60L)
+
+static PRBool
+ocsp_TimeIsRecent(PRTime checkTime)
+{
+ PRTime now = PR_Now();
+ PRTime lapse, tmp;
+
+ LL_I2L(lapse, OCSP_ALLOWABLE_LAPSE_SECONDS);
+ LL_I2L(tmp, PR_USEC_PER_SEC);
+ LL_MUL(lapse, lapse, tmp); /* allowable lapse in microseconds */
+
+ LL_ADD(checkTime, checkTime, lapse);
+ if (LL_CMP(now, >, checkTime))
+ return PR_FALSE;
+
+ return PR_TRUE;
+}
+
+#define OCSP_SLOP (5L * 60L) /* OCSP responses are allowed to be 5 minutes \
+ in the future by default */
+
+static PRUint32 ocspsloptime = OCSP_SLOP; /* seconds */
+
+/*
+ * If an old response contains the revoked certificate status, we want
+ * to return SECSuccess so the response will be used.
+ */
+static SECStatus
+ocsp_HandleOldSingleResponse(CERTOCSPSingleResponse *single, PRTime time)
+{
+ SECStatus rv;
+ ocspCertStatus *status = single->certStatus;
+ if (status->certStatusType == ocspCertStatus_revoked) {
+ rv = ocsp_CertRevokedAfter(status->certStatusInfo.revokedInfo, time);
+ if (rv != SECSuccess &&
+ PORT_GetError() == SEC_ERROR_REVOKED_CERTIFICATE) {
+ /*
+ * Return SECSuccess now. The subsequent ocsp_CertRevokedAfter
+ * call in ocsp_CertHasGoodStatus will cause
+ * ocsp_CertHasGoodStatus to fail with
+ * SEC_ERROR_REVOKED_CERTIFICATE.
+ */
+ return SECSuccess;
+ }
+ }
+ PORT_SetError(SEC_ERROR_OCSP_OLD_RESPONSE);
+ return SECFailure;
+}
+
+/*
+ * Check that this single response is okay. A return of SECSuccess means:
+ * 1. The signer (represented by "signerCert") is authorized to give status
+ * for the cert represented by the individual response in "single".
+ * 2. The value of thisUpdate is earlier than now.
+ * 3. The value of producedAt is later than or the same as thisUpdate.
+ * 4. If nextUpdate is given:
+ * - The value of nextUpdate is later than now.
+ * - The value of producedAt is earlier than nextUpdate.
+ * Else if no nextUpdate:
+ * - The value of thisUpdate is fairly recent.
+ * - The value of producedAt is fairly recent.
+ * However we do not need to perform an explicit check for this last
+ * constraint because it is already guaranteed by checking that
+ * producedAt is later than thisUpdate and thisUpdate is recent.
+ * Oh, and any responder is "authorized" to say that a cert is unknown to it.
+ *
+ * If any of those checks fail, SECFailure is returned and an error is set:
+ * SEC_ERROR_OCSP_FUTURE_RESPONSE
+ * SEC_ERROR_OCSP_OLD_RESPONSE
+ * SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ */
+static SECStatus
+ocsp_VerifySingleResponse(CERTOCSPSingleResponse *single,
+ CERTCertDBHandle *handle,
+ CERTCertificate *signerCert,
+ PRTime producedAt)
+{
+ CERTOCSPCertID *certID = single->certID;
+ PRTime now, thisUpdate, nextUpdate, tmstamp, tmp;
+ SECStatus rv;
+
+ OCSP_TRACE(("OCSP ocsp_VerifySingleResponse, nextUpdate: %d\n",
+ ((single->nextUpdate) != 0)));
+ /*
+ * If all the responder said was that the given cert was unknown to it,
+ * that is a valid response. Not very interesting to us, of course,
+ * but all this function is concerned with is validity of the response,
+ * not the status of the cert.
+ */
+ PORT_Assert(single->certStatus != NULL);
+ if (single->certStatus->certStatusType == ocspCertStatus_unknown)
+ return SECSuccess;
+
+ /*
+ * We need to extract "thisUpdate" for use below and to pass along
+ * to AuthorizedResponderForCertID in case it needs it for doing an
+ * issuer look-up.
+ */
+ rv = DER_GeneralizedTimeToTime(&thisUpdate, &single->thisUpdate);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * First confirm that signerCert is authorized to give this status.
+ */
+ if (ocsp_AuthorizedResponderForCertID(handle, signerCert, certID,
+ thisUpdate) != PR_TRUE)
+ return SECFailure;
+
+ /*
+ * Now check the time stuff, as described above.
+ */
+ now = PR_Now();
+ /* allow slop time for future response */
+ LL_UI2L(tmstamp, ocspsloptime); /* get slop time in seconds */
+ LL_UI2L(tmp, PR_USEC_PER_SEC);
+ LL_MUL(tmp, tmstamp, tmp); /* convert the slop time to PRTime */
+ LL_ADD(tmstamp, tmp, now); /* add current time to it */
+
+ if (LL_CMP(thisUpdate, >, tmstamp) || LL_CMP(producedAt, <, thisUpdate)) {
+ PORT_SetError(SEC_ERROR_OCSP_FUTURE_RESPONSE);
+ return SECFailure;
+ }
+ if (single->nextUpdate != NULL) {
+ rv = DER_GeneralizedTimeToTime(&nextUpdate, single->nextUpdate);
+ if (rv != SECSuccess)
+ return rv;
+
+ LL_ADD(tmp, tmp, nextUpdate);
+ if (LL_CMP(tmp, <, now) || LL_CMP(producedAt, >, nextUpdate))
+ return ocsp_HandleOldSingleResponse(single, now);
+ } else if (ocsp_TimeIsRecent(thisUpdate) != PR_TRUE) {
+ return ocsp_HandleOldSingleResponse(single, now);
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * FUNCTION: CERT_GetOCSPAuthorityInfoAccessLocation
+ * Get the value of the URI of the OCSP responder for the given cert.
+ * This is found in the (optional) Authority Information Access extension
+ * in the cert.
+ * INPUTS:
+ * CERTCertificate *cert
+ * The certificate being examined.
+ * RETURN:
+ * char *
+ * A copy of the URI for the OCSP method, if found. If either the
+ * extension is not present or it does not contain an entry for OCSP,
+ * SEC_ERROR_CERT_BAD_ACCESS_LOCATION will be set and a NULL returned.
+ * Any other error will also result in a NULL being returned.
+ *
+ * This result should be freed (via PORT_Free) when no longer in use.
+ */
+char *
+CERT_GetOCSPAuthorityInfoAccessLocation(const CERTCertificate *cert)
+{
+ CERTGeneralName *locname = NULL;
+ SECItem *location = NULL;
+ SECItem *encodedAuthInfoAccess = NULL;
+ CERTAuthInfoAccess **authInfoAccess = NULL;
+ char *locURI = NULL;
+ PLArenaPool *arena = NULL;
+ SECStatus rv;
+ int i;
+
+ /*
+ * Allocate this one from the heap because it will get filled in
+ * by CERT_FindCertExtension which will also allocate from the heap,
+ * and we can free the entire thing on our way out.
+ */
+ encodedAuthInfoAccess = SECITEM_AllocItem(NULL, NULL, 0);
+ if (encodedAuthInfoAccess == NULL)
+ goto loser;
+
+ rv = CERT_FindCertExtension(cert, SEC_OID_X509_AUTH_INFO_ACCESS,
+ encodedAuthInfoAccess);
+ if (rv == SECFailure) {
+ PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ goto loser;
+ }
+
+ /*
+ * The rest of the things allocated in the routine will come out of
+ * this arena, which is temporary just for us to decode and get at the
+ * AIA extension. The whole thing will be destroyed on our way out,
+ * after we have copied the location string (url) itself (if found).
+ */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ goto loser;
+
+ authInfoAccess = CERT_DecodeAuthInfoAccessExtension(arena,
+ encodedAuthInfoAccess);
+ if (authInfoAccess == NULL)
+ goto loser;
+
+ for (i = 0; authInfoAccess[i] != NULL; i++) {
+ if (SECOID_FindOIDTag(&authInfoAccess[i]->method) == SEC_OID_PKIX_OCSP)
+ locname = authInfoAccess[i]->location;
+ }
+
+ /*
+ * If we found an AIA extension, but it did not include an OCSP method,
+ * that should look to our caller as if we did not find the extension
+ * at all, because it is only an OCSP method that we care about.
+ * So set the same error that would be set if the AIA extension was
+ * not there at all.
+ */
+ if (locname == NULL) {
+ PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ goto loser;
+ }
+
+ /*
+ * The following is just a pointer back into locname (i.e. not a copy);
+ * thus it should not be freed.
+ */
+ location = CERT_GetGeneralNameByType(locname, certURI, PR_FALSE);
+ if (location == NULL) {
+ /*
+ * XXX Appears that CERT_GetGeneralNameByType does not set an
+ * error if there is no name by that type. For lack of anything
+ * better, act as if the extension was not found. In the future
+ * this should probably be something more like the extension was
+ * badly formed.
+ */
+ PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ goto loser;
+ }
+
+ /*
+ * That location is really a string, but it has a specified length
+ * without a null-terminator. We need a real string that does have
+ * a null-terminator, and we need a copy of it anyway to return to
+ * our caller -- so allocate and copy.
+ */
+ locURI = PORT_Alloc(location->len + 1);
+ if (locURI == NULL) {
+ goto loser;
+ }
+ PORT_Memcpy(locURI, location->data, location->len);
+ locURI[location->len] = '\0';
+
+loser:
+ if (arena != NULL)
+ PORT_FreeArena(arena, PR_FALSE);
+
+ if (encodedAuthInfoAccess != NULL)
+ SECITEM_FreeItem(encodedAuthInfoAccess, PR_TRUE);
+
+ return locURI;
+}
+
+/*
+ * Figure out where we should go to find out the status of the given cert
+ * via OCSP. If allowed to use a default responder uri and a default
+ * responder is set up, then that is our answer.
+ * If not, see if the certificate has an Authority Information Access (AIA)
+ * extension for OCSP, and return the value of that. Otherwise return NULL.
+ * We also let our caller know whether or not the responder chosen was
+ * a default responder or not through the output variable isDefault;
+ * its value has no meaning unless a good (non-null) value is returned
+ * for the location.
+ *
+ * The result needs to be freed (PORT_Free) when no longer in use.
+ */
+char *
+ocsp_GetResponderLocation(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool canUseDefault, PRBool *isDefault)
+{
+ ocspCheckingContext *ocspcx = NULL;
+ char *ocspUrl = NULL;
+
+ if (canUseDefault) {
+ ocspcx = ocsp_GetCheckingContext(handle);
+ }
+ if (ocspcx != NULL && ocspcx->useDefaultResponder) {
+ /*
+ * A default responder wins out, if specified.
+ * XXX Someday this may be a more complicated determination based
+ * on the cert's issuer. (That is, we could have different default
+ * responders configured for different issuers.)
+ */
+ PORT_Assert(ocspcx->defaultResponderURI != NULL);
+ *isDefault = PR_TRUE;
+ return (PORT_Strdup(ocspcx->defaultResponderURI));
+ }
+
+ /*
+ * No default responder set up, so go see if we can find an AIA
+ * extension that has a value for OCSP, and get the url from that.
+ */
+ *isDefault = PR_FALSE;
+ ocspUrl = CERT_GetOCSPAuthorityInfoAccessLocation(cert);
+ if (!ocspUrl) {
+ CERT_StringFromCertFcn altFcn;
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ altFcn = OCSP_Global.alternateOCSPAIAFcn;
+ PR_ExitMonitor(OCSP_Global.monitor);
+ if (altFcn) {
+ ocspUrl = (*altFcn)(cert);
+ if (ocspUrl)
+ *isDefault = PR_TRUE;
+ }
+ }
+ return ocspUrl;
+}
+
+/*
+ * Return SECSuccess if the cert was revoked *after* "time",
+ * SECFailure otherwise.
+ */
+static SECStatus
+ocsp_CertRevokedAfter(ocspRevokedInfo *revokedInfo, PRTime time)
+{
+ PRTime revokedTime;
+ SECStatus rv;
+
+ rv = DER_GeneralizedTimeToTime(&revokedTime, &revokedInfo->revocationTime);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * Set the error even if we will return success; someone might care.
+ */
+ PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
+
+ if (LL_CMP(revokedTime, >, time))
+ return SECSuccess;
+
+ return SECFailure;
+}
+
+/*
+ * See if the cert represented in the single response had a good status
+ * at the specified time.
+ */
+SECStatus
+ocsp_CertHasGoodStatus(ocspCertStatus *status, PRTime time)
+{
+ SECStatus rv;
+ switch (status->certStatusType) {
+ case ocspCertStatus_good:
+ rv = SECSuccess;
+ break;
+ case ocspCertStatus_revoked:
+ rv = ocsp_CertRevokedAfter(status->certStatusInfo.revokedInfo, time);
+ break;
+ case ocspCertStatus_unknown:
+ PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT);
+ rv = SECFailure;
+ break;
+ case ocspCertStatus_other:
+ default:
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+ rv = SECFailure;
+ break;
+ }
+ return rv;
+}
+
+static SECStatus
+ocsp_SingleResponseCertHasGoodStatus(CERTOCSPSingleResponse *single,
+ PRTime time)
+{
+ return ocsp_CertHasGoodStatus(single->certStatus, time);
+}
+
+/* SECFailure means the arguments were invalid.
+ * On SECSuccess, the out parameters contain the OCSP status.
+ * rvOcsp contains the overall result of the OCSP operation.
+ * Depending on input parameter ignoreGlobalOcspFailureSetting,
+ * a soft failure might be converted into *rvOcsp=SECSuccess.
+ * If the cached attempt to obtain OCSP information had resulted
+ * in a failure, missingResponseError shows the error code of
+ * that failure.
+ * cacheFreshness is ocspMissing if no entry was found,
+ * ocspFresh if a fresh entry was found, or
+ * ocspStale if a stale entry was found.
+ */
+SECStatus
+ocsp_GetCachedOCSPResponseStatus(CERTOCSPCertID *certID,
+ PRTime time,
+ PRBool ignoreGlobalOcspFailureSetting,
+ SECStatus *rvOcsp,
+ SECErrorCodes *missingResponseError,
+ OCSPFreshness *cacheFreshness)
+{
+ OCSPCacheItem *cacheItem = NULL;
+
+ if (!certID || !missingResponseError || !rvOcsp || !cacheFreshness) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ *rvOcsp = SECFailure;
+ *missingResponseError = 0;
+ *cacheFreshness = ocspMissing;
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ cacheItem = ocsp_FindCacheEntry(&OCSP_Global.cache, certID);
+ if (cacheItem) {
+ *cacheFreshness = ocsp_IsCacheItemFresh(cacheItem) ? ocspFresh
+ : ocspStale;
+ /* having an arena means, we have a cached certStatus */
+ if (cacheItem->certStatusArena) {
+ *rvOcsp = ocsp_CertHasGoodStatus(&cacheItem->certStatus, time);
+ if (*rvOcsp != SECSuccess) {
+ *missingResponseError = PORT_GetError();
+ }
+ } else {
+ /*
+ * No status cached, the previous attempt failed.
+ * If OCSP is required, we never decide based on a failed attempt
+ * However, if OCSP is optional, a recent OCSP failure is
+ * an allowed good state.
+ */
+ if (*cacheFreshness == ocspFresh &&
+ !ignoreGlobalOcspFailureSetting &&
+ OCSP_Global.ocspFailureMode ==
+ ocspMode_FailureIsNotAVerificationFailure) {
+ *rvOcsp = SECSuccess;
+ }
+ *missingResponseError = cacheItem->missingResponseError;
+ }
+ }
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return SECSuccess;
+}
+
+PRBool
+ocsp_FetchingFailureIsVerificationFailure(void)
+{
+ PRBool isFailure;
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ isFailure =
+ OCSP_Global.ocspFailureMode == ocspMode_FailureIsVerificationFailure;
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return isFailure;
+}
+
+/*
+ * FUNCTION: CERT_CheckOCSPStatus
+ * Checks the status of a certificate via OCSP. Will only check status for
+ * a certificate that has an AIA (Authority Information Access) extension
+ * for OCSP *or* when a "default responder" is specified and enabled.
+ * (If no AIA extension for OCSP and no default responder in place, the
+ * cert is considered to have a good status and SECSuccess is returned.)
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * certificate DB of the cert that is being checked
+ * CERTCertificate *cert
+ * the certificate being checked
+ * XXX in the long term also need a boolean parameter that specifies
+ * whether to check the cert chain, as well; for now we check only
+ * the leaf (the specified certificate)
+ * PRTime time
+ * time for which status is to be determined
+ * void *pwArg
+ * argument for password prompting, if needed
+ * RETURN:
+ * Returns SECSuccess if an approved OCSP responder "knows" the cert
+ * *and* returns a non-revoked status for it; SECFailure otherwise,
+ * with an error set describing the reason:
+ *
+ * SEC_ERROR_OCSP_BAD_HTTP_RESPONSE
+ * SEC_ERROR_OCSP_FUTURE_RESPONSE
+ * SEC_ERROR_OCSP_MALFORMED_REQUEST
+ * SEC_ERROR_OCSP_MALFORMED_RESPONSE
+ * SEC_ERROR_OCSP_OLD_RESPONSE
+ * SEC_ERROR_OCSP_REQUEST_NEEDS_SIG
+ * SEC_ERROR_OCSP_SERVER_ERROR
+ * SEC_ERROR_OCSP_TRY_SERVER_LATER
+ * SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST
+ * SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE
+ * SEC_ERROR_OCSP_UNKNOWN_CERT
+ * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS
+ * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE
+ *
+ * SEC_ERROR_BAD_SIGNATURE
+ * SEC_ERROR_CERT_BAD_ACCESS_LOCATION
+ * SEC_ERROR_INVALID_TIME
+ * SEC_ERROR_REVOKED_CERTIFICATE
+ * SEC_ERROR_UNKNOWN_ISSUER
+ * SEC_ERROR_UNKNOWN_SIGNER
+ *
+ * Other errors are any of the many possible failures in cert verification
+ * (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when
+ * verifying the signer's cert, or low-level problems (error allocating
+ * memory, error performing ASN.1 decoding, etc.).
+ */
+SECStatus
+CERT_CheckOCSPStatus(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRTime time, void *pwArg)
+{
+ CERTOCSPCertID *certID;
+ PRBool certIDWasConsumed = PR_FALSE;
+ SECStatus rv;
+ SECStatus rvOcsp;
+ SECErrorCodes cachedErrorCode;
+ OCSPFreshness cachedResponseFreshness;
+
+ OCSP_TRACE_CERT(cert);
+ OCSP_TRACE_TIME("## requested validity time:", time);
+
+ certID = CERT_CreateOCSPCertID(cert, time);
+ if (!certID)
+ return SECFailure;
+ rv = ocsp_GetCachedOCSPResponseStatus(
+ certID, time, PR_FALSE, /* ignoreGlobalOcspFailureSetting */
+ &rvOcsp, &cachedErrorCode, &cachedResponseFreshness);
+ if (rv != SECSuccess) {
+ CERT_DestroyOCSPCertID(certID);
+ return SECFailure;
+ }
+ if (cachedResponseFreshness == ocspFresh) {
+ CERT_DestroyOCSPCertID(certID);
+ if (rvOcsp != SECSuccess) {
+ PORT_SetError(cachedErrorCode);
+ }
+ return rvOcsp;
+ }
+
+ rv = ocsp_GetOCSPStatusFromNetwork(handle, certID, cert, time, pwArg,
+ &certIDWasConsumed,
+ &rvOcsp);
+ if (rv != SECSuccess) {
+ PRErrorCode err = PORT_GetError();
+ if (ocsp_FetchingFailureIsVerificationFailure()) {
+ PORT_SetError(err);
+ rvOcsp = SECFailure;
+ } else if (cachedResponseFreshness == ocspStale &&
+ (cachedErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT ||
+ cachedErrorCode == SEC_ERROR_REVOKED_CERTIFICATE)) {
+ /* If we couldn't get a response for a certificate that the OCSP
+ * responder previously told us was bad, then assume it is still
+ * bad until we hear otherwise, as it is very unlikely that the
+ * certificate status has changed from "revoked" to "good" and it
+ * is also unlikely that the certificate status has changed from
+ * "unknown" to "good", except for some buggy OCSP responders.
+ */
+ PORT_SetError(cachedErrorCode);
+ rvOcsp = SECFailure;
+ } else {
+ rvOcsp = SECSuccess;
+ }
+ }
+ if (!certIDWasConsumed) {
+ CERT_DestroyOCSPCertID(certID);
+ }
+ return rvOcsp;
+}
+
+/*
+ * FUNCTION: CERT_CacheOCSPResponseFromSideChannel
+ * First, this function checks the OCSP cache to see if a good response
+ * for the given certificate already exists. If it does, then the function
+ * returns successfully.
+ *
+ * If not, then it validates that the given OCSP response is a valid,
+ * good response for the given certificate and inserts it into the
+ * cache.
+ *
+ * This function is intended for use when OCSP responses are provided via a
+ * side-channel, i.e. TLS OCSP stapling (a.k.a. the status_request extension).
+ *
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * certificate DB of the cert that is being checked
+ * CERTCertificate *cert
+ * the certificate being checked
+ * PRTime time
+ * time for which status is to be determined
+ * SECItem *encodedResponse
+ * the DER encoded bytes of the OCSP response
+ * void *pwArg
+ * argument for password prompting, if needed
+ * RETURN:
+ * SECSuccess if the cert was found in the cache, or if the OCSP response was
+ * found to be valid and inserted into the cache. SECFailure otherwise.
+ */
+SECStatus
+CERT_CacheOCSPResponseFromSideChannel(CERTCertDBHandle *handle,
+ CERTCertificate *cert,
+ PRTime time,
+ const SECItem *encodedResponse,
+ void *pwArg)
+{
+ CERTOCSPCertID *certID = NULL;
+ PRBool certIDWasConsumed = PR_FALSE;
+ SECStatus rv = SECFailure;
+ SECStatus rvOcsp = SECFailure;
+ SECErrorCodes dummy_error_code; /* we ignore this */
+ CERTOCSPResponse *decodedResponse = NULL;
+ CERTOCSPSingleResponse *singleResponse = NULL;
+ OCSPFreshness freshness;
+
+ /* The OCSP cache can be in three states regarding this certificate:
+ * + Good (cached, timely, 'good' response, or revoked in the future)
+ * + Revoked (cached, timely, but doesn't fit in the last category)
+ * + Miss (no knowledge)
+ *
+ * Likewise, the side-channel information can be
+ * + Good (timely, 'good' response, or revoked in the future)
+ * + Revoked (timely, but doesn't fit in the last category)
+ * + Invalid (bad syntax, bad signature, not timely etc)
+ *
+ * The common case is that the cache result is Good and so is the
+ * side-channel information. We want to save processing time in this case
+ * so we say that any time we see a Good result from the cache we return
+ * early.
+ *
+ * Cache result
+ * | Good Revoked Miss
+ * ---+--------------------------------------------
+ * G | noop Cache more Cache it
+ * S | recent result
+ * i |
+ * d |
+ * e |
+ * R | noop Cache more Cache it
+ * C | recent result
+ * h |
+ * a |
+ * n |
+ * n I | noop Noop Noop
+ * e |
+ * l |
+ *
+ * When we fetch from the network we might choose to cache a negative
+ * result when the response is invalid. This saves us hammering, uselessly,
+ * at a broken responder. However, side channels are commonly attacker
+ * controlled and so we must not cache a negative result for an Invalid
+ * side channel.
+ */
+
+ if (!cert || !encodedResponse) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ certID = CERT_CreateOCSPCertID(cert, time);
+ if (!certID)
+ return SECFailure;
+
+ /* We pass PR_TRUE for ignoreGlobalOcspFailureSetting so that a cached
+ * error entry is not interpreted as being a 'Good' entry here.
+ */
+ rv = ocsp_GetCachedOCSPResponseStatus(
+ certID, time, PR_TRUE, /* ignoreGlobalOcspFailureSetting */
+ &rvOcsp, &dummy_error_code, &freshness);
+ if (rv == SECSuccess && rvOcsp == SECSuccess && freshness == ocspFresh) {
+ /* The cached value is good. We don't want to waste time validating
+ * this OCSP response. This is the first column in the table above. */
+ CERT_DestroyOCSPCertID(certID);
+ return rv;
+ }
+
+ /* The logic for caching the more recent response is handled in
+ * ocsp_CacheSingleResponse. */
+
+ rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert,
+ time, pwArg,
+ encodedResponse,
+ &decodedResponse,
+ &singleResponse);
+ if (rv == SECSuccess) {
+ rvOcsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time);
+ /* Cache any valid singleResponse, regardless of status. */
+ ocsp_CacheSingleResponse(certID, singleResponse, &certIDWasConsumed);
+ }
+ if (decodedResponse) {
+ CERT_DestroyOCSPResponse(decodedResponse);
+ }
+ if (!certIDWasConsumed) {
+ CERT_DestroyOCSPCertID(certID);
+ }
+ return rv == SECSuccess ? rvOcsp : rv;
+}
+
+/*
+ * Status in *certIDWasConsumed will always be correct, regardless of
+ * return value.
+ */
+static SECStatus
+ocsp_GetOCSPStatusFromNetwork(CERTCertDBHandle *handle,
+ CERTOCSPCertID *certID,
+ CERTCertificate *cert,
+ PRTime time,
+ void *pwArg,
+ PRBool *certIDWasConsumed,
+ SECStatus *rv_ocsp)
+{
+ char *location = NULL;
+ PRBool locationIsDefault;
+ SECItem *encodedResponse = NULL;
+ CERTOCSPRequest *request = NULL;
+ SECStatus rv = SECFailure;
+
+ CERTOCSPResponse *decodedResponse = NULL;
+ CERTOCSPSingleResponse *singleResponse = NULL;
+ enum { stageGET,
+ stagePOST } currentStage;
+ PRBool retry = PR_FALSE;
+
+ if (!certIDWasConsumed || !rv_ocsp) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ *certIDWasConsumed = PR_FALSE;
+ *rv_ocsp = SECFailure;
+
+ if (!OCSP_Global.monitor) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+ PR_EnterMonitor(OCSP_Global.monitor);
+ if (OCSP_Global.forcePost) {
+ currentStage = stagePOST;
+ } else {
+ currentStage = stageGET;
+ }
+ PR_ExitMonitor(OCSP_Global.monitor);
+
+ /*
+ * The first thing we need to do is find the location of the responder.
+ * This will be the value of the default responder (if enabled), else
+ * it will come out of the AIA extension in the cert (if present).
+ * If we have no such location, then this cert does not "deserve" to
+ * be checked -- that is, we consider it a success and just return.
+ * The way we tell that is by looking at the error number to see if
+ * the problem was no AIA extension was found; any other error was
+ * a true failure that we unfortunately have to treat as an overall
+ * failure here.
+ */
+ location = ocsp_GetResponderLocation(handle, cert, PR_TRUE,
+ &locationIsDefault);
+ if (location == NULL) {
+ int err = PORT_GetError();
+ if (err == SEC_ERROR_EXTENSION_NOT_FOUND ||
+ err == SEC_ERROR_CERT_BAD_ACCESS_LOCATION) {
+ PORT_SetError(0);
+ *rv_ocsp = SECSuccess;
+ return SECSuccess;
+ }
+ return SECFailure;
+ }
+
+ /*
+ * XXX In the fullness of time, we will want/need to handle a
+ * certificate chain. This will be done either when a new parameter
+ * tells us to, or some configuration variable tells us to. In any
+ * case, handling it is complicated because we may need to send as
+ * many requests (and receive as many responses) as we have certs
+ * in the chain. If we are going to talk to a default responder,
+ * and we only support one default responder, we can put all of the
+ * certs together into one request. Otherwise, we must break them up
+ * into multiple requests. (Even if all of the requests will go to
+ * the same location, the signature on each response will be different,
+ * because each issuer is different. Carefully read the OCSP spec
+ * if you do not understand this.)
+ */
+
+ /*
+ * XXX If/when signing of requests is supported, that second NULL
+ * should be changed to be the signer certificate. Not sure if that
+ * should be passed into this function or retrieved via some operation
+ * on the handle/context.
+ */
+
+ do {
+ const char *method;
+ PRBool validResponseWithAccurateInfo = PR_FALSE;
+ retry = PR_FALSE;
+ *rv_ocsp = SECFailure;
+
+ if (currentStage == stageGET) {
+ method = "GET";
+ } else {
+ PORT_Assert(currentStage == stagePOST);
+ method = "POST";
+ }
+
+ encodedResponse =
+ ocsp_GetEncodedOCSPResponseForSingleCert(NULL, certID, cert,
+ location, method,
+ time, locationIsDefault,
+ pwArg, &request);
+
+ if (encodedResponse) {
+ rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert,
+ time, pwArg,
+ encodedResponse,
+ &decodedResponse,
+ &singleResponse);
+ if (rv == SECSuccess) {
+ switch (singleResponse->certStatus->certStatusType) {
+ case ocspCertStatus_good:
+ case ocspCertStatus_revoked:
+ validResponseWithAccurateInfo = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ *rv_ocsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time);
+ }
+ }
+
+ if (currentStage == stageGET) {
+ /* only accept GET response if good or revoked */
+ if (validResponseWithAccurateInfo) {
+ ocsp_CacheSingleResponse(certID, singleResponse,
+ certIDWasConsumed);
+ } else {
+ retry = PR_TRUE;
+ currentStage = stagePOST;
+ }
+ } else {
+ /* cache the POST respone, regardless of status */
+ if (!singleResponse) {
+ cert_RememberOCSPProcessingFailure(certID, certIDWasConsumed);
+ } else {
+ ocsp_CacheSingleResponse(certID, singleResponse,
+ certIDWasConsumed);
+ }
+ }
+
+ if (encodedResponse) {
+ SECITEM_FreeItem(encodedResponse, PR_TRUE);
+ encodedResponse = NULL;
+ }
+ if (request) {
+ CERT_DestroyOCSPRequest(request);
+ request = NULL;
+ }
+ if (decodedResponse) {
+ CERT_DestroyOCSPResponse(decodedResponse);
+ decodedResponse = NULL;
+ }
+ singleResponse = NULL;
+
+ } while (retry);
+
+ PORT_Free(location);
+ return rv;
+}
+
+/*
+ * FUNCTION: ocsp_GetDecodedVerifiedSingleResponseForID
+ * This function decodes an OCSP response and checks for a valid response
+ * concerning the given certificate.
+ *
+ * Note: a 'valid' response is one that parses successfully, is not an OCSP
+ * exception (see RFC 2560 Section 2.3), is correctly signed and is current.
+ * A 'good' response is a valid response that attests that the certificate
+ * is not currently revoked (see RFC 2560 Section 2.2).
+ *
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * certificate DB of the cert that is being checked
+ * CERTOCSPCertID *certID
+ * the cert ID corresponding to |cert|
+ * CERTCertificate *cert
+ * the certificate being checked
+ * PRTime time
+ * time for which status is to be determined
+ * void *pwArg
+ * the opaque argument to the password prompting function.
+ * SECItem *encodedResponse
+ * the DER encoded bytes of the OCSP response
+ * CERTOCSPResponse **pDecodedResponse
+ * (output) The caller must ALWAYS check for this output parameter,
+ * and if it's non-null, must destroy it using CERT_DestroyOCSPResponse.
+ * CERTOCSPSingleResponse **pSingle
+ * (output) on success, this points to the single response that corresponds
+ * to the certID parameter. Points to the inside of pDecodedResponse.
+ * It isn't a copy, don't free it.
+ * RETURN:
+ * SECSuccess iff the response is valid.
+ */
+static SECStatus
+ocsp_GetDecodedVerifiedSingleResponseForID(CERTCertDBHandle *handle,
+ CERTOCSPCertID *certID,
+ CERTCertificate *cert,
+ PRTime time,
+ void *pwArg,
+ const SECItem *encodedResponse,
+ CERTOCSPResponse **pDecodedResponse,
+ CERTOCSPSingleResponse **pSingle)
+{
+ CERTCertificate *signerCert = NULL;
+ CERTCertificate *issuerCert = NULL;
+ SECStatus rv = SECFailure;
+
+ if (!pSingle || !pDecodedResponse) {
+ return SECFailure;
+ }
+ *pSingle = NULL;
+ *pDecodedResponse = CERT_DecodeOCSPResponse(encodedResponse);
+ if (!*pDecodedResponse) {
+ return SECFailure;
+ }
+
+ /*
+ * Okay, we at least have a response that *looks* like a response!
+ * Now see if the overall response status value is good or not.
+ * If not, we set an error and give up. (It means that either the
+ * server had a problem, or it didn't like something about our
+ * request. Either way there is nothing to do but give up.)
+ * Otherwise, we continue to find the actual per-cert status
+ * in the response.
+ */
+ if (CERT_GetOCSPResponseStatus(*pDecodedResponse) != SECSuccess) {
+ goto loser;
+ }
+
+ /*
+ * If we've made it this far, we expect a response with a good signature.
+ * So, check for that.
+ */
+ issuerCert = CERT_FindCertIssuer(cert, time, certUsageAnyCA);
+ rv = CERT_VerifyOCSPResponseSignature(*pDecodedResponse, handle, pwArg,
+ &signerCert, issuerCert);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ PORT_Assert(signerCert != NULL); /* internal consistency check */
+ /* XXX probably should set error, return failure if signerCert is null */
+
+ /*
+ * Again, we are only doing one request for one cert.
+ * XXX When we handle cert chains, the following code will obviously
+ * have to be modified, in coordation with the code above that will
+ * have to determine how to make multiple requests, etc.
+ */
+ rv = ocsp_GetVerifiedSingleResponseForCertID(handle, *pDecodedResponse, certID,
+ signerCert, time, pSingle);
+loser:
+ if (issuerCert != NULL)
+ CERT_DestroyCertificate(issuerCert);
+ if (signerCert != NULL)
+ CERT_DestroyCertificate(signerCert);
+ return rv;
+}
+
+/*
+ * FUNCTION: ocsp_CacheSingleResponse
+ * This function requires that the caller has checked that the response
+ * is valid and verified.
+ * The (positive or negative) valid response will be used to update the cache.
+ * INPUTS:
+ * CERTOCSPCertID *certID
+ * the cert ID corresponding to |cert|
+ * PRBool *certIDWasConsumed
+ * (output) on return, this is true iff |certID| was consumed by this
+ * function.
+ */
+void
+ocsp_CacheSingleResponse(CERTOCSPCertID *certID,
+ CERTOCSPSingleResponse *single,
+ PRBool *certIDWasConsumed)
+{
+ if (single != NULL) {
+ PR_EnterMonitor(OCSP_Global.monitor);
+ if (OCSP_Global.maxCacheEntries >= 0) {
+ ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, single,
+ certIDWasConsumed);
+ /* ignore cache update failures */
+ }
+ PR_ExitMonitor(OCSP_Global.monitor);
+ }
+}
+
+SECStatus
+ocsp_GetVerifiedSingleResponseForCertID(CERTCertDBHandle *handle,
+ CERTOCSPResponse *response,
+ CERTOCSPCertID *certID,
+ CERTCertificate *signerCert,
+ PRTime time,
+ CERTOCSPSingleResponse
+ **pSingleResponse)
+{
+ SECStatus rv;
+ ocspResponseData *responseData;
+ PRTime producedAt;
+ CERTOCSPSingleResponse *single;
+
+ /*
+ * The ResponseData part is the real guts of the response.
+ */
+ responseData = ocsp_GetResponseData(response, NULL);
+ if (responseData == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /*
+ * There is one producedAt time for the entire response (and a separate
+ * thisUpdate time for each individual single response). We need to
+ * compare them, so get the overall time to pass into the check of each
+ * single response.
+ */
+ rv = DER_GeneralizedTimeToTime(&producedAt, &responseData->producedAt);
+ if (rv != SECSuccess)
+ goto loser;
+
+ single = ocsp_GetSingleResponseForCertID(responseData->responses,
+ handle, certID);
+ if (single == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = ocsp_VerifySingleResponse(single, handle, signerCert, producedAt);
+ if (rv != SECSuccess)
+ goto loser;
+ *pSingleResponse = single;
+
+loser:
+ return rv;
+}
+
+SECStatus
+CERT_GetOCSPStatusForCertID(CERTCertDBHandle *handle,
+ CERTOCSPResponse *response,
+ CERTOCSPCertID *certID,
+ CERTCertificate *signerCert,
+ PRTime time)
+{
+ /*
+ * We do not update the cache, because:
+ *
+ * CERT_GetOCSPStatusForCertID is an old exported API that was introduced
+ * before the OCSP cache got implemented.
+ *
+ * The implementation of helper function cert_ProcessOCSPResponse
+ * requires the ability to transfer ownership of the the given certID to
+ * the cache. The external API doesn't allow us to prevent the caller from
+ * destroying the certID. We don't have the original certificate available,
+ * therefore we are unable to produce another certID object (that could
+ * be stored in the cache).
+ *
+ * Should we ever implement code to produce a deep copy of certID,
+ * then this could be changed to allow updating the cache.
+ * The duplication would have to be done in
+ * cert_ProcessOCSPResponse, if the out parameter to indicate
+ * a transfer of ownership is NULL.
+ */
+ return cert_ProcessOCSPResponse(handle, response, certID,
+ signerCert, time,
+ NULL, NULL);
+}
+
+/*
+ * The first 5 parameters match the definition of CERT_GetOCSPStatusForCertID.
+ */
+SECStatus
+cert_ProcessOCSPResponse(CERTCertDBHandle *handle,
+ CERTOCSPResponse *response,
+ CERTOCSPCertID *certID,
+ CERTCertificate *signerCert,
+ PRTime time,
+ PRBool *certIDWasConsumed,
+ SECStatus *cacheUpdateStatus)
+{
+ SECStatus rv;
+ SECStatus rv_cache = SECSuccess;
+ CERTOCSPSingleResponse *single = NULL;
+
+ rv = ocsp_GetVerifiedSingleResponseForCertID(handle, response, certID,
+ signerCert, time, &single);
+ if (rv == SECSuccess) {
+ /*
+ * Check whether the status says revoked, and if so
+ * how that compares to the time value passed into this routine.
+ */
+ rv = ocsp_SingleResponseCertHasGoodStatus(single, time);
+ }
+
+ if (certIDWasConsumed) {
+ /*
+ * We don't have copy-of-certid implemented. In order to update
+ * the cache, the caller must supply an out variable
+ * certIDWasConsumed, allowing us to return ownership status.
+ */
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ if (OCSP_Global.maxCacheEntries >= 0) {
+ /* single == NULL means: remember response failure */
+ rv_cache =
+ ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID,
+ single, certIDWasConsumed);
+ }
+ PR_ExitMonitor(OCSP_Global.monitor);
+ if (cacheUpdateStatus) {
+ *cacheUpdateStatus = rv_cache;
+ }
+ }
+
+ return rv;
+}
+
+SECStatus
+cert_RememberOCSPProcessingFailure(CERTOCSPCertID *certID,
+ PRBool *certIDWasConsumed)
+{
+ SECStatus rv = SECSuccess;
+ PR_EnterMonitor(OCSP_Global.monitor);
+ if (OCSP_Global.maxCacheEntries >= 0) {
+ rv = ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, NULL,
+ certIDWasConsumed);
+ }
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return rv;
+}
+
+/*
+ * Disable status checking and destroy related structures/data.
+ */
+static SECStatus
+ocsp_DestroyStatusChecking(CERTStatusConfig *statusConfig)
+{
+ ocspCheckingContext *statusContext;
+
+ /*
+ * Disable OCSP checking
+ */
+ statusConfig->statusChecker = NULL;
+
+ statusContext = statusConfig->statusContext;
+ PORT_Assert(statusContext != NULL);
+ if (statusContext == NULL)
+ return SECFailure;
+
+ if (statusContext->defaultResponderURI != NULL)
+ PORT_Free(statusContext->defaultResponderURI);
+ if (statusContext->defaultResponderNickname != NULL)
+ PORT_Free(statusContext->defaultResponderNickname);
+
+ PORT_Free(statusContext);
+ statusConfig->statusContext = NULL;
+
+ PORT_Free(statusConfig);
+
+ return SECSuccess;
+}
+
+/*
+ * FUNCTION: CERT_DisableOCSPChecking
+ * Turns off OCSP checking for the given certificate database.
+ * This routine disables OCSP checking. Though it will return
+ * SECFailure if OCSP checking is not enabled, it is "safe" to
+ * call it that way and just ignore the return value, if it is
+ * easier to just call it than to "remember" whether it is enabled.
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * Certificate database for which OCSP checking will be disabled.
+ * RETURN:
+ * Returns SECFailure if an error occurred (usually means that OCSP
+ * checking was not enabled or status contexts were not initialized --
+ * error set will be SEC_ERROR_OCSP_NOT_ENABLED); SECSuccess otherwise.
+ */
+SECStatus
+CERT_DisableOCSPChecking(CERTCertDBHandle *handle)
+{
+ CERTStatusConfig *statusConfig;
+ ocspCheckingContext *statusContext;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ statusConfig = CERT_GetStatusConfig(handle);
+ statusContext = ocsp_GetCheckingContext(handle);
+ if (statusContext == NULL)
+ return SECFailure;
+
+ if (statusConfig->statusChecker != CERT_CheckOCSPStatus) {
+ /*
+ * Status configuration is present, but either not currently
+ * enabled or not for OCSP.
+ */
+ PORT_SetError(SEC_ERROR_OCSP_NOT_ENABLED);
+ return SECFailure;
+ }
+
+ /* cache no longer necessary */
+ CERT_ClearOCSPCache();
+
+ /*
+ * This is how we disable status checking. Everything else remains
+ * in place in case we are enabled again.
+ */
+ statusConfig->statusChecker = NULL;
+
+ return SECSuccess;
+}
+
+/*
+ * Allocate and initialize the informational structures for status checking.
+ * This is done when some configuration of OCSP is being done or when OCSP
+ * checking is being turned on, whichever comes first.
+ */
+static SECStatus
+ocsp_InitStatusChecking(CERTCertDBHandle *handle)
+{
+ CERTStatusConfig *statusConfig = NULL;
+ ocspCheckingContext *statusContext = NULL;
+
+ PORT_Assert(CERT_GetStatusConfig(handle) == NULL);
+ if (CERT_GetStatusConfig(handle) != NULL) {
+ /* XXX or call statusConfig->statusDestroy and continue? */
+ return SECFailure;
+ }
+
+ statusConfig = PORT_ZNew(CERTStatusConfig);
+ if (statusConfig == NULL)
+ goto loser;
+
+ statusContext = PORT_ZNew(ocspCheckingContext);
+ if (statusContext == NULL)
+ goto loser;
+
+ statusConfig->statusDestroy = ocsp_DestroyStatusChecking;
+ statusConfig->statusContext = statusContext;
+
+ CERT_SetStatusConfig(handle, statusConfig);
+
+ return SECSuccess;
+
+loser:
+ if (statusConfig != NULL)
+ PORT_Free(statusConfig);
+ return SECFailure;
+}
+
+/*
+ * FUNCTION: CERT_EnableOCSPChecking
+ * Turns on OCSP checking for the given certificate database.
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * Certificate database for which OCSP checking will be enabled.
+ * RETURN:
+ * Returns SECFailure if an error occurred (likely only problem
+ * allocating memory); SECSuccess otherwise.
+ */
+SECStatus
+CERT_EnableOCSPChecking(CERTCertDBHandle *handle)
+{
+ CERTStatusConfig *statusConfig;
+
+ SECStatus rv;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ statusConfig = CERT_GetStatusConfig(handle);
+ if (statusConfig == NULL) {
+ rv = ocsp_InitStatusChecking(handle);
+ if (rv != SECSuccess)
+ return rv;
+
+ /* Get newly established value */
+ statusConfig = CERT_GetStatusConfig(handle);
+ PORT_Assert(statusConfig != NULL);
+ }
+
+ /*
+ * Setting the checker function is what really enables the checking
+ * when each cert verification is done.
+ */
+ statusConfig->statusChecker = CERT_CheckOCSPStatus;
+
+ return SECSuccess;
+}
+
+/*
+ * FUNCTION: CERT_SetOCSPDefaultResponder
+ * Specify the location and cert of the default responder.
+ * If OCSP checking is already enabled *and* use of a default responder
+ * is also already enabled, all OCSP checking from now on will go directly
+ * to the specified responder. If OCSP checking is not enabled, or if
+ * it is but use of a default responder is not enabled, the information
+ * will be recorded and take effect whenever both are enabled.
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * Cert database on which OCSP checking should use the default responder.
+ * char *url
+ * The location of the default responder (e.g. "http://foo.com:80/ocsp")
+ * Note that the location will not be tested until the first attempt
+ * to send a request there.
+ * char *name
+ * The nickname of the cert to trust (expected) to sign the OCSP responses.
+ * If the corresponding cert cannot be found, SECFailure is returned.
+ * RETURN:
+ * Returns SECFailure if an error occurred; SECSuccess otherwise.
+ * The most likely error is that the cert for "name" could not be found
+ * (probably SEC_ERROR_UNKNOWN_CERT). Other errors are low-level (no memory,
+ * bad database, etc.).
+ */
+SECStatus
+CERT_SetOCSPDefaultResponder(CERTCertDBHandle *handle,
+ const char *url, const char *name)
+{
+ CERTCertificate *cert;
+ ocspCheckingContext *statusContext;
+ char *url_copy = NULL;
+ char *name_copy = NULL;
+ SECStatus rv;
+
+ if (handle == NULL || url == NULL || name == NULL) {
+ /*
+ * XXX When interface is exported, probably want better errors;
+ * perhaps different one for each parameter.
+ */
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /*
+ * Find the certificate for the specified nickname. Do this first
+ * because it seems the most likely to fail.
+ *
+ * XXX Shouldn't need that cast if the FindCertByNickname interface
+ * used const to convey that it does not modify the name. Maybe someday.
+ */
+ cert = CERT_FindCertByNickname(handle, (char *)name);
+ if (cert == NULL) {
+ /*
+ * look for the cert on an external token.
+ */
+ cert = PK11_FindCertFromNickname((char *)name, NULL);
+ }
+ if (cert == NULL)
+ return SECFailure;
+
+ /*
+ * Make a copy of the url and nickname.
+ */
+ url_copy = PORT_Strdup(url);
+ name_copy = PORT_Strdup(name);
+ if (url_copy == NULL || name_copy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ statusContext = ocsp_GetCheckingContext(handle);
+
+ /*
+ * Allocate and init the context if it doesn't already exist.
+ */
+ if (statusContext == NULL) {
+ rv = ocsp_InitStatusChecking(handle);
+ if (rv != SECSuccess)
+ goto loser;
+
+ statusContext = ocsp_GetCheckingContext(handle);
+ PORT_Assert(statusContext != NULL); /* extreme paranoia */
+ }
+
+ /*
+ * Note -- we do not touch the status context until after all of
+ * the steps which could cause errors. If something goes wrong,
+ * we want to leave things as they were.
+ */
+
+ /*
+ * Get rid of old url and name if there.
+ */
+ if (statusContext->defaultResponderNickname != NULL)
+ PORT_Free(statusContext->defaultResponderNickname);
+ if (statusContext->defaultResponderURI != NULL)
+ PORT_Free(statusContext->defaultResponderURI);
+
+ /*
+ * And replace them with the new ones.
+ */
+ statusContext->defaultResponderURI = url_copy;
+ statusContext->defaultResponderNickname = name_copy;
+
+ /*
+ * If there was already a cert in place, get rid of it and replace it.
+ * Otherwise, we are not currently enabled, so we don't want to save it;
+ * it will get re-found and set whenever use of a default responder is
+ * enabled.
+ */
+ if (statusContext->defaultResponderCert != NULL) {
+ CERT_DestroyCertificate(statusContext->defaultResponderCert);
+ statusContext->defaultResponderCert = cert;
+ /*OCSP enabled, switching responder: clear cache*/
+ CERT_ClearOCSPCache();
+ } else {
+ PORT_Assert(statusContext->useDefaultResponder == PR_FALSE);
+ CERT_DestroyCertificate(cert);
+ /*OCSP currently not enabled, no need to clear cache*/
+ }
+
+ return SECSuccess;
+
+loser:
+ CERT_DestroyCertificate(cert);
+ if (url_copy != NULL)
+ PORT_Free(url_copy);
+ if (name_copy != NULL)
+ PORT_Free(name_copy);
+ return rv;
+}
+
+/*
+ * FUNCTION: CERT_EnableOCSPDefaultResponder
+ * Turns on use of a default responder when OCSP checking.
+ * If OCSP checking is already enabled, this will make subsequent checks
+ * go directly to the default responder. (The location of the responder
+ * and the nickname of the responder cert must already be specified.)
+ * If OCSP checking is not enabled, this will be recorded and take effect
+ * whenever it is enabled.
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * Cert database on which OCSP checking should use the default responder.
+ * RETURN:
+ * Returns SECFailure if an error occurred; SECSuccess otherwise.
+ * No errors are especially likely unless the caller did not previously
+ * perform a successful call to SetOCSPDefaultResponder (in which case
+ * the error set will be SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER).
+ */
+SECStatus
+CERT_EnableOCSPDefaultResponder(CERTCertDBHandle *handle)
+{
+ ocspCheckingContext *statusContext;
+ CERTCertificate *cert;
+ SECStatus rv;
+ SECCertificateUsage usage;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ statusContext = ocsp_GetCheckingContext(handle);
+
+ if (statusContext == NULL) {
+ /*
+ * Strictly speaking, the error already set is "correct",
+ * but cover over it with one more helpful in this context.
+ */
+ PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER);
+ return SECFailure;
+ }
+
+ if (statusContext->defaultResponderURI == NULL) {
+ PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER);
+ return SECFailure;
+ }
+
+ if (statusContext->defaultResponderNickname == NULL) {
+ PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER);
+ return SECFailure;
+ }
+
+ /*
+ * Find the cert for the nickname.
+ */
+ cert = CERT_FindCertByNickname(handle,
+ statusContext->defaultResponderNickname);
+ if (cert == NULL) {
+ cert = PK11_FindCertFromNickname(statusContext->defaultResponderNickname,
+ NULL);
+ }
+ /*
+ * We should never have trouble finding the cert, because its
+ * existence should have been proven by SetOCSPDefaultResponder.
+ */
+ PORT_Assert(cert != NULL);
+ if (cert == NULL)
+ return SECFailure;
+
+ /*
+ * Supplied cert should at least have a signing capability in order for us
+ * to use it as a trusted responder cert. Ability to sign is guaranteed if
+ * cert is validated to have any set of the usages below.
+ */
+ rv = CERT_VerifyCertificateNow(handle, cert, PR_TRUE,
+ certificateUsageCheckAllUsages,
+ NULL, &usage);
+ if (rv != SECSuccess || (usage & (certificateUsageSSLClient | certificateUsageSSLServer | certificateUsageSSLServerWithStepUp | certificateUsageEmailSigner | certificateUsageObjectSigner | certificateUsageStatusResponder | certificateUsageSSLCA)) == 0) {
+ PORT_SetError(SEC_ERROR_OCSP_RESPONDER_CERT_INVALID);
+ return SECFailure;
+ }
+
+ /*
+ * And hang onto it.
+ */
+ statusContext->defaultResponderCert = cert;
+
+ /* we don't allow a mix of cache entries from different responders */
+ CERT_ClearOCSPCache();
+
+ /*
+ * Finally, record the fact that we now have a default responder enabled.
+ */
+ statusContext->useDefaultResponder = PR_TRUE;
+ return SECSuccess;
+}
+
+/*
+ * FUNCTION: CERT_DisableOCSPDefaultResponder
+ * Turns off use of a default responder when OCSP checking.
+ * (Does nothing if use of a default responder is not enabled.)
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * Cert database on which OCSP checking should stop using a default
+ * responder.
+ * RETURN:
+ * Returns SECFailure if an error occurred; SECSuccess otherwise.
+ * Errors very unlikely (like random memory corruption...).
+ */
+SECStatus
+CERT_DisableOCSPDefaultResponder(CERTCertDBHandle *handle)
+{
+ CERTStatusConfig *statusConfig;
+ ocspCheckingContext *statusContext;
+ CERTCertificate *tmpCert;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ statusConfig = CERT_GetStatusConfig(handle);
+ if (statusConfig == NULL)
+ return SECSuccess;
+
+ statusContext = ocsp_GetCheckingContext(handle);
+ PORT_Assert(statusContext != NULL);
+ if (statusContext == NULL)
+ return SECFailure;
+
+ tmpCert = statusContext->defaultResponderCert;
+ if (tmpCert) {
+ statusContext->defaultResponderCert = NULL;
+ CERT_DestroyCertificate(tmpCert);
+ /* we don't allow a mix of cache entries from different responders */
+ CERT_ClearOCSPCache();
+ }
+
+ /*
+ * Finally, record the fact.
+ */
+ statusContext->useDefaultResponder = PR_FALSE;
+ return SECSuccess;
+}
+
+SECStatus
+CERT_ForcePostMethodForOCSP(PRBool forcePost)
+{
+ if (!OCSP_Global.monitor) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+
+ PR_EnterMonitor(OCSP_Global.monitor);
+ OCSP_Global.forcePost = forcePost;
+ PR_ExitMonitor(OCSP_Global.monitor);
+
+ return SECSuccess;
+}
+
+SECStatus
+CERT_GetOCSPResponseStatus(CERTOCSPResponse *response)
+{
+ PORT_Assert(response);
+ if (response->statusValue == ocspResponse_successful)
+ return SECSuccess;
+
+ switch (response->statusValue) {
+ case ocspResponse_malformedRequest:
+ PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST);
+ break;
+ case ocspResponse_internalError:
+ PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR);
+ break;
+ case ocspResponse_tryLater:
+ PORT_SetError(SEC_ERROR_OCSP_TRY_SERVER_LATER);
+ break;
+ case ocspResponse_sigRequired:
+ /* XXX We *should* retry with a signature, if possible. */
+ PORT_SetError(SEC_ERROR_OCSP_REQUEST_NEEDS_SIG);
+ break;
+ case ocspResponse_unauthorized:
+ PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST);
+ break;
+ case ocspResponse_unused:
+ default:
+ PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS);
+ break;
+ }
+ return SECFailure;
+}
diff --git a/security/nss/lib/certhigh/ocsp.h b/security/nss/lib/certhigh/ocsp.h
new file mode 100644
index 0000000000..1b94aec2e3
--- /dev/null
+++ b/security/nss/lib/certhigh/ocsp.h
@@ -0,0 +1,723 @@
+/* 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/. */
+
+/*
+ * Interface to the OCSP implementation.
+ */
+
+#ifndef _OCSP_H_
+#define _OCSP_H_
+
+#include "plarena.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "keythi.h"
+#include "certt.h"
+#include "ocspt.h"
+
+/************************************************************************/
+SEC_BEGIN_PROTOS
+
+/*
+ * This function registers the HttpClient with whose functions the
+ * HttpClientFcn structure has been populated as the default Http
+ * client.
+ *
+ * The function table must be a global object.
+ * The caller must ensure that NSS will be able to call
+ * the registered functions for the lifetime of the process.
+ */
+extern SECStatus
+SEC_RegisterDefaultHttpClient(const SEC_HttpClientFcn *fcnTable);
+
+/*
+ * This function obtains the HttpClient which has been registered
+ * by an earlier call to SEC_RegisterDefaultHttpClient.
+ */
+extern const SEC_HttpClientFcn *
+SEC_GetRegisteredHttpClient(void);
+
+/*
+ * Sets parameters that control NSS' internal OCSP cache.
+ * maxCacheEntries, special varlues are:
+ * -1 disable cache
+ * 0 unlimited cache entries
+ * minimumSecondsToNextFetchAttempt:
+ * whenever an OCSP request was attempted or completed over the network,
+ * wait at least this number of seconds before trying to fetch again.
+ * maximumSecondsToNextFetchAttempt:
+ * this is the maximum age of a cached response we allow, until we try
+ * to fetch an updated response, even if the OCSP responder expects
+ * that newer information update will not be available yet.
+ */
+extern SECStatus
+CERT_OCSPCacheSettings(PRInt32 maxCacheEntries,
+ PRUint32 minimumSecondsToNextFetchAttempt,
+ PRUint32 maximumSecondsToNextFetchAttempt);
+
+/*
+ * Set the desired behaviour on OCSP failures.
+ * See definition of ocspFailureMode for allowed choices.
+ */
+extern SECStatus
+CERT_SetOCSPFailureMode(SEC_OcspFailureMode ocspFailureMode);
+
+/*
+ * Configure the maximum time NSS will wait for an OCSP response.
+ */
+extern SECStatus
+CERT_SetOCSPTimeout(PRUint32 seconds);
+
+/*
+ * Removes all items currently stored in the OCSP cache.
+ */
+extern SECStatus
+CERT_ClearOCSPCache(void);
+
+/*
+ * FUNCTION: CERT_EnableOCSPChecking
+ * Turns on OCSP checking for the given certificate database.
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * Certificate database for which OCSP checking will be enabled.
+ * RETURN:
+ * Returns SECFailure if an error occurred (likely only problem
+ * allocating memory); SECSuccess otherwise.
+ */
+extern SECStatus
+CERT_EnableOCSPChecking(CERTCertDBHandle *handle);
+
+/*
+ * FUNCTION: CERT_DisableOCSPChecking
+ * Turns off OCSP checking for the given certificate database.
+ * This routine disables OCSP checking. Though it will return
+ * SECFailure if OCSP checking is not enabled, it is "safe" to
+ * call it that way and just ignore the return value, if it is
+ * easier to just call it than to "remember" whether it is enabled.
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * Certificate database for which OCSP checking will be disabled.
+ * RETURN:
+ * Returns SECFailure if an error occurred (usually means that OCSP
+ * checking was not enabled or status contexts were not initialized --
+ * error set will be SEC_ERROR_OCSP_NOT_ENABLED); SECSuccess otherwise.
+ */
+extern SECStatus
+CERT_DisableOCSPChecking(CERTCertDBHandle *handle);
+
+/*
+ * FUNCTION: CERT_SetOCSPDefaultResponder
+ * Specify the location and cert of the default responder.
+ * If OCSP checking is already enabled *and* use of a default responder
+ * is also already enabled, all OCSP checking from now on will go directly
+ * to the specified responder. If OCSP checking is not enabled, or if
+ * it is but use of a default responder is not enabled, the information
+ * will be recorded and take effect whenever both are enabled.
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * Cert database on which OCSP checking should use the default responder.
+ * const char *url
+ * The location of the default responder (e.g. "http://foo.com:80/ocsp")
+ * Note that the location will not be tested until the first attempt
+ * to send a request there.
+ * const char *name
+ * The nickname of the cert to trust (expected) to sign the OCSP responses.
+ * If the corresponding cert cannot be found, SECFailure is returned.
+ * RETURN:
+ * Returns SECFailure if an error occurred; SECSuccess otherwise.
+ * The most likely error is that the cert for "name" could not be found
+ * (probably SEC_ERROR_UNKNOWN_CERT). Other errors are low-level (no memory,
+ * bad database, etc.).
+ */
+extern SECStatus
+CERT_SetOCSPDefaultResponder(CERTCertDBHandle *handle,
+ const char *url, const char *name);
+
+/*
+ * FUNCTION: CERT_EnableOCSPDefaultResponder
+ * Turns on use of a default responder when OCSP checking.
+ * If OCSP checking is already enabled, this will make subsequent checks
+ * go directly to the default responder. (The location of the responder
+ * and the nickname of the responder cert must already be specified.)
+ * If OCSP checking is not enabled, this will be recorded and take effect
+ * whenever it is enabled.
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * Cert database on which OCSP checking should use the default responder.
+ * RETURN:
+ * Returns SECFailure if an error occurred; SECSuccess otherwise.
+ * No errors are especially likely unless the caller did not previously
+ * perform a successful call to SetOCSPDefaultResponder (in which case
+ * the error set will be SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER).
+ */
+extern SECStatus
+CERT_EnableOCSPDefaultResponder(CERTCertDBHandle *handle);
+
+/*
+ * FUNCTION: CERT_DisableOCSPDefaultResponder
+ * Turns off use of a default responder when OCSP checking.
+ * (Does nothing if use of a default responder is not enabled.)
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * Cert database on which OCSP checking should stop using a default
+ * responder.
+ * RETURN:
+ * Returns SECFailure if an error occurred; SECSuccess otherwise.
+ * Errors very unlikely (like random memory corruption...).
+ */
+extern SECStatus
+CERT_DisableOCSPDefaultResponder(CERTCertDBHandle *handle);
+
+/* If forcePost is set, OCSP requests will only be sent using the HTTP POST
+ * method. When forcePost is not set, OCSP requests will be sent using the
+ * HTTP GET method, with a fallback to POST when we fail to receive a response
+ * and/or when we receive an uncacheable response like "Unknown."
+ *
+ * The default is to use GET and fallback to POST.
+ */
+extern SECStatus CERT_ForcePostMethodForOCSP(PRBool forcePost);
+
+/*
+ * -------------------------------------------------------
+ * The Functions above are those expected to be used by a client
+ * providing OCSP status checking along with every cert verification.
+ * The functions below are for OCSP testing, debugging, or clients
+ * or servers performing more specialized OCSP tasks.
+ * -------------------------------------------------------
+ */
+
+/*
+ * FUNCTION: CERT_CreateOCSPRequest
+ * Creates a CERTOCSPRequest, requesting the status of the certs in
+ * the given list.
+ * INPUTS:
+ * CERTCertList *certList
+ * A list of certs for which status will be requested.
+ * Note that all of these certificates should have the same issuer,
+ * or it's expected the response will be signed by a trusted responder.
+ * If the certs need to be broken up into multiple requests, that
+ * must be handled by the caller (and thus by having multiple calls
+ * to this routine), who knows about where the request(s) are being
+ * sent and whether there are any trusted responders in place.
+ * PRTime time
+ * Indicates the time for which the certificate status is to be
+ * determined -- this may be used in the search for the cert's issuer
+ * but has no effect on the request itself.
+ * PRBool addServiceLocator
+ * If true, the Service Locator extension should be added to the
+ * single request(s) for each cert.
+ * CERTCertificate *signerCert
+ * If non-NULL, means sign the request using this cert. Otherwise,
+ * do not sign.
+ * XXX note that request signing is not yet supported; see comment in code
+ * RETURN:
+ * A pointer to a CERTOCSPRequest structure containing an OCSP request
+ * for the cert list. On error, null is returned, with an error set
+ * indicating the reason. This is likely SEC_ERROR_UNKNOWN_ISSUER.
+ * (The issuer is needed to create a request for the certificate.)
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ */
+extern CERTOCSPRequest *
+CERT_CreateOCSPRequest(CERTCertList *certList, PRTime time,
+ PRBool addServiceLocator,
+ CERTCertificate *signerCert);
+
+/*
+ * FUNCTION: CERT_AddOCSPAcceptableResponses
+ * Add the AcceptableResponses extension to an OCSP Request.
+ * INPUTS:
+ * CERTOCSPRequest *request
+ * The request to which the extension should be added.
+ * SECOidTag responseType0, ...
+ * A list (of one or more) of SECOidTag -- each of the response types
+ * to be added. The last OID *must* be SEC_OID_PKIX_OCSP_BASIC_RESPONSE.
+ * (This marks the end of the list, and it must be specified because a
+ * client conforming to the OCSP standard is required to handle the basic
+ * response type.) The OIDs are not checked in any way.
+ * RETURN:
+ * SECSuccess if the extension is added; SECFailure if anything goes wrong.
+ * All errors are internal or low-level problems (e.g. no memory).
+ */
+extern SECStatus
+CERT_AddOCSPAcceptableResponses(CERTOCSPRequest *request,
+ SECOidTag responseType0, ...);
+
+/*
+ * FUNCTION: CERT_EncodeOCSPRequest
+ * DER encodes an OCSP Request, possibly adding a signature as well.
+ * XXX Signing is not yet supported, however; see comments in code.
+ * INPUTS:
+ * PLArenaPool *arena
+ * The return value is allocated from here.
+ * If a NULL is passed in, allocation is done from the heap instead.
+ * CERTOCSPRequest *request
+ * The request to be encoded.
+ * void *pwArg
+ * Pointer to argument for password prompting, if needed. (Definitely
+ * not needed if not signing.)
+ * RETURN:
+ * Returns a NULL on error and a pointer to the SECItem with the
+ * encoded value otherwise. Any error is likely to be low-level
+ * (e.g. no memory).
+ */
+extern SECItem *
+CERT_EncodeOCSPRequest(PLArenaPool *arena, CERTOCSPRequest *request,
+ void *pwArg);
+
+/*
+ * FUNCTION: CERT_DecodeOCSPRequest
+ * Decode a DER encoded OCSP Request.
+ * INPUTS:
+ * SECItem *src
+ * Pointer to a SECItem holding DER encoded OCSP Request.
+ * RETURN:
+ * Returns a pointer to a CERTOCSPRequest containing the decoded request.
+ * On error, returns NULL. Most likely error is trouble decoding
+ * (SEC_ERROR_OCSP_MALFORMED_REQUEST), or low-level problem (no memory).
+ */
+extern CERTOCSPRequest *
+CERT_DecodeOCSPRequest(const SECItem *src);
+
+/*
+ * FUNCTION: CERT_DestroyOCSPRequest
+ * Frees an OCSP Request structure.
+ * INPUTS:
+ * CERTOCSPRequest *request
+ * Pointer to CERTOCSPRequest to be freed.
+ * RETURN:
+ * No return value; no errors.
+ */
+extern void
+CERT_DestroyOCSPRequest(CERTOCSPRequest *request);
+
+/*
+ * FUNCTION: CERT_DecodeOCSPResponse
+ * Decode a DER encoded OCSP Response.
+ * INPUTS:
+ * SECItem *src
+ * Pointer to a SECItem holding DER encoded OCSP Response.
+ * RETURN:
+ * Returns a pointer to a CERTOCSPResponse (the decoded OCSP Response);
+ * the caller is responsible for destroying it. Or NULL if error (either
+ * response could not be decoded (SEC_ERROR_OCSP_MALFORMED_RESPONSE),
+ * it was of an unexpected type (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE),
+ * or a low-level or internal error occurred).
+ */
+extern CERTOCSPResponse *
+CERT_DecodeOCSPResponse(const SECItem *src);
+
+/*
+ * FUNCTION: CERT_DestroyOCSPResponse
+ * Frees an OCSP Response structure.
+ * INPUTS:
+ * CERTOCSPResponse *request
+ * Pointer to CERTOCSPResponse to be freed.
+ * RETURN:
+ * No return value; no errors.
+ */
+extern void
+CERT_DestroyOCSPResponse(CERTOCSPResponse *response);
+
+/*
+ * FUNCTION: CERT_GetEncodedOCSPResponse
+ * Creates and sends a request to an OCSP responder, then reads and
+ * returns the (encoded) response.
+ * INPUTS:
+ * PLArenaPool *arena
+ * Pointer to arena from which return value will be allocated.
+ * If NULL, result will be allocated from the heap (and thus should
+ * be freed via SECITEM_FreeItem).
+ * CERTCertList *certList
+ * A list of certs for which status will be requested.
+ * Note that all of these certificates should have the same issuer,
+ * or it's expected the response will be signed by a trusted responder.
+ * If the certs need to be broken up into multiple requests, that
+ * must be handled by the caller (and thus by having multiple calls
+ * to this routine), who knows about where the request(s) are being
+ * sent and whether there are any trusted responders in place.
+ * const char *location
+ * The location of the OCSP responder (a URL).
+ * PRTime time
+ * Indicates the time for which the certificate status is to be
+ * determined -- this may be used in the search for the cert's issuer
+ * but has no other bearing on the operation.
+ * PRBool addServiceLocator
+ * If true, the Service Locator extension should be added to the
+ * single request(s) for each cert.
+ * CERTCertificate *signerCert
+ * If non-NULL, means sign the request using this cert. Otherwise,
+ * do not sign.
+ * void *pwArg
+ * Pointer to argument for password prompting, if needed. (Definitely
+ * not needed if not signing.)
+ * OUTPUTS:
+ * CERTOCSPRequest **pRequest
+ * Pointer in which to store the OCSP request created for the given
+ * list of certificates. It is only filled in if the entire operation
+ * is successful and the pointer is not null -- and in that case the
+ * caller is then reponsible for destroying it.
+ * RETURN:
+ * Returns a pointer to the SECItem holding the response.
+ * On error, returns null with error set describing the reason:
+ * SEC_ERROR_UNKNOWN_ISSUER
+ * SEC_ERROR_CERT_BAD_ACCESS_LOCATION
+ * SEC_ERROR_OCSP_BAD_HTTP_RESPONSE
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ */
+extern SECItem *
+CERT_GetEncodedOCSPResponse(PLArenaPool *arena, CERTCertList *certList,
+ const char *location, PRTime time,
+ PRBool addServiceLocator,
+ CERTCertificate *signerCert, void *pwArg,
+ CERTOCSPRequest **pRequest);
+
+/*
+ * FUNCTION: CERT_VerifyOCSPResponseSignature
+ * Check the signature on an OCSP Response. Will also perform a
+ * verification of the signer's certificate. Note, however, that a
+ * successful verification does not make any statement about the
+ * signer's *authority* to provide status for the certificate(s),
+ * that must be checked individually for each certificate.
+ * INPUTS:
+ * CERTOCSPResponse *response
+ * Pointer to response structure with signature to be checked.
+ * CERTCertDBHandle *handle
+ * Pointer to CERTCertDBHandle for certificate DB to use for verification.
+ * void *pwArg
+ * Pointer to argument for password prompting, if needed.
+ * CERTCertificate *issuerCert
+ * Issuer of the certificate that generated the OCSP request.
+ * OUTPUTS:
+ * CERTCertificate **pSignerCert
+ * Pointer in which to store signer's certificate; only filled-in if
+ * non-null.
+ * RETURN:
+ * Returns SECSuccess when signature is valid, anything else means invalid.
+ * Possible errors set:
+ * SEC_ERROR_OCSP_MALFORMED_RESPONSE - unknown type of ResponderID
+ * SEC_ERROR_INVALID_TIME - bad format of "ProducedAt" time
+ * SEC_ERROR_UNKNOWN_SIGNER - signer's cert could not be found
+ * SEC_ERROR_BAD_SIGNATURE - the signature did not verify
+ * Other errors are any of the many possible failures in cert verification
+ * (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when
+ * verifying the signer's cert, or low-level problems (no memory, etc.)
+ */
+extern SECStatus
+CERT_VerifyOCSPResponseSignature(CERTOCSPResponse *response,
+ CERTCertDBHandle *handle, void *pwArg,
+ CERTCertificate **pSignerCert,
+ CERTCertificate *issuerCert);
+
+/*
+ * FUNCTION: CERT_GetOCSPAuthorityInfoAccessLocation
+ * Get the value of the URI of the OCSP responder for the given cert.
+ * This is found in the (optional) Authority Information Access extension
+ * in the cert.
+ * INPUTS:
+ * CERTCertificate *cert
+ * The certificate being examined.
+ * RETURN:
+ * char *
+ * A copy of the URI for the OCSP method, if found. If either the
+ * extension is not present or it does not contain an entry for OCSP,
+ * SEC_ERROR_EXTENSION_NOT_FOUND will be set and a NULL returned.
+ * Any other error will also result in a NULL being returned.
+ *
+ * This result should be freed (via PORT_Free) when no longer in use.
+ */
+extern char *
+CERT_GetOCSPAuthorityInfoAccessLocation(const CERTCertificate *cert);
+
+/*
+ * FUNCTION: CERT_RegisterAlternateOCSPAIAInfoCallBack
+ * This function serves two purposes.
+ * 1) It registers the address of a callback function that will be
+ * called for certs that have no OCSP AIA extension, to see if the
+ * callback wishes to supply an alternative URL for such an OCSP inquiry.
+ * 2) It outputs the previously registered function's address to the
+ * address supplied by the caller, unless that is NULL.
+ * The registered callback function returns NULL, or an allocated string
+ * that may be subsequently freed by calling PORT_Free().
+ * RETURN:
+ * SECSuccess or SECFailure (if the library is not yet intialized)
+ */
+extern SECStatus
+CERT_RegisterAlternateOCSPAIAInfoCallBack(
+ CERT_StringFromCertFcn newCallback,
+ CERT_StringFromCertFcn *oldCallback);
+
+/*
+ * FUNCTION: CERT_ParseURL
+ * Parse a URI into hostname, port, and path. The scheme in the URI must
+ * be "http".
+ * INPUTS:
+ * const char *url
+ * The URI to be parsed
+ * OUTPUTS:
+ * char **pHostname
+ * Pointer to store the hostname obtained from the URI.
+ * This result should be freed (via PORT_Free) when no longer in use.
+ * PRUint16 *pPort
+ * Pointer to store the port number obtained from the URI.
+ * char **pPath
+ * Pointer to store the path obtained from the URI.
+ * This result should be freed (via PORT_Free) when no longer in use.
+ * RETURN:
+ * Returns SECSuccess when parsing was successful. Returns SECFailure when
+ * problems were encountered.
+ */
+extern SECStatus
+CERT_ParseURL(const char *url, char **pHostname, PRUint16 *pPort, char **pPath);
+
+/*
+ * FUNCTION: CERT_CheckOCSPStatus
+ * Checks the status of a certificate via OCSP. Will only check status for
+ * a certificate that has an AIA (Authority Information Access) extension
+ * for OCSP *or* when a "default responder" is specified and enabled.
+ * (If no AIA extension for OCSP and no default responder in place, the
+ * cert is considered to have a good status and SECSuccess is returned.)
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * certificate DB of the cert that is being checked
+ * CERTCertificate *cert
+ * the certificate being checked
+ * XXX in the long term also need a boolean parameter that specifies
+ * whether to check the cert chain, as well; for now we check only
+ * the leaf (the specified certificate)
+ * PRTime time
+ * time for which status is to be determined
+ * void *pwArg
+ * argument for password prompting, if needed
+ * RETURN:
+ * Returns SECSuccess if an approved OCSP responder "knows" the cert
+ * *and* returns a non-revoked status for it; SECFailure otherwise,
+ * with an error set describing the reason:
+ *
+ * SEC_ERROR_OCSP_BAD_HTTP_RESPONSE
+ * SEC_ERROR_OCSP_FUTURE_RESPONSE
+ * SEC_ERROR_OCSP_MALFORMED_REQUEST
+ * SEC_ERROR_OCSP_MALFORMED_RESPONSE
+ * SEC_ERROR_OCSP_OLD_RESPONSE
+ * SEC_ERROR_OCSP_REQUEST_NEEDS_SIG
+ * SEC_ERROR_OCSP_SERVER_ERROR
+ * SEC_ERROR_OCSP_TRY_SERVER_LATER
+ * SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST
+ * SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE
+ * SEC_ERROR_OCSP_UNKNOWN_CERT
+ * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS
+ * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE
+ *
+ * SEC_ERROR_BAD_SIGNATURE
+ * SEC_ERROR_CERT_BAD_ACCESS_LOCATION
+ * SEC_ERROR_INVALID_TIME
+ * SEC_ERROR_REVOKED_CERTIFICATE
+ * SEC_ERROR_UNKNOWN_ISSUER
+ * SEC_ERROR_UNKNOWN_SIGNER
+ *
+ * Other errors are any of the many possible failures in cert verification
+ * (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when
+ * verifying the signer's cert, or low-level problems (error allocating
+ * memory, error performing ASN.1 decoding, etc.).
+ */
+extern SECStatus
+CERT_CheckOCSPStatus(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRTime time, void *pwArg);
+
+/*
+ * FUNCTION: CERT_CacheOCSPResponseFromSideChannel
+ * First, this function checks the OCSP cache to see if a good response
+ * for the given certificate already exists. If it does, then the function
+ * returns successfully.
+ *
+ * If not, then it validates that the given OCSP response is a valid,
+ * good response for the given certificate and inserts it into the
+ * cache.
+ *
+ * This function is intended for use when OCSP responses are provided via a
+ * side-channel, i.e. TLS OCSP stapling (a.k.a. the status_request extension).
+ *
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * certificate DB of the cert that is being checked
+ * CERTCertificate *cert
+ * the certificate being checked
+ * PRTime time
+ * time for which status is to be determined
+ * SECItem *encodedResponse
+ * the DER encoded bytes of the OCSP response
+ * void *pwArg
+ * argument for password prompting, if needed
+ * RETURN:
+ * SECSuccess if the cert was found in the cache, or if the OCSP response was
+ * found to be valid and inserted into the cache. SECFailure otherwise.
+ */
+extern SECStatus
+CERT_CacheOCSPResponseFromSideChannel(CERTCertDBHandle *handle,
+ CERTCertificate *cert,
+ PRTime time,
+ const SECItem *encodedResponse,
+ void *pwArg);
+
+/*
+ * FUNCTION: CERT_GetOCSPStatusForCertID
+ * Returns the OCSP status contained in the passed in parameter response
+ * that corresponds to the certID passed in.
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * certificate DB of the cert that is being checked
+ * CERTOCSPResponse *response
+ * the OCSP response we want to retrieve status from.
+ * CERTOCSPCertID *certID
+ * the ID we want to look for from the response.
+ * CERTCertificate *signerCert
+ * the certificate that was used to sign the OCSP response.
+ * must be obtained via a call to CERT_VerifyOCSPResponseSignature.
+ * PRTime time
+ * The time at which we're checking the status for.
+ * RETURN:
+ * Return values are the same as those for CERT_CheckOCSPStatus
+ */
+extern SECStatus
+CERT_GetOCSPStatusForCertID(CERTCertDBHandle *handle,
+ CERTOCSPResponse *response,
+ CERTOCSPCertID *certID,
+ CERTCertificate *signerCert,
+ PRTime time);
+
+/*
+ * FUNCTION CERT_GetOCSPResponseStatus
+ * Returns the response status for the response passed.
+ * INPUTS:
+ * CERTOCSPResponse *response
+ * The response to query for status
+ * RETURN:
+ * Returns SECSuccess if the response has a successful status value.
+ * Otherwise it returns SECFailure and sets one of the following error
+ * codes via PORT_SetError
+ * SEC_ERROR_OCSP_MALFORMED_REQUEST
+ * SEC_ERROR_OCSP_SERVER_ERROR
+ * SEC_ERROR_OCSP_TRY_SERVER_LATER
+ * SEC_ERROR_OCSP_REQUEST_NEEDS_SIG
+ * SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST
+ * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS
+ */
+extern SECStatus
+CERT_GetOCSPResponseStatus(CERTOCSPResponse *response);
+
+/*
+ * FUNCTION CERT_CreateOCSPCertID
+ * Returns the OCSP certID for the certificate passed in.
+ * INPUTS:
+ * CERTCertificate *cert
+ * The certificate for which to create the certID for.
+ * PRTime time
+ * The time at which the id is requested for. This is used
+ * to determine the appropriate issuer for the cert since
+ * the issuing CA may be an older expired certificate.
+ * RETURN:
+ * A new copy of a CERTOCSPCertID*. The memory for this certID
+ * should be freed by calling CERT_DestroyOCSPCertID when the
+ * certID is no longer necessary.
+ */
+extern CERTOCSPCertID *
+CERT_CreateOCSPCertID(CERTCertificate *cert, PRTime time);
+
+/*
+ * FUNCTION: CERT_DestroyOCSPCertID
+ * Frees the memory associated with the certID passed in.
+ * INPUTS:
+ * CERTOCSPCertID* certID
+ * The certID that the caller no longer needs and wants to
+ * free the associated memory.
+ * RETURN:
+ * SECSuccess if freeing the memory was successful. Returns
+ * SECFailure if the memory passed in was not allocated with
+ * a call to CERT_CreateOCSPCertID.
+ */
+extern SECStatus
+CERT_DestroyOCSPCertID(CERTOCSPCertID *certID);
+
+extern CERTOCSPSingleResponse *
+CERT_CreateOCSPSingleResponseGood(PLArenaPool *arena,
+ CERTOCSPCertID *id,
+ PRTime thisUpdate,
+ const PRTime *nextUpdate);
+
+extern CERTOCSPSingleResponse *
+CERT_CreateOCSPSingleResponseUnknown(PLArenaPool *arena,
+ CERTOCSPCertID *id,
+ PRTime thisUpdate,
+ const PRTime *nextUpdate);
+
+extern CERTOCSPSingleResponse *
+CERT_CreateOCSPSingleResponseRevoked(
+ PLArenaPool *arena,
+ CERTOCSPCertID *id,
+ PRTime thisUpdate,
+ const PRTime *nextUpdate,
+ PRTime revocationTime,
+ const CERTCRLEntryReasonCode *revocationReason);
+
+extern SECItem *
+CERT_CreateEncodedOCSPSuccessResponse(
+ PLArenaPool *arena,
+ CERTCertificate *responderCert,
+ CERTOCSPResponderIDType responderIDType,
+ PRTime producedAt,
+ CERTOCSPSingleResponse **responses,
+ void *wincx);
+
+/*
+ * FUNCTION: CERT_CreateEncodedOCSPErrorResponse
+ * Creates an encoded OCSP response with an error response status.
+ * INPUTS:
+ * PLArenaPool *arena
+ * The return value is allocated from here.
+ * If a NULL is passed in, allocation is done from the heap instead.
+ * int error
+ * An NSS error code indicating an error response status. The error
+ * code is mapped to an OCSP response status as follows:
+ * SEC_ERROR_OCSP_MALFORMED_REQUEST -> malformedRequest
+ * SEC_ERROR_OCSP_SERVER_ERROR -> internalError
+ * SEC_ERROR_OCSP_TRY_SERVER_LATER -> tryLater
+ * SEC_ERROR_OCSP_REQUEST_NEEDS_SIG -> sigRequired
+ * SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST -> unauthorized
+ * where the OCSP response status is an enumerated type defined in
+ * RFC 2560:
+ * OCSPResponseStatus ::= ENUMERATED {
+ * successful (0), --Response has valid confirmations
+ * malformedRequest (1), --Illegal confirmation request
+ * internalError (2), --Internal error in issuer
+ * tryLater (3), --Try again later
+ * --(4) is not used
+ * sigRequired (5), --Must sign the request
+ * unauthorized (6) --Request unauthorized
+ * }
+ * RETURN:
+ * Returns a pointer to the SECItem holding the response.
+ * On error, returns null with error set describing the reason:
+ * SEC_ERROR_INVALID_ARGS
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ */
+extern SECItem *
+CERT_CreateEncodedOCSPErrorResponse(PLArenaPool *arena, int error);
+
+/* Sends an OCSP request using the HTTP POST method to the location addressed
+ * by the URL in |location| parameter. The request body will be
+ * |encodedRequest|, which must be a valid encoded OCSP request. On success,
+ * the server's response is returned and the caller must free it using
+ * SECITEM_FreeItem. On failure, NULL is returned. No parsing or validation of
+ * the HTTP response is done.
+ *
+ * If a default HTTP client has been registered with
+ * SEC_RegisterDefaultHttpClient then that client is used. Otherwise, an
+ * internal HTTP client is used.
+ */
+SECItem *CERT_PostOCSPRequest(PLArenaPool *arena, const char *location,
+ const SECItem *encodedRequest);
+
+/************************************************************************/
+SEC_END_PROTOS
+
+#endif /* _OCSP_H_ */
diff --git a/security/nss/lib/certhigh/ocspi.h b/security/nss/lib/certhigh/ocspi.h
new file mode 100644
index 0000000000..c946d9f51c
--- /dev/null
+++ b/security/nss/lib/certhigh/ocspi.h
@@ -0,0 +1,166 @@
+/* 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/. */
+/*
+ * ocspi.h - NSS internal interfaces to OCSP code
+ */
+
+#ifndef _OCSPI_H_
+#define _OCSPI_H_
+
+SECStatus OCSP_InitGlobal(void);
+SECStatus OCSP_ShutdownGlobal(void);
+
+ocspResponseData *
+ocsp_GetResponseData(CERTOCSPResponse *response, SECItem **tbsResponseDataDER);
+
+ocspSignature *
+ocsp_GetResponseSignature(CERTOCSPResponse *response);
+
+SECItem *
+ocsp_DigestValue(PLArenaPool *arena, SECOidTag digestAlg,
+ SECItem *fill, const SECItem *src);
+
+PRBool
+ocsp_CertIsOCSPDefaultResponder(CERTCertDBHandle *handle, CERTCertificate *cert);
+
+CERTCertificate *
+ocsp_GetSignerCertificate(CERTCertDBHandle *handle, ocspResponseData *tbsData,
+ ocspSignature *signature, CERTCertificate *issuer);
+
+SECStatus
+ocsp_VerifyResponseSignature(CERTCertificate *signerCert,
+ ocspSignature *signature,
+ SECItem *tbsResponseDataDER,
+ void *pwArg);
+
+CERTOCSPRequest *
+cert_CreateSingleCertOCSPRequest(CERTOCSPCertID *certID,
+ CERTCertificate *singleCert,
+ PRTime time,
+ PRBool addServiceLocator,
+ CERTCertificate *signerCert);
+
+typedef enum { ocspMissing,
+ ocspFresh,
+ ocspStale } OCSPFreshness;
+
+SECStatus
+ocsp_GetCachedOCSPResponseStatus(CERTOCSPCertID *certID,
+ PRTime time,
+ PRBool ignoreOcspFailureMode,
+ SECStatus *rvOcsp,
+ SECErrorCodes *missingResponseError,
+ OCSPFreshness *freshness);
+
+/*
+ * FUNCTION: cert_ProcessOCSPResponse
+ * Same behavior and basic parameters as CERT_GetOCSPStatusForCertID.
+ * In addition it can update the OCSP cache (using information
+ * available internally to this function).
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * certificate DB of the cert that is being checked
+ * CERTOCSPResponse *response
+ * the OCSP response we want to retrieve status from.
+ * CERTOCSPCertID *certID
+ * the ID we want to look for from the response.
+ * CERTCertificate *signerCert
+ * the certificate that was used to sign the OCSP response.
+ * must be obtained via a call to CERT_VerifyOCSPResponseSignature.
+ * PRTime time
+ * The time at which we're checking the status for.
+ * PRBool *certIDWasConsumed
+ * In and Out parameter.
+ * If certIDWasConsumed is NULL on input,
+ * this function might produce a deep copy of cert ID
+ * for storing it in the cache.
+ * If out value is true, ownership of parameter certID was
+ * transferred to the OCSP cache.
+ * SECStatus *cacheUpdateStatus
+ * This optional out parameter will contain the result
+ * of the cache update operation (if requested).
+ * RETURN:
+ * The return value is not influenced by the cache operation,
+ * it matches the documentation for CERT_CheckOCSPStatus
+ */
+
+SECStatus
+cert_ProcessOCSPResponse(CERTCertDBHandle *handle,
+ CERTOCSPResponse *response,
+ CERTOCSPCertID *certID,
+ CERTCertificate *signerCert,
+ PRTime time,
+ PRBool *certIDWasConsumed,
+ SECStatus *cacheUpdateStatus);
+
+/*
+ * FUNCTION: cert_RememberOCSPProcessingFailure
+ * If an application notices a failure during OCSP processing,
+ * it should finally call this function. The failure will be recorded
+ * in the OCSP cache in order to avoid repetitive failures.
+ * INPUTS:
+ * CERTOCSPCertID *certID
+ * the ID that was used for the failed OCSP processing
+ * PRBool *certIDWasConsumed
+ * Out parameter, if set to true, ownership of parameter certID was
+ * transferred to the OCSP cache.
+ * RETURN:
+ * Status of the cache update operation.
+ */
+
+SECStatus
+cert_RememberOCSPProcessingFailure(CERTOCSPCertID *certID,
+ PRBool *certIDWasConsumed);
+
+/*
+ * FUNCTION: ocsp_GetResponderLocation
+ * Check ocspx context for user-designated responder URI first. If not
+ * found, checks cert AIA extension.
+ * INPUTS:
+ * CERTCertDBHandle *handle
+ * certificate DB of the cert that is being checked
+ * CERTCertificate *cert
+ * The certificate being examined.
+ * PRBool *certIDWasConsumed
+ * Out parameter, if set to true, URI of default responder is
+ * returned.
+ * RETURN:
+ * Responder URI.
+ */
+char *
+ocsp_GetResponderLocation(CERTCertDBHandle *handle,
+ CERTCertificate *cert,
+ PRBool canUseDefaultLocation,
+ PRBool *isDefault);
+
+/* FUNCTION: ocsp_FetchingFailureIsVerificationFailure
+ * The function checks the global ocsp settings and
+ * tells how to treat an ocsp response fetching failure.
+ * RETURNS:
+ * if PR_TRUE is returned, then treat fetching as a
+ * revoked cert status.
+ */
+PRBool
+ocsp_FetchingFailureIsVerificationFailure(void);
+
+size_t
+ocsp_UrlEncodeBase64Buf(const char *base64Buf, char *outputBuf);
+
+SECStatus
+ocsp_GetVerifiedSingleResponseForCertID(CERTCertDBHandle *handle,
+ CERTOCSPResponse *response,
+ CERTOCSPCertID *certID,
+ CERTCertificate *signerCert,
+ PRTime time,
+ CERTOCSPSingleResponse **pSingleResponse);
+
+SECStatus
+ocsp_CertHasGoodStatus(ocspCertStatus *status, PRTime time);
+
+void
+ocsp_CacheSingleResponse(CERTOCSPCertID *certID,
+ CERTOCSPSingleResponse *single,
+ PRBool *certIDWasConsumed);
+
+#endif /* _OCSPI_H_ */
diff --git a/security/nss/lib/certhigh/ocspsig.c b/security/nss/lib/certhigh/ocspsig.c
new file mode 100644
index 0000000000..94606baf56
--- /dev/null
+++ b/security/nss/lib/certhigh/ocspsig.c
@@ -0,0 +1,597 @@
+/* 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 "secasn1.h"
+#include "secder.h"
+#include "cert.h"
+#include "secerr.h"
+#include "secoid.h"
+#include "sechash.h"
+#include "keyhi.h"
+#include "cryptohi.h"
+#include "ocsp.h"
+#include "ocspti.h"
+#include "ocspi.h"
+#include "pk11pub.h"
+
+extern const SEC_ASN1Template ocsp_ResponderIDByNameTemplate[];
+extern const SEC_ASN1Template ocsp_ResponderIDByKeyTemplate[];
+extern const SEC_ASN1Template ocsp_OCSPResponseTemplate[];
+
+ocspCertStatus *
+ocsp_CreateCertStatus(PLArenaPool *arena,
+ ocspCertStatusType status,
+ PRTime revocationTime)
+{
+ ocspCertStatus *cs;
+
+ if (!arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ switch (status) {
+ case ocspCertStatus_good:
+ case ocspCertStatus_unknown:
+ case ocspCertStatus_revoked:
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ cs = PORT_ArenaZNew(arena, ocspCertStatus);
+ if (!cs)
+ return NULL;
+ cs->certStatusType = status;
+ switch (status) {
+ case ocspCertStatus_good:
+ cs->certStatusInfo.goodInfo = SECITEM_AllocItem(arena, NULL, 0);
+ if (!cs->certStatusInfo.goodInfo)
+ return NULL;
+ break;
+ case ocspCertStatus_unknown:
+ cs->certStatusInfo.unknownInfo = SECITEM_AllocItem(arena, NULL, 0);
+ if (!cs->certStatusInfo.unknownInfo)
+ return NULL;
+ break;
+ case ocspCertStatus_revoked:
+ cs->certStatusInfo.revokedInfo =
+ PORT_ArenaZNew(arena, ocspRevokedInfo);
+ if (!cs->certStatusInfo.revokedInfo)
+ return NULL;
+ cs->certStatusInfo.revokedInfo->revocationReason =
+ SECITEM_AllocItem(arena, NULL, 0);
+ if (!cs->certStatusInfo.revokedInfo->revocationReason)
+ return NULL;
+ if (DER_TimeToGeneralizedTimeArena(arena,
+ &cs->certStatusInfo.revokedInfo->revocationTime,
+ revocationTime) !=
+ SECSuccess)
+ return NULL;
+ break;
+ default:
+ PORT_Assert(PR_FALSE);
+ }
+ return cs;
+}
+
+static const SEC_ASN1Template mySEC_EnumeratedTemplate[] = {
+ { SEC_ASN1_ENUMERATED, 0, NULL, sizeof(SECItem) }
+};
+
+static const SEC_ASN1Template mySEC_PointerToEnumeratedTemplate[] = {
+ { SEC_ASN1_POINTER, 0, mySEC_EnumeratedTemplate }
+};
+
+static const SEC_ASN1Template ocsp_EncodeRevokedInfoTemplate[] = {
+ { SEC_ASN1_GENERALIZED_TIME,
+ offsetof(ocspRevokedInfo, revocationTime) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(ocspRevokedInfo, revocationReason),
+ mySEC_PointerToEnumeratedTemplate },
+ { 0 }
+};
+
+static const SEC_ASN1Template ocsp_PointerToEncodeRevokedInfoTemplate[] = {
+ { SEC_ASN1_POINTER, 0,
+ ocsp_EncodeRevokedInfoTemplate }
+};
+
+static const SEC_ASN1Template mySEC_NullTemplate[] = {
+ { SEC_ASN1_NULL, 0, NULL, sizeof(SECItem) }
+};
+
+static const SEC_ASN1Template ocsp_CertStatusTemplate[] = {
+ { SEC_ASN1_CHOICE, offsetof(ocspCertStatus, certStatusType),
+ 0, sizeof(ocspCertStatus) },
+ { SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ 0, mySEC_NullTemplate, ocspCertStatus_good },
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(ocspCertStatus, certStatusInfo.revokedInfo),
+ ocsp_PointerToEncodeRevokedInfoTemplate, ocspCertStatus_revoked },
+ { SEC_ASN1_CONTEXT_SPECIFIC | 2,
+ 0, mySEC_NullTemplate, ocspCertStatus_unknown },
+ { 0 }
+};
+
+static const SEC_ASN1Template mySECOID_AlgorithmIDTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SECAlgorithmID) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(SECAlgorithmID, algorithm) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
+ offsetof(SECAlgorithmID, parameters) },
+ { 0 }
+};
+
+static const SEC_ASN1Template mySEC_AnyTemplate[] = {
+ { SEC_ASN1_ANY | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) }
+};
+
+static const SEC_ASN1Template mySEC_SequenceOfAnyTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF, 0, mySEC_AnyTemplate }
+};
+
+static const SEC_ASN1Template mySEC_PointerToSequenceOfAnyTemplate[] = {
+ { SEC_ASN1_POINTER, 0, mySEC_SequenceOfAnyTemplate }
+};
+
+static const SEC_ASN1Template mySEC_IntegerTemplate[] = {
+ { SEC_ASN1_INTEGER, 0, NULL, sizeof(SECItem) }
+};
+
+static const SEC_ASN1Template mySEC_PointerToIntegerTemplate[] = {
+ { SEC_ASN1_POINTER, 0, mySEC_IntegerTemplate }
+};
+
+static const SEC_ASN1Template mySEC_GeneralizedTimeTemplate[] = {
+ { SEC_ASN1_GENERALIZED_TIME | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) }
+};
+
+static const SEC_ASN1Template mySEC_PointerToGeneralizedTimeTemplate[] = {
+ { SEC_ASN1_POINTER, 0, mySEC_GeneralizedTimeTemplate }
+};
+
+static const SEC_ASN1Template ocsp_myCertIDTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTOCSPCertID) },
+ { SEC_ASN1_INLINE,
+ offsetof(CERTOCSPCertID, hashAlgorithm),
+ mySECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(CERTOCSPCertID, issuerNameHash) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(CERTOCSPCertID, issuerKeyHash) },
+ { SEC_ASN1_INTEGER,
+ offsetof(CERTOCSPCertID, serialNumber) },
+ { 0 }
+};
+
+static const SEC_ASN1Template myCERT_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 myCERT_SequenceOfCertExtensionTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF, 0, myCERT_CertExtensionTemplate }
+};
+
+static const SEC_ASN1Template myCERT_PointerToSequenceOfCertExtensionTemplate[] = {
+ { SEC_ASN1_POINTER, 0, myCERT_SequenceOfCertExtensionTemplate }
+};
+
+static const SEC_ASN1Template ocsp_mySingleResponseTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTOCSPSingleResponse) },
+ { SEC_ASN1_POINTER,
+ offsetof(CERTOCSPSingleResponse, certID),
+ ocsp_myCertIDTemplate },
+ { SEC_ASN1_ANY,
+ offsetof(CERTOCSPSingleResponse, derCertStatus) },
+ { SEC_ASN1_GENERALIZED_TIME,
+ offsetof(CERTOCSPSingleResponse, thisUpdate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(CERTOCSPSingleResponse, nextUpdate),
+ mySEC_PointerToGeneralizedTimeTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(CERTOCSPSingleResponse, singleExtensions),
+ myCERT_PointerToSequenceOfCertExtensionTemplate },
+ { 0 }
+};
+
+static const SEC_ASN1Template ocsp_myResponseDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(ocspResponseData) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | /* XXX DER_DEFAULT */
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(ocspResponseData, version),
+ mySEC_PointerToIntegerTemplate },
+ { SEC_ASN1_ANY,
+ offsetof(ocspResponseData, derResponderID) },
+ { SEC_ASN1_GENERALIZED_TIME,
+ offsetof(ocspResponseData, producedAt) },
+ { SEC_ASN1_SEQUENCE_OF,
+ offsetof(ocspResponseData, responses),
+ ocsp_mySingleResponseTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(ocspResponseData, responseExtensions),
+ myCERT_PointerToSequenceOfCertExtensionTemplate },
+ { 0 }
+};
+
+static const SEC_ASN1Template ocsp_EncodeBasicOCSPResponseTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(ocspBasicOCSPResponse) },
+ { SEC_ASN1_POINTER,
+ offsetof(ocspBasicOCSPResponse, tbsResponseData),
+ ocsp_myResponseDataTemplate },
+ { SEC_ASN1_INLINE,
+ offsetof(ocspBasicOCSPResponse, responseSignature.signatureAlgorithm),
+ mySECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_BIT_STRING,
+ offsetof(ocspBasicOCSPResponse, responseSignature.signature) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(ocspBasicOCSPResponse, responseSignature.derCerts),
+ mySEC_PointerToSequenceOfAnyTemplate },
+ { 0 }
+};
+
+static CERTOCSPSingleResponse *
+ocsp_CreateSingleResponse(PLArenaPool *arena,
+ CERTOCSPCertID *id, ocspCertStatus *status,
+ PRTime thisUpdate, const PRTime *nextUpdate)
+{
+ CERTOCSPSingleResponse *sr;
+
+ if (!arena || !id || !status) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ sr = PORT_ArenaZNew(arena, CERTOCSPSingleResponse);
+ if (!sr)
+ return NULL;
+ sr->arena = arena;
+ sr->certID = id;
+ sr->certStatus = status;
+ if (DER_TimeToGeneralizedTimeArena(arena, &sr->thisUpdate, thisUpdate) !=
+ SECSuccess)
+ return NULL;
+ sr->nextUpdate = NULL;
+ if (nextUpdate) {
+ sr->nextUpdate = SECITEM_AllocItem(arena, NULL, 0);
+ if (!sr->nextUpdate)
+ return NULL;
+ if (DER_TimeToGeneralizedTimeArena(arena, sr->nextUpdate, *nextUpdate) !=
+ SECSuccess)
+ return NULL;
+ }
+
+ sr->singleExtensions = PORT_ArenaNewArray(arena, CERTCertExtension *, 1);
+ if (!sr->singleExtensions)
+ return NULL;
+
+ sr->singleExtensions[0] = NULL;
+
+ if (!SEC_ASN1EncodeItem(arena, &sr->derCertStatus,
+ status, ocsp_CertStatusTemplate))
+ return NULL;
+
+ return sr;
+}
+
+CERTOCSPSingleResponse *
+CERT_CreateOCSPSingleResponseGood(PLArenaPool *arena,
+ CERTOCSPCertID *id,
+ PRTime thisUpdate,
+ const PRTime *nextUpdate)
+{
+ ocspCertStatus *cs;
+ if (!arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ cs = ocsp_CreateCertStatus(arena, ocspCertStatus_good, 0);
+ if (!cs)
+ return NULL;
+ return ocsp_CreateSingleResponse(arena, id, cs, thisUpdate, nextUpdate);
+}
+
+CERTOCSPSingleResponse *
+CERT_CreateOCSPSingleResponseUnknown(PLArenaPool *arena,
+ CERTOCSPCertID *id,
+ PRTime thisUpdate,
+ const PRTime *nextUpdate)
+{
+ ocspCertStatus *cs;
+ if (!arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ cs = ocsp_CreateCertStatus(arena, ocspCertStatus_unknown, 0);
+ if (!cs)
+ return NULL;
+ return ocsp_CreateSingleResponse(arena, id, cs, thisUpdate, nextUpdate);
+}
+
+CERTOCSPSingleResponse *
+CERT_CreateOCSPSingleResponseRevoked(
+ PLArenaPool *arena,
+ CERTOCSPCertID *id,
+ PRTime thisUpdate,
+ const PRTime *nextUpdate,
+ PRTime revocationTime,
+ const CERTCRLEntryReasonCode *revocationReason)
+{
+ ocspCertStatus *cs;
+ /* revocationReason is not yet supported, so it must be NULL. */
+ if (!arena || revocationReason) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ cs = ocsp_CreateCertStatus(arena, ocspCertStatus_revoked, revocationTime);
+ if (!cs)
+ return NULL;
+ return ocsp_CreateSingleResponse(arena, id, cs, thisUpdate, nextUpdate);
+}
+
+/* responderCert == 0 means:
+ * create a response with an invalid signature (for testing purposes) */
+SECItem *
+CERT_CreateEncodedOCSPSuccessResponse(
+ PLArenaPool *arena,
+ CERTCertificate *responderCert,
+ CERTOCSPResponderIDType responderIDType,
+ PRTime producedAt,
+ CERTOCSPSingleResponse **responses,
+ void *wincx)
+{
+ PLArenaPool *tmpArena;
+ ocspResponseData *rd = NULL;
+ ocspResponderID *rid = NULL;
+ const SEC_ASN1Template *responderIDTemplate = NULL;
+ ocspBasicOCSPResponse *br = NULL;
+ ocspResponseBytes *rb = NULL;
+ CERTOCSPResponse *response = NULL;
+
+ SECOidTag algID;
+ SECOidData *od = NULL;
+ SECKEYPrivateKey *privKey = NULL;
+ SECItem *result = NULL;
+
+ if (!arena || !responses) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ if (responderIDType != ocspResponderID_byName &&
+ responderIDType != ocspResponderID_byKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!tmpArena)
+ return NULL;
+
+ rd = PORT_ArenaZNew(tmpArena, ocspResponseData);
+ if (!rd)
+ goto done;
+ rid = PORT_ArenaZNew(tmpArena, ocspResponderID);
+ if (!rid)
+ goto done;
+ br = PORT_ArenaZNew(tmpArena, ocspBasicOCSPResponse);
+ if (!br)
+ goto done;
+ rb = PORT_ArenaZNew(tmpArena, ocspResponseBytes);
+ if (!rb)
+ goto done;
+ response = PORT_ArenaZNew(tmpArena, CERTOCSPResponse);
+ if (!response)
+ goto done;
+
+ rd->version.data = NULL;
+ rd->version.len = 0;
+ rd->responseExtensions = NULL;
+ rd->responses = responses;
+ if (DER_TimeToGeneralizedTimeArena(tmpArena, &rd->producedAt, producedAt) !=
+ SECSuccess)
+ goto done;
+
+ if (!responderCert) {
+ /* use invalid signature for testing purposes */
+ unsigned char dummyChar = 'd';
+ SECItem dummy;
+
+ dummy.len = 1;
+ dummy.data = &dummyChar;
+
+ /* it's easier to produdce a keyHash out of nowhere,
+ * than to produce an encoded subject,
+ * so for our dummy response we always use byKey
+ */
+
+ rid->responderIDType = ocspResponderID_byKey;
+ if (!ocsp_DigestValue(tmpArena, SEC_OID_SHA1, &rid->responderIDValue.keyHash,
+ &dummy))
+ goto done;
+
+ if (!SEC_ASN1EncodeItem(tmpArena, &rd->derResponderID, rid,
+ ocsp_ResponderIDByKeyTemplate))
+ goto done;
+
+ br->tbsResponseData = rd;
+
+ if (!SEC_ASN1EncodeItem(tmpArena, &br->tbsResponseDataDER, br->tbsResponseData,
+ ocsp_myResponseDataTemplate))
+ goto done;
+
+ br->responseSignature.derCerts = PORT_ArenaNewArray(tmpArena, SECItem *, 1);
+ if (!br->responseSignature.derCerts)
+ goto done;
+ br->responseSignature.derCerts[0] = NULL;
+
+ algID = SEC_GetSignatureAlgorithmOidTag(rsaKey, SEC_OID_SHA1);
+ if (algID == SEC_OID_UNKNOWN)
+ goto done;
+
+ /* match the regular signature code, which doesn't use the arena */
+ if (!SECITEM_AllocItem(NULL, &br->responseSignature.signature, 1))
+ goto done;
+ PORT_Memcpy(br->responseSignature.signature.data, &dummyChar, 1);
+
+ /* convert len-in-bytes to len-in-bits */
+ br->responseSignature.signature.len = br->responseSignature.signature.len << 3;
+ } else {
+ rid->responderIDType = responderIDType;
+ if (responderIDType == ocspResponderID_byName) {
+ responderIDTemplate = ocsp_ResponderIDByNameTemplate;
+ if (CERT_CopyName(tmpArena, &rid->responderIDValue.name,
+ &responderCert->subject) != SECSuccess)
+ goto done;
+ } else {
+ responderIDTemplate = ocsp_ResponderIDByKeyTemplate;
+ if (!CERT_GetSubjectPublicKeyDigest(tmpArena, responderCert,
+ SEC_OID_SHA1, &rid->responderIDValue.keyHash))
+ goto done;
+ }
+
+ if (!SEC_ASN1EncodeItem(tmpArena, &rd->derResponderID, rid,
+ responderIDTemplate))
+ goto done;
+
+ br->tbsResponseData = rd;
+
+ if (!SEC_ASN1EncodeItem(tmpArena, &br->tbsResponseDataDER, br->tbsResponseData,
+ ocsp_myResponseDataTemplate))
+ goto done;
+
+ br->responseSignature.derCerts = PORT_ArenaNewArray(tmpArena, SECItem *, 1);
+ if (!br->responseSignature.derCerts)
+ goto done;
+ br->responseSignature.derCerts[0] = NULL;
+
+ privKey = PK11_FindKeyByAnyCert(responderCert, wincx);
+ if (!privKey)
+ goto done;
+
+ algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, SEC_OID_SHA1);
+ if (algID == SEC_OID_UNKNOWN)
+ goto done;
+
+ if (SEC_SignData(&br->responseSignature.signature,
+ br->tbsResponseDataDER.data, br->tbsResponseDataDER.len,
+ privKey, algID) !=
+ SECSuccess)
+ goto done;
+
+ /* convert len-in-bytes to len-in-bits */
+ br->responseSignature.signature.len = br->responseSignature.signature.len << 3;
+
+ /* br->responseSignature.signature wasn't allocated from arena,
+ * we must free it when done. */
+ }
+
+ if (SECOID_SetAlgorithmID(tmpArena, &br->responseSignature.signatureAlgorithm, algID, 0) !=
+ SECSuccess)
+ goto done;
+
+ if (!SEC_ASN1EncodeItem(tmpArena, &rb->response, br,
+ ocsp_EncodeBasicOCSPResponseTemplate))
+ goto done;
+
+ rb->responseTypeTag = SEC_OID_PKIX_OCSP_BASIC_RESPONSE;
+
+ od = SECOID_FindOIDByTag(rb->responseTypeTag);
+ if (!od)
+ goto done;
+
+ rb->responseType = od->oid;
+ rb->decodedResponse.basic = br;
+
+ response->arena = tmpArena;
+ response->responseBytes = rb;
+ response->statusValue = ocspResponse_successful;
+
+ if (!SEC_ASN1EncodeInteger(tmpArena, &response->responseStatus,
+ response->statusValue))
+ goto done;
+
+ result = SEC_ASN1EncodeItem(arena, NULL, response, ocsp_OCSPResponseTemplate);
+
+done:
+ if (privKey)
+ SECKEY_DestroyPrivateKey(privKey);
+ if (br && br->responseSignature.signature.data)
+ SECITEM_FreeItem(&br->responseSignature.signature, PR_FALSE);
+ PORT_FreeArena(tmpArena, PR_FALSE);
+
+ return result;
+}
+
+static const SEC_ASN1Template ocsp_OCSPErrorResponseTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTOCSPResponse) },
+ { SEC_ASN1_ENUMERATED,
+ offsetof(CERTOCSPResponse, responseStatus) },
+ { 0, 0,
+ mySEC_NullTemplate },
+ { 0 }
+};
+
+SECItem *
+CERT_CreateEncodedOCSPErrorResponse(PLArenaPool *arena, int error)
+{
+ CERTOCSPResponse response;
+ SECItem *result = NULL;
+
+ switch (error) {
+ case SEC_ERROR_OCSP_MALFORMED_REQUEST:
+ response.statusValue = ocspResponse_malformedRequest;
+ break;
+ case SEC_ERROR_OCSP_SERVER_ERROR:
+ response.statusValue = ocspResponse_internalError;
+ break;
+ case SEC_ERROR_OCSP_TRY_SERVER_LATER:
+ response.statusValue = ocspResponse_tryLater;
+ break;
+ case SEC_ERROR_OCSP_REQUEST_NEEDS_SIG:
+ response.statusValue = ocspResponse_sigRequired;
+ break;
+ case SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST:
+ response.statusValue = ocspResponse_unauthorized;
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ if (!SEC_ASN1EncodeInteger(NULL, &response.responseStatus,
+ response.statusValue))
+ return NULL;
+
+ result = SEC_ASN1EncodeItem(arena, NULL, &response,
+ ocsp_OCSPErrorResponseTemplate);
+
+ SECITEM_FreeItem(&response.responseStatus, PR_FALSE);
+
+ return result;
+}
diff --git a/security/nss/lib/certhigh/ocspt.h b/security/nss/lib/certhigh/ocspt.h
new file mode 100644
index 0000000000..db429ff058
--- /dev/null
+++ b/security/nss/lib/certhigh/ocspt.h
@@ -0,0 +1,301 @@
+/* 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/. */
+
+/*
+ * Public header for exported OCSP types.
+ */
+
+#ifndef _OCSPT_H_
+#define _OCSPT_H_
+
+/*
+ * The following are all opaque types. If someone needs to get at
+ * a field within, then we need to fix the API. Try very hard not
+ * make the type available to them.
+ */
+typedef struct CERTOCSPRequestStr CERTOCSPRequest;
+typedef struct CERTOCSPResponseStr CERTOCSPResponse;
+
+/*
+ * XXX I think only those first two above should need to be exported,
+ * but until I know for certain I am leaving the rest of these here, too.
+ */
+typedef struct CERTOCSPCertIDStr CERTOCSPCertID;
+typedef struct CERTOCSPSingleResponseStr CERTOCSPSingleResponse;
+
+/*
+ * This interface is described in terms of an HttpClient which
+ * supports at least a specified set of functions. (An implementer may
+ * provide HttpClients with additional functionality accessible only to
+ * users with a particular implementation in mind.) The basic behavior
+ * is provided by defining a set of functions, listed in an
+ * SEC_HttpServerFcnStruct. If the implementor of a SpecificHttpClient
+ * registers his SpecificHttpClient as the default HttpClient, then his
+ * functions will be called by the user of an HttpClient, such as an
+ * OCSPChecker.
+ *
+ * The implementer of a specific HttpClient (e.g., the NSS-provided
+ * DefaultHttpClient), populates an SEC_HttpClientFcnStruct, uses it to
+ * register his client, and waits for his functions to be called.
+ *
+ * For future expandability, the SEC_HttpClientFcnStruct is defined as a
+ * union, with the version field acting as a selector. The proposed
+ * initial version of the structure is given following the definition
+ * of the union. The HttpClientState structure is implementation-
+ * dependent, and should be opaque to the user.
+ */
+
+typedef void *SEC_HTTP_SERVER_SESSION;
+typedef void *SEC_HTTP_REQUEST_SESSION;
+
+/*
+ * This function creates a SEC_HTTP_SERVER_SESSION object. The implementer of a
+ * specific HttpClient will allocate the necessary space, when this
+ * function is called, and will free it when the corresponding FreeFcn
+ * is called. The SEC_HTTP_SERVER_SESSION object is passed, as an opaque object,
+ * to subsequent calls.
+ *
+ * If the function returns SECSuccess, the returned SEC_HTTP_SERVER_SESSION
+ * must be cleaned up with a call to SEC_HttpServer_FreeSession,
+ * after processing is finished.
+ */
+typedef SECStatus (*SEC_HttpServer_CreateSessionFcn)(
+ const char *host,
+ PRUint16 portnum,
+ SEC_HTTP_SERVER_SESSION *pSession);
+
+/*
+ * This function is called to allow the implementation to attempt to keep
+ * the connection alive. Depending on the underlying platform, it might
+ * immediately return SECSuccess without having performed any operations.
+ * (If a connection has not been kept alive, a subsequent call to
+ * SEC_HttpRequest_TrySendAndReceiveFcn should reopen the connection
+ * automatically.)
+ *
+ * If the connection uses nonblocking I/O, this function may return
+ * SECWouldBlock and store a nonzero value at "pPollDesc". In that case
+ * the caller may wait on the poll descriptor, and should call this function
+ * again until SECSuccess (and a zero value at "pPollDesc") is obtained.
+ */
+typedef SECStatus (*SEC_HttpServer_KeepAliveSessionFcn)(
+ SEC_HTTP_SERVER_SESSION session,
+ PRPollDesc **pPollDesc);
+
+/*
+ * This function frees the client SEC_HTTP_SERVER_SESSION object, closes all
+ * SEC_HTTP_REQUEST_SESSIONs created for that server, discards all partial results,
+ * frees any memory that was allocated by the client, and invalidates any
+ * response pointers that might have been returned by prior server or request
+ * functions.
+ */
+typedef SECStatus (*SEC_HttpServer_FreeSessionFcn)(
+ SEC_HTTP_SERVER_SESSION session);
+
+/*
+ * This function creates a SEC_HTTP_REQUEST_SESSION object. The implementer of a
+ * specific HttpClient will allocate the necessary space, when this
+ * function is called, and will free it when the corresponding FreeFcn
+ * is called. The SEC_HTTP_REQUEST_SESSION object is passed, as an opaque object,
+ * to subsequent calls.
+ *
+ * An implementation that does not support the requested protocol variant
+ * (usually "http", but could eventually allow "https") or request method
+ * should return SECFailure.
+ *
+ * Timeout values may include the constants PR_INTERVAL_NO_TIMEOUT (wait
+ * forever) or PR_INTERVAL_NO_WAIT (nonblocking I/O).
+ *
+ * If the function returns SECSuccess, the returned SEC_HTTP_REQUEST_SESSION
+ * must be cleaned up with a call to SEC_HttpRequest_FreeSession,
+ * after processing is finished.
+ */
+typedef SECStatus (*SEC_HttpRequest_CreateFcn)(
+ SEC_HTTP_SERVER_SESSION session,
+ const char *http_protocol_variant, /* usually "http" */
+ const char *path_and_query_string,
+ const char *http_request_method,
+ const PRIntervalTime timeout,
+ SEC_HTTP_REQUEST_SESSION *pRequest);
+
+/*
+ * This function sets data to be sent to the server for an HTTP request
+ * of http_request_method == POST. If a particular implementation
+ * supports it, the details for the POST request can be set by calling
+ * this function, prior to activating the request with TrySendAndReceiveFcn.
+ *
+ * An implementation that does not support the POST method should
+ * implement a SetPostDataFcn function that returns immediately.
+ *
+ * Setting http_content_type is optional, the parameter may
+ * by NULL or the empty string.
+ */
+typedef SECStatus (*SEC_HttpRequest_SetPostDataFcn)(
+ SEC_HTTP_REQUEST_SESSION request,
+ const char *http_data,
+ const PRUint32 http_data_len,
+ const char *http_content_type);
+
+/*
+ * This function sets an additional HTTP protocol request header.
+ * If a particular implementation supports it, one or multiple headers
+ * can be added to the request by calling this function once or multiple
+ * times, prior to activating the request with TryFcn.
+ *
+ * An implementation that does not support setting additional headers
+ * should implement an AddRequestHeaderFcn function that returns immediately.
+ */
+typedef SECStatus (*SEC_HttpRequest_AddHeaderFcn)(
+ SEC_HTTP_REQUEST_SESSION request,
+ const char *http_header_name,
+ const char *http_header_value);
+
+/*
+ * This function initiates or continues an HTTP request. After
+ * parameters have been set with the Create function and, optionally,
+ * modified or enhanced with the AddParams function, this call creates
+ * the socket connection and initiates the communication.
+ *
+ * If a timeout value of zero is specified, indicating non-blocking
+ * I/O, the client creates a non-blocking socket, and returns a status
+ * of SECWouldBlock and a non-NULL PRPollDesc if the operation is not
+ * complete. In that case all other return parameters are undefined.
+ * The caller is expected to repeat the call, possibly after using
+ * PRPoll to determine that a completion has occurred, until a return
+ * value of SECSuccess (and a NULL value for pPollDesc) or a return
+ * value of SECFailure (indicating failure on the network level)
+ * is obtained.
+ *
+ * http_response_data_len is both input and output parameter.
+ * If a pointer to a PRUint32 is supplied, the http client is
+ * expected to check the given integer value and always set an out
+ * value, even on failure.
+ * An input value of zero means, the caller will accept any response len.
+ * A different input value indicates the maximum response value acceptable
+ * to the caller.
+ * If data is successfully read and the size is acceptable to the caller,
+ * the function will return SECSuccess and set http_response_data_len to
+ * the size of the block returned in http_response_data.
+ * If the data read from the http server is larger than the acceptable
+ * size, the function will return SECFailure.
+ * http_response_data_len will be set to a value different from zero to
+ * indicate the reason of the failure.
+ * An out value of "0" means, the failure was unrelated to the
+ * acceptable size.
+ * An out value of "1" means, the result data is larger than the
+ * accpeptable size, but the real size is not yet known to the http client
+ * implementation and it stopped retrieving it,
+ * Any other out value combined with a return value of SECFailure
+ * will indicate the actual size of the server data.
+ *
+ * The caller is permitted to provide NULL values for any of the
+ * http_response arguments, indicating the caller is not interested in
+ * those values. If the caller does provide an address, the HttpClient
+ * stores at that address a pointer to the corresponding argument, at
+ * the completion of the operation.
+ *
+ * All returned pointers will be owned by the the HttpClient
+ * implementation and will remain valid until the call to
+ * SEC_HttpRequest_FreeFcn.
+ */
+typedef SECStatus (*SEC_HttpRequest_TrySendAndReceiveFcn)(
+ SEC_HTTP_REQUEST_SESSION request,
+ PRPollDesc **pPollDesc,
+ PRUint16 *http_response_code,
+ const char **http_response_content_type,
+ const char **http_response_headers,
+ const char **http_response_data,
+ PRUint32 *http_response_data_len);
+
+/*
+ * Calling CancelFcn asks for premature termination of the request.
+ *
+ * Future calls to SEC_HttpRequest_TrySendAndReceive should
+ * by avoided, but in this case the HttpClient implementation
+ * is expected to return immediately with SECFailure.
+ *
+ * After calling CancelFcn, a separate call to SEC_HttpRequest_FreeFcn
+ * is still necessary to free resources.
+ */
+typedef SECStatus (*SEC_HttpRequest_CancelFcn)(
+ SEC_HTTP_REQUEST_SESSION request);
+
+/*
+ * Before calling this function, it must be assured the request
+ * has been completed, i.e. either SEC_HttpRequest_TrySendAndReceiveFcn has
+ * returned SECSuccess, or the request has been canceled with
+ * a call to SEC_HttpRequest_CancelFcn.
+ *
+ * This function frees the client state object, closes all sockets,
+ * discards all partial results, frees any memory that was allocated
+ * by the client, and invalidates all response pointers that might
+ * have been returned by SEC_HttpRequest_TrySendAndReceiveFcn
+ */
+typedef SECStatus (*SEC_HttpRequest_FreeFcn)(
+ SEC_HTTP_REQUEST_SESSION request);
+
+typedef struct SEC_HttpClientFcnV1Struct {
+ SEC_HttpServer_CreateSessionFcn createSessionFcn;
+ SEC_HttpServer_KeepAliveSessionFcn keepAliveSessionFcn;
+ SEC_HttpServer_FreeSessionFcn freeSessionFcn;
+ SEC_HttpRequest_CreateFcn createFcn;
+ SEC_HttpRequest_SetPostDataFcn setPostDataFcn;
+ SEC_HttpRequest_AddHeaderFcn addHeaderFcn;
+ SEC_HttpRequest_TrySendAndReceiveFcn trySendAndReceiveFcn;
+ SEC_HttpRequest_CancelFcn cancelFcn;
+ SEC_HttpRequest_FreeFcn freeFcn;
+} SEC_HttpClientFcnV1;
+
+typedef struct SEC_HttpClientFcnStruct {
+ PRInt16 version;
+ union {
+ SEC_HttpClientFcnV1 ftable1;
+ /* SEC_HttpClientFcnV2 ftable2; */
+ /* ... */
+ } fcnTable;
+} SEC_HttpClientFcn;
+
+/*
+ * ocspMode_FailureIsVerificationFailure:
+ * This is the classic behaviour of NSS.
+ * Any OCSP failure is a verification failure (classic mode, default).
+ * Without a good response, OCSP networking will be retried each time
+ * it is required for verifying a cert.
+ *
+ * ocspMode_FailureIsNotAVerificationFailure:
+ * If we fail to obtain a valid OCSP response, consider the
+ * cert as good.
+ * Failed OCSP attempts might get cached and not retried until
+ * minimumSecondsToNextFetchAttempt.
+ * If we are able to obtain a valid response, the cert
+ * will be considered good, if either status is "good"
+ * or the cert was not yet revoked at verification time.
+ *
+ * Additional failure modes might be added in the future.
+ */
+typedef enum {
+ ocspMode_FailureIsVerificationFailure = 0,
+ ocspMode_FailureIsNotAVerificationFailure = 1
+} SEC_OcspFailureMode;
+
+/*
+ * A ResponderID identifies the responder -- or more correctly, the
+ * signer of the response. The ASN.1 definition of a ResponderID is:
+ *
+ * ResponderID ::= CHOICE {
+ * byName [1] EXPLICIT Name,
+ * byKey [2] EXPLICIT KeyHash }
+ *
+ * Because it is CHOICE, the type of identification used and the
+ * identification itself are actually encoded together. To represent
+ * this same information internally, we explicitly define a type and
+ * save it, along with the value, into a data structure.
+ */
+
+typedef enum {
+ ocspResponderID_other = -1, /* unknown kind of responderID */
+ ocspResponderID_byName = 1,
+ ocspResponderID_byKey = 2
+} CERTOCSPResponderIDType;
+
+#endif /* _OCSPT_H_ */
diff --git a/security/nss/lib/certhigh/ocspti.h b/security/nss/lib/certhigh/ocspti.h
new file mode 100644
index 0000000000..d9297dba6a
--- /dev/null
+++ b/security/nss/lib/certhigh/ocspti.h
@@ -0,0 +1,356 @@
+/* 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/. */
+
+/*
+ * Private header defining OCSP types.
+ */
+
+#ifndef _OCSPTI_H_
+#define _OCSPTI_H_
+
+#include "ocspt.h"
+
+#include "certt.h"
+#include "plarena.h"
+#include "seccomon.h"
+#include "secoidt.h"
+
+/*
+ * Some notes about naming conventions...
+ *
+ * The public data types all start with "CERTOCSP" (e.g. CERTOCSPRequest).
+ * (Even the public types are opaque, however. Only their names are
+ * "exported".)
+ *
+ * Internal-only data types drop the "CERT" prefix and use only the
+ * lower-case "ocsp" (e.g. ocspTBSRequest), for brevity sake.
+ *
+ * In either case, the base/suffix of the type name usually matches the
+ * name as defined in the OCSP specification. The exceptions to this are:
+ * - When there is overlap between the "OCSP" or "ocsp" prefix and
+ * the name used in the standard. That is, you cannot strip off the
+ * "CERTOCSP" or "ocsp" prefix and necessarily get the name of the
+ * type as it is defined in the standard; the "real" name will be
+ * *either* "OCSPSuffix" or just "Suffix".
+ * - When the name in the standard was a little too generic. (e.g. The
+ * standard defines "Request" but we call it a "SingleRequest".)
+ * In this case a comment above the type definition calls attention
+ * to the difference.
+ *
+ * The definitions laid out in this header file are intended to follow
+ * the same order as the definitions in the OCSP specification itself.
+ * With the OCSP standard in hand, you should be able to move through
+ * this file and follow along. To future modifiers of this file: please
+ * try to keep it that way. The only exceptions are the few cases where
+ * we need to define a type before it is referenced (e.g. enumerations),
+ * whereas in the OCSP specification these are usually defined the other
+ * way around (reference before definition).
+ */
+
+/*
+ * Forward-declarations of internal-only data structures.
+ *
+ * These are in alphabetical order (case-insensitive); please keep it that way!
+ */
+typedef struct ocspBasicOCSPResponseStr ocspBasicOCSPResponse;
+typedef struct ocspCertStatusStr ocspCertStatus;
+typedef struct ocspResponderIDStr ocspResponderID;
+typedef struct ocspResponseBytesStr ocspResponseBytes;
+typedef struct ocspResponseDataStr ocspResponseData;
+typedef struct ocspRevokedInfoStr ocspRevokedInfo;
+typedef struct ocspServiceLocatorStr ocspServiceLocator;
+typedef struct ocspSignatureStr ocspSignature;
+typedef struct ocspSingleRequestStr ocspSingleRequest;
+typedef struct ocspSingleResponseStr ocspSingleResponse;
+typedef struct ocspTBSRequestStr ocspTBSRequest;
+
+/*
+ * An OCSPRequest; this is what is sent (encoded) to an OCSP responder.
+ */
+struct CERTOCSPRequestStr {
+ PLArenaPool *arena; /* local; not part of encoding */
+ ocspTBSRequest *tbsRequest;
+ ocspSignature *optionalSignature;
+};
+
+/*
+ * A TBSRequest; when an OCSPRequest is signed, the encoding of this
+ * is what the signature is actually applied to. ("TBS" == To Be Signed)
+ * Whether signed or not, however, this structure will be present, and
+ * is the "meat" of the OCSPRequest.
+ *
+ * Note that the "requestorName" field cannot be encoded/decoded in the
+ * same pass as the entire request -- it needs to be handled with a special
+ * call to convert to/from our internal form of a GeneralName. Thus the
+ * "derRequestorName" field, which is the actual DER-encoded bytes.
+ *
+ * The "extensionHandle" field is used on creation only; it holds
+ * in-progress extensions as they are optionally added to the request.
+ */
+struct ocspTBSRequestStr {
+ SECItem version; /* an INTEGER */
+ SECItem *derRequestorName; /* encoded GeneralName; see above */
+ CERTGeneralNameList *requestorName; /* local; not part of encoding */
+ ocspSingleRequest **requestList;
+ CERTCertExtension **requestExtensions;
+ void *extensionHandle; /* local; not part of encoding */
+};
+
+/*
+ * This is the actual signature information for an OCSPRequest (applied to
+ * the TBSRequest structure) or for a BasicOCSPResponse (applied to a
+ * ResponseData structure).
+ *
+ * Note that the "signature" field itself is a BIT STRING; operations on
+ * it need to keep that in mind, converting the length to bytes as needed
+ * and back again afterward (so that the length is usually expressing bits).
+ *
+ * The "cert" field is the signer's certificate. In the case of a received
+ * signature, it will be filled in when the signature is verified. In the
+ * case of a created signature, it is filled in on creation and will be the
+ * cert used to create the signature when the signing-and-encoding occurs,
+ * as well as the cert (and its chain) to fill in derCerts if requested.
+ *
+ * The extra fields cache information about the signature after we have
+ * attempted a verification. "wasChecked", if true, means the signature
+ * has been checked against the appropriate data and thus that "status"
+ * contains the result of that verification. If "status" is not SECSuccess,
+ * "failureReason" is a copy of the error code that was set at the time;
+ * presumably it tells why the signature verification failed.
+ */
+struct ocspSignatureStr {
+ SECAlgorithmID signatureAlgorithm;
+ SECItem signature; /* a BIT STRING */
+ SECItem **derCerts; /* a SEQUENCE OF Certificate */
+ CERTCertificate *cert; /* local; not part of encoding */
+ PRBool wasChecked; /* local; not part of encoding */
+ SECStatus status; /* local; not part of encoding */
+ int failureReason; /* local; not part of encoding */
+};
+
+/*
+ * An OCSPRequest contains a SEQUENCE OF these, one for each certificate
+ * whose status is being checked.
+ *
+ * Note that in the OCSP specification this is just called "Request",
+ * but since that seemed confusing (vs. an OCSPRequest) and to be more
+ * consistent with the parallel type "SingleResponse", I called it a
+ * "SingleRequest".
+ *
+ * XXX figure out how to get rid of that arena -- there must be a way
+ */
+struct ocspSingleRequestStr {
+ PLArenaPool *arena; /* just a copy of the response arena,
+ * needed here for extension handling
+ * routines, on creation only */
+ CERTOCSPCertID *reqCert;
+ CERTCertExtension **singleRequestExtensions;
+};
+
+/*
+ * A CertID is the means of identifying a certificate, used both in requests
+ * and in responses.
+ *
+ * When in a SingleRequest it specifies the certificate to be checked.
+ * When in a SingleResponse it is the cert whose status is being given.
+ */
+struct CERTOCSPCertIDStr {
+ SECAlgorithmID hashAlgorithm;
+ SECItem issuerNameHash; /* an OCTET STRING */
+ SECItem issuerKeyHash; /* an OCTET STRING */
+ SECItem serialNumber; /* an INTEGER */
+ SECItem issuerSHA1NameHash; /* keep other hashes around when */
+ SECItem issuerMD5NameHash; /* we have them */
+ SECItem issuerMD2NameHash;
+ SECItem issuerSHA1KeyHash; /* keep other hashes around when */
+ SECItem issuerMD5KeyHash; /* we have them */
+ SECItem issuerMD2KeyHash;
+ PLArenaPool *poolp;
+};
+
+/*
+ * This describes the value of the responseStatus field in an OCSPResponse.
+ * The corresponding ASN.1 definition is:
+ *
+ * OCSPResponseStatus ::= ENUMERATED {
+ * successful (0), --Response has valid confirmations
+ * malformedRequest (1), --Illegal confirmation request
+ * internalError (2), --Internal error in issuer
+ * tryLater (3), --Try again later
+ * --(4) is not used
+ * sigRequired (5), --Must sign the request
+ * unauthorized (6), --Request unauthorized
+ * }
+ */
+typedef enum {
+ ocspResponse_min = 0,
+ ocspResponse_successful = 0,
+ ocspResponse_malformedRequest = 1,
+ ocspResponse_internalError = 2,
+ ocspResponse_tryLater = 3,
+ ocspResponse_unused = 4,
+ ocspResponse_sigRequired = 5,
+ ocspResponse_unauthorized = 6,
+ ocspResponse_max = 6 /* Please update max when adding values.
+ * Remember to also update arrays, e.g.
+ * "responseStatusNames" in ocspclnt.c
+ * and potentially other places. */
+} ocspResponseStatus;
+
+/*
+ * An OCSPResponse is what is sent (encoded) by an OCSP responder.
+ *
+ * The field "responseStatus" is the ASN.1 encoded value; the field
+ * "statusValue" is simply that same value translated into our local
+ * type ocspResponseStatus.
+ */
+struct CERTOCSPResponseStr {
+ PLArenaPool *arena; /* local; not part of encoding */
+ SECItem responseStatus; /* an ENUMERATED, see above */
+ ocspResponseStatus statusValue; /* local; not part of encoding */
+ ocspResponseBytes *responseBytes; /* only when status is successful */
+};
+
+/*
+ * A ResponseBytes (despite appearances) is what contains the meat
+ * of a successful response -- but still in encoded form. The type
+ * given as "responseType" tells you how to decode the string.
+ *
+ * We look at the OID and translate it into our local OID representation
+ * "responseTypeTag", and use that value to tell us how to decode the
+ * actual response itself. For now the only kind of OCSP response we
+ * know about is a BasicOCSPResponse. However, the intention in the
+ * OCSP specification is to allow for other response types, so we are
+ * building in that flexibility from the start and thus put a pointer
+ * to that data structure inside of a union. Whenever OCSP adds more
+ * response types, just add them to the union.
+ */
+struct ocspResponseBytesStr {
+ SECItem responseType; /* an OBJECT IDENTIFIER */
+ SECOidTag responseTypeTag; /* local; not part of encoding */
+ SECItem response; /* an OCTET STRING */
+ union {
+ ocspBasicOCSPResponse *basic; /* when type is id-pkix-ocsp-basic */
+ } decodedResponse; /* local; not part of encoding */
+};
+
+/*
+ * A BasicOCSPResponse -- when the responseType in a ResponseBytes is
+ * id-pkix-ocsp-basic, the "response" OCTET STRING above is the DER
+ * encoding of one of these.
+ *
+ * Note that in the OCSP specification, the signature fields are not
+ * part of a separate sub-structure. But since they are the same fields
+ * as we define for the signature in a request, it made sense to share
+ * the C data structure here and in some shared code to operate on them.
+ */
+struct ocspBasicOCSPResponseStr {
+ SECItem tbsResponseDataDER;
+ ocspResponseData *tbsResponseData; /* "tbs" == To Be Signed */
+ ocspSignature responseSignature;
+};
+
+/*
+ * A ResponseData is the part of a BasicOCSPResponse that is signed
+ * (after it is DER encoded). It contains the real details of the response
+ * (a per-certificate status).
+ */
+struct ocspResponseDataStr {
+ SECItem version; /* an INTEGER */
+ SECItem derResponderID;
+ ocspResponderID *responderID; /* local; not part of encoding */
+ SECItem producedAt; /* a GeneralizedTime */
+ CERTOCSPSingleResponse **responses;
+ CERTCertExtension **responseExtensions;
+};
+
+struct ocspResponderIDStr {
+ CERTOCSPResponderIDType responderIDType; /* local; not part of encoding */
+ union {
+ CERTName name; /* when ocspResponderID_byName */
+ SECItem keyHash; /* when ocspResponderID_byKey */
+ SECItem other; /* when ocspResponderID_other */
+ } responderIDValue;
+};
+
+/*
+ * The ResponseData in a BasicOCSPResponse contains a SEQUENCE OF
+ * SingleResponse -- one for each certificate whose status is being supplied.
+ *
+ * XXX figure out how to get rid of that arena -- there must be a way
+ */
+struct CERTOCSPSingleResponseStr {
+ PLArenaPool *arena; /* just a copy of the response arena,
+ * needed here for extension handling
+ * routines, on creation only */
+ CERTOCSPCertID *certID;
+ SECItem derCertStatus;
+ ocspCertStatus *certStatus; /* local; not part of encoding */
+ SECItem thisUpdate; /* a GeneralizedTime */
+ SECItem *nextUpdate; /* a GeneralizedTime */
+ CERTCertExtension **singleExtensions;
+};
+
+/*
+ * A CertStatus is the actual per-certificate status. Its ASN.1 definition:
+ *
+ * CertStatus ::= CHOICE {
+ * good [0] IMPLICIT NULL,
+ * revoked [1] IMPLICIT RevokedInfo,
+ * unknown [2] IMPLICIT UnknownInfo }
+ *
+ * (where for now UnknownInfo is defined to be NULL but in the
+ * future may be replaced with an enumeration).
+ *
+ * Because it is CHOICE, the status value and its associated information
+ * (if any) are actually encoded together. To represent this same
+ * information internally, we explicitly define a type and save it,
+ * along with the value, into a data structure.
+ */
+
+typedef enum {
+ ocspCertStatus_good, /* cert is not revoked */
+ ocspCertStatus_revoked, /* cert is revoked */
+ ocspCertStatus_unknown, /* cert was unknown to the responder */
+ ocspCertStatus_other /* status was not an expected value */
+} ocspCertStatusType;
+
+/*
+ * This is the actual per-certificate status.
+ *
+ * The "goodInfo" and "unknownInfo" items are only place-holders for a NULL.
+ * (Though someday OCSP may replace UnknownInfo with an enumeration that
+ * gives more detailed information.)
+ */
+struct ocspCertStatusStr {
+ ocspCertStatusType certStatusType; /* local; not part of encoding */
+ union {
+ SECItem *goodInfo; /* when ocspCertStatus_good */
+ ocspRevokedInfo *revokedInfo; /* when ocspCertStatus_revoked */
+ SECItem *unknownInfo; /* when ocspCertStatus_unknown */
+ SECItem *otherInfo; /* when ocspCertStatus_other */
+ } certStatusInfo;
+};
+
+/*
+ * A RevokedInfo gives information about a revoked certificate -- when it
+ * was revoked and why.
+ */
+struct ocspRevokedInfoStr {
+ SECItem revocationTime; /* a GeneralizedTime */
+ SECItem *revocationReason; /* a CRLReason; ignored for now */
+};
+
+/*
+ * ServiceLocator can be included as one of the singleRequestExtensions.
+ * When added, it specifies the (name of the) issuer of the cert being
+ * checked, and optionally the value of the AuthorityInfoAccess extension
+ * if the cert has one.
+ */
+struct ocspServiceLocatorStr {
+ CERTName *issuer;
+ SECItem locator; /* DER encoded authInfoAccess extension from cert */
+};
+
+#endif /* _OCSPTI_H_ */
diff --git a/security/nss/lib/certhigh/xcrldist.c b/security/nss/lib/certhigh/xcrldist.c
new file mode 100644
index 0000000000..4f74cdb259
--- /dev/null
+++ b/security/nss/lib/certhigh/xcrldist.c
@@ -0,0 +1,212 @@
+/* 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 x.509 v3 CRL Distribution Point extension.
+ */
+#include "genname.h"
+#include "certt.h"
+#include "secerr.h"
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+SEC_ASN1_MKSUB(SEC_BitStringTemplate)
+
+extern void PrepareBitStringForEncoding(SECItem *bitMap, SECItem *value);
+
+static const SEC_ASN1Template FullNameTemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0,
+ offsetof(CRLDistributionPoint, derFullName),
+ CERT_GeneralNamesTemplate }
+};
+
+static const SEC_ASN1Template RelativeNameTemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 1,
+ offsetof(CRLDistributionPoint, distPoint.relativeName),
+ CERT_RDNTemplate }
+};
+
+static const SEC_ASN1Template DistributionPointNameTemplate[] = {
+ { SEC_ASN1_CHOICE,
+ offsetof(CRLDistributionPoint, distPointType), NULL,
+ sizeof(CRLDistributionPoint) },
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0,
+ offsetof(CRLDistributionPoint, derFullName),
+ CERT_GeneralNamesTemplate, generalName },
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 1,
+ offsetof(CRLDistributionPoint, distPoint.relativeName),
+ CERT_RDNTemplate, relativeDistinguishedName },
+ { 0 }
+};
+
+static const SEC_ASN1Template CRLDistributionPointTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CRLDistributionPoint) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_XTRN | 0,
+ offsetof(CRLDistributionPoint, derDistPoint),
+ SEC_ASN1_SUB(SEC_AnyTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
+ offsetof(CRLDistributionPoint, bitsmap),
+ SEC_ASN1_SUB(SEC_BitStringTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_CONSTRUCTED | 2,
+ offsetof(CRLDistributionPoint, derCrlIssuer),
+ CERT_GeneralNamesTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template CERTCRLDistributionPointsTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF, 0, CRLDistributionPointTemplate }
+};
+
+SECStatus
+CERT_EncodeCRLDistributionPoints(PLArenaPool *arena,
+ CERTCrlDistributionPoints *value,
+ SECItem *derValue)
+{
+ CRLDistributionPoint **pointList, *point;
+ PLArenaPool *ourPool = NULL;
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(derValue);
+ PORT_Assert(value && value->distPoints);
+
+ do {
+ ourPool = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (ourPool == NULL) {
+ rv = SECFailure;
+ break;
+ }
+
+ pointList = value->distPoints;
+ while (*pointList) {
+ point = *pointList;
+ point->derFullName = NULL;
+ point->derDistPoint.data = NULL;
+
+ switch (point->distPointType) {
+ case generalName:
+ point->derFullName = cert_EncodeGeneralNames(ourPool, point->distPoint.fullName);
+
+ if (!point->derFullName ||
+ !SEC_ASN1EncodeItem(ourPool, &point->derDistPoint,
+ point, FullNameTemplate))
+ rv = SECFailure;
+ break;
+
+ case relativeDistinguishedName:
+ if (!SEC_ASN1EncodeItem(ourPool, &point->derDistPoint,
+ point, RelativeNameTemplate))
+ rv = SECFailure;
+ break;
+
+ default:
+ PORT_SetError(SEC_ERROR_EXTENSION_VALUE_INVALID);
+ rv = SECFailure;
+ break;
+ }
+
+ if (rv != SECSuccess)
+ break;
+
+ if (point->reasons.data)
+ PrepareBitStringForEncoding(&point->bitsmap, &point->reasons);
+
+ if (point->crlIssuer) {
+ point->derCrlIssuer = cert_EncodeGeneralNames(ourPool, point->crlIssuer);
+ if (!point->derCrlIssuer) {
+ rv = SECFailure;
+ break;
+ }
+ }
+ ++pointList;
+ }
+ if (rv != SECSuccess)
+ break;
+ if (!SEC_ASN1EncodeItem(arena, derValue, value,
+ CERTCRLDistributionPointsTemplate)) {
+ rv = SECFailure;
+ break;
+ }
+ } while (0);
+ PORT_FreeArena(ourPool, PR_FALSE);
+ return rv;
+}
+
+CERTCrlDistributionPoints *
+CERT_DecodeCRLDistributionPoints(PLArenaPool *arena, SECItem *encodedValue)
+{
+ CERTCrlDistributionPoints *value = NULL;
+ CRLDistributionPoint **pointList, *point;
+ SECStatus rv = SECSuccess;
+ SECItem newEncodedValue;
+
+ PORT_Assert(arena);
+ do {
+ value = PORT_ArenaZNew(arena, CERTCrlDistributionPoints);
+ if (value == NULL) {
+ rv = SECFailure;
+ break;
+ }
+
+ /* copy the DER into the arena, since Quick DER returns data that points
+ into the DER input, which may get freed by the caller */
+ rv = SECITEM_CopyItem(arena, &newEncodedValue, encodedValue);
+ if (rv != SECSuccess)
+ break;
+
+ rv = SEC_QuickDERDecodeItem(arena, &value->distPoints,
+ CERTCRLDistributionPointsTemplate, &newEncodedValue);
+ if (rv != SECSuccess)
+ break;
+
+ pointList = value->distPoints;
+ while (NULL != (point = *pointList)) {
+
+ /* get the data if the distributionPointName is not omitted */
+ if (point->derDistPoint.data != NULL) {
+ rv = SEC_QuickDERDecodeItem(arena, point,
+ DistributionPointNameTemplate, &(point->derDistPoint));
+ if (rv != SECSuccess)
+ break;
+
+ switch (point->distPointType) {
+ case generalName:
+ point->distPoint.fullName =
+ cert_DecodeGeneralNames(arena, point->derFullName);
+ rv = point->distPoint.fullName ? SECSuccess : SECFailure;
+ break;
+
+ case relativeDistinguishedName:
+ break;
+
+ default:
+ PORT_SetError(SEC_ERROR_EXTENSION_VALUE_INVALID);
+ rv = SECFailure;
+ break;
+ } /* end switch */
+ if (rv != SECSuccess)
+ break;
+ } /* end if */
+
+ /* Get the reason code if it's not omitted in the encoding */
+ if (point->bitsmap.data != NULL) {
+ SECItem bitsmap = point->bitsmap;
+ DER_ConvertBitString(&bitsmap);
+ rv = SECITEM_CopyItem(arena, &point->reasons, &bitsmap);
+ if (rv != SECSuccess)
+ break;
+ }
+
+ /* Get the crl issuer name if it's not omitted in the encoding */
+ if (point->derCrlIssuer != NULL) {
+ point->crlIssuer = cert_DecodeGeneralNames(arena,
+ point->derCrlIssuer);
+ if (!point->crlIssuer)
+ break;
+ }
+ ++pointList;
+ } /* end while points remain */
+ } while (0);
+ return (rv == SECSuccess ? value : NULL);
+}