summaryrefslogtreecommitdiff
path: root/security/nss/lib/certhigh
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/certhigh')
-rw-r--r--security/nss/lib/certhigh/Makefile76
-rw-r--r--security/nss/lib/certhigh/certhigh.c1194
-rw-r--r--security/nss/lib/certhigh/certhtml.c605
-rw-r--r--security/nss/lib/certhigh/certreq.c230
-rw-r--r--security/nss/lib/certhigh/certvfy.c2111
-rw-r--r--security/nss/lib/certhigh/config.mk43
-rw-r--r--security/nss/lib/certhigh/crlv2.c126
-rw-r--r--security/nss/lib/certhigh/manifest.mn59
-rw-r--r--security/nss/lib/certhigh/ocsp.c3998
-rw-r--r--security/nss/lib/certhigh/ocsp.h534
-rw-r--r--security/nss/lib/certhigh/ocspt.h59
-rw-r--r--security/nss/lib/certhigh/ocspti.h405
-rw-r--r--security/nss/lib/certhigh/xcrldist.c230
13 files changed, 9670 insertions, 0 deletions
diff --git a/security/nss/lib/certhigh/Makefile b/security/nss/lib/certhigh/Makefile
new file mode 100644
index 000000000..08b7137d5
--- /dev/null
+++ b/security/nss/lib/certhigh/Makefile
@@ -0,0 +1,76 @@
+#! gmake
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+-include config.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+export:: private_export
+
diff --git a/security/nss/lib/certhigh/certhigh.c b/security/nss/lib/certhigh/certhigh.c
new file mode 100644
index 000000000..5d8c59a9f
--- /dev/null
+++ b/security/nss/lib/certhigh/certhigh.c
@@ -0,0 +1,1194 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+#include "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"
+
+#ifndef NSS_3_4_CODE
+#define NSS_3_4_CODE
+#endif
+#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;
+ char *token = NULL;
+ int len;
+
+ /* 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) {
+ token=name1;
+ nickname1=token1;
+ nickname2=name2;
+ } else {
+ token=name2;
+ nickname1=token2;
+ nickname2=name1;
+ }
+ len = nickname1-token;
+ nickname1++;
+ if (PORT_Strcmp(nickname1,nickname2) != 0) {
+ return PR_FALSE;
+ }
+ /* 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;
+ int64 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;
+
+ 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,
+ char *nickname,
+ SECCertUsage usage,
+ PRBool validOnly,
+ void *proto_win)
+{
+ CERTCertificate *cert = NULL;
+ CERTCertList *certList = NULL;
+ SECStatus rv;
+ int64 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 ) {
+ /* 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_END(CERT_LIST_HEAD(certList), 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 ) {
+ 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 ) {
+ return(PR_FAILURE);
+ }
+
+ /* copy the string */
+ len = PORT_Strlen(nickname) + 1;
+ node->string = (char*)PORT_ArenaAlloc(names->arena, len);
+ if ( node->string == NULL ) {
+ if (nickname) 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)
+{
+ PRArenaPool *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, 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->trust ) {
+ trust = cert->trust;
+
+ /* 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_GetSSLCACerts(CERTCertDBHandle *handle)
+{
+ PRArenaPool *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_DistNamesFromNicknames(CERTCertDBHandle *handle, char **nicknames,
+ int nnames)
+{
+ CERTDistNames *dnames = NULL;
+ PRArenaPool *arena;
+ int i, rv;
+ SECItem *names = NULL;
+ CERTCertificate *cert = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) goto loser;
+ dnames = (CERTDistNames*)PORT_Alloc(sizeof(CERTDistNames));
+ if (dnames == NULL) goto loser;
+
+ dnames->arena = arena;
+ dnames->nnames = nnames;
+ dnames->names = names = (SECItem*)PORT_Alloc(nnames * sizeof(SECItem));
+ 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;
+ PRArenaPool *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 ) {
+ goto loser;
+ }
+
+ 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;
+
+ encodedExtenValue.data = NULL;
+ encodedExtenValue.len = 0;
+
+ rv = cert_FindExtension(cert->extensions, SEC_OID_X509_CRL_DIST_POINTS,
+ &encodedExtenValue);
+ if ( rv != SECSuccess ) {
+ return (NULL);
+ }
+
+ return (CERT_DecodeCRLDistributionPoints (cert->arena,
+ &encodedExtenValue));
+}
+
+/* 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;
+
+ 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 */
+ if (cert->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;
+ }
+
+ CERT_DestroyCertificate(cert);
+ cert = NULL;
+
+ 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)
+{
+#ifdef NSS_CLASSIC
+ CERTCertificateList *chain = NULL;
+ CERTCertificate *c;
+ SECItem *p;
+ int rv, len = 0;
+ PRArenaPool *tmpArena, *arena;
+ certNode *head, *tail, *node;
+
+ /*
+ * Initialize stuff so we can goto loser.
+ */
+ head = NULL;
+ arena = NULL;
+
+ /* arena for linked list */
+ tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (tmpArena == NULL) goto no_memory;
+
+ /* arena for SecCertificateList */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) goto no_memory;
+
+ head = tail = (certNode*)PORT_ArenaZAlloc(tmpArena, sizeof(certNode));
+ if (head == NULL) goto no_memory;
+
+ /* put primary cert first in the linked list */
+ head->cert = c = CERT_DupCertificate(cert);
+ if (head->cert == NULL) goto loser;
+ len++;
+
+ /* add certs until we come to a self-signed one */
+ while(SECITEM_CompareItem(&c->derIssuer, &c->derSubject) != SECEqual) {
+ c = CERT_FindCertIssuer(tail->cert, PR_Now(), usage);
+ if (c == NULL) {
+ /* no root is found, so make sure we don't attempt to delete one
+ * below
+ */
+ includeRoot = PR_TRUE;
+ break;
+ }
+
+ tail->next = (certNode*)PORT_ArenaZAlloc(tmpArena, sizeof(certNode));
+ tail = tail->next;
+ if (tail == NULL) goto no_memory;
+
+ tail->cert = c;
+ len++;
+ }
+
+ /* now build the CERTCertificateList */
+ chain = (CERTCertificateList *)PORT_ArenaAlloc(arena, sizeof(CERTCertificateList));
+ if (chain == NULL) goto no_memory;
+ chain->certs = (SECItem*)PORT_ArenaAlloc(arena, len * sizeof(SECItem));
+ if (chain->certs == NULL) goto no_memory;
+
+ for(node = head, p = chain->certs; node; node = node->next, p++) {
+ rv = SECITEM_CopyItem(arena, p, &node->cert->derCert);
+ CERT_DestroyCertificate(node->cert);
+ node->cert = NULL;
+ if (rv < 0) goto loser;
+ }
+ if ( !includeRoot && len > 1) {
+ chain->len = len - 1;
+ } else {
+ chain->len = len;
+ }
+
+ chain->arena = arena;
+
+ PORT_FreeArena(tmpArena, PR_FALSE);
+
+ return chain;
+
+no_memory:
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+loser:
+ if (head != NULL) {
+ for (node = head; node; node = node->next) {
+ if (node->cert != NULL)
+ CERT_DestroyCertificate(node->cert);
+ }
+ }
+
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ if (tmpArena != NULL) {
+ PORT_FreeArena(tmpArena, PR_FALSE);
+ }
+
+ return NULL;
+#else
+ CERTCertificateList *chain = NULL;
+ NSSCertificate **stanChain;
+ NSSCertificate *stanCert;
+ PRArenaPool *arena;
+ NSSUsage nssUsage;
+ int i, len;
+
+ stanCert = STAN_GetNSSCertificate(cert);
+ nssUsage.anyUsage = PR_FALSE;
+ nssUsage.nss3usage = usage;
+ nssUsage.nss3lookingForCA = PR_FALSE;
+ stanChain = NSSCertificate_BuildChain(stanCert, NULL, &nssUsage, NULL,
+ NULL, 0, NULL, NULL);
+ if (!stanChain) {
+ 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;
+ SECITEM_CopyItem(arena, &chain->certs[i], &derCert);
+ 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;
+#endif
+}
+
+/* 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;
+ PRArenaPool *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(CERTCertificateList * oldList)
+{
+ CERTCertificateList *newList = NULL;
+ PRArenaPool *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/certhtml.c b/security/nss/lib/certhigh/certhtml.c
new file mode 100644
index 000000000..e4e1215c2
--- /dev/null
+++ b/security/nss/lib/certhigh/certhtml.c
@@ -0,0 +1,605 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * certhtml.c --- convert a cert to html
+ *
+ * $Id$
+ */
+
+#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;
+}
+
+static char *
+gatherStrings(char **strings)
+{
+ char **strs;
+ int len;
+ char *ret;
+ char *s;
+
+ /* find total length of all strings */
+ strs = strings;
+ len = 0;
+ while ( *strs ) {
+ len += PORT_Strlen(*strs);
+ strs++;
+ }
+
+ /* alloc enough memory for it */
+ ret = (char*)PORT_Alloc(len + 1);
+ if ( !ret ) {
+ return(ret);
+ }
+
+ s = ret;
+
+ /* copy the strings */
+ strs = strings;
+ while ( *strs ) {
+ PORT_Strcpy(s, *strs);
+ s += PORT_Strlen(*strs);
+ strs++;
+ }
+
+ return( ret );
+}
+
+#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:
+ cn = CERT_DecodeAVAValue(&ava->value);
+ len += cn->len;
+ break;
+ case SEC_OID_AVA_COUNTRY_NAME:
+ country = CERT_DecodeAVAValue(&ava->value);
+ len += country->len;
+ break;
+ case SEC_OID_AVA_LOCALITY:
+ loc = CERT_DecodeAVAValue(&ava->value);
+ len += loc->len;
+ break;
+ case SEC_OID_AVA_STATE_OR_PROVINCE:
+ state = CERT_DecodeAVAValue(&ava->value);
+ len += state->len;
+ break;
+ case SEC_OID_AVA_ORGANIZATION_NAME:
+ org = CERT_DecodeAVAValue(&ava->value);
+ len += org->len;
+ break;
+ case SEC_OID_AVA_DN_QUALIFIER:
+ dq = CERT_DecodeAVAValue(&ava->value);
+ len += dq->len;
+ break;
+ case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME:
+ if (ou_count < MAX_OUS) {
+ orgunit[ou_count] = CERT_DecodeAVAValue(&ava->value);
+ len += orgunit[ou_count++]->len;
+ }
+ break;
+ case SEC_OID_AVA_DC:
+ if (dc_count < MAX_DC) {
+ dc[dc_count] = CERT_DecodeAVAValue(&ava->value);
+ len += dc[dc_count++]->len;
+ }
+ break;
+ case SEC_OID_PKCS9_EMAIL_ADDRESS:
+ case SEC_OID_RFC1274_MAIL:
+ email = CERT_DecodeAVAValue(&ava->value);
+ len += email->len;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /* XXX - add some for formatting */
+ len += 128;
+
+ /* allocate buffer */
+ buf = (char *)PORT_Alloc(len);
+ if ( !buf ) {
+ return(0);
+ }
+
+ tmpbuf = buf;
+
+ if ( cn ) {
+ PORT_Memcpy(tmpbuf, cn->data, cn->len);
+ tmpbuf += cn->len;
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ SECITEM_FreeItem(cn, PR_TRUE);
+ }
+ if ( email ) {
+ PORT_Memcpy(tmpbuf, email->data, email->len);
+ tmpbuf += ( email->len );
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ SECITEM_FreeItem(email, PR_TRUE);
+ }
+ 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;
+ SECITEM_FreeItem(orgunit[i], PR_TRUE);
+ }
+ if ( dq ) {
+ PORT_Memcpy(tmpbuf, dq->data, dq->len);
+ tmpbuf += ( dq->len );
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ SECITEM_FreeItem(dq, PR_TRUE);
+ }
+ if ( org ) {
+ PORT_Memcpy(tmpbuf, org->data, org->len);
+ tmpbuf += ( org->len );
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ SECITEM_FreeItem(org, PR_TRUE);
+ }
+ 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;
+ SECITEM_FreeItem(dc[i], PR_TRUE);
+ }
+ first = PR_TRUE;
+ if ( loc ) {
+ PORT_Memcpy(tmpbuf, loc->data, loc->len);
+ tmpbuf += ( loc->len );
+ first = PR_FALSE;
+ SECITEM_FreeItem(loc, PR_TRUE);
+ }
+ if ( state ) {
+ if ( !first ) {
+ PORT_Memcpy(tmpbuf, COMMA, COMMALEN);
+ tmpbuf += COMMALEN;
+ }
+ PORT_Memcpy(tmpbuf, state->data, state->len);
+ tmpbuf += ( state->len );
+ first = PR_FALSE;
+ SECITEM_FreeItem(state, PR_TRUE);
+ }
+ if ( country ) {
+ if ( !first ) {
+ PORT_Memcpy(tmpbuf, COMMA, COMMALEN);
+ tmpbuf += COMMALEN;
+ }
+ PORT_Memcpy(tmpbuf, country->data, country->len);
+ tmpbuf += ( country->len );
+ first = PR_FALSE;
+ SECITEM_FreeItem(country, PR_TRUE);
+ }
+ if ( !first ) {
+ PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
+ tmpbuf += BREAKLEN;
+ }
+
+ *tmpbuf = 0;
+
+ return(buf);
+}
+
+static char *sec_FortezzaClearance(SECItem *clearance) {
+ unsigned char clr = 0;
+
+ if (clearance->len > 0) { clr = clearance->data[0]; }
+
+ if (clr & 0x4) return "Top Secret";
+ if (clr & 0x8) return "Secret";
+ if (clr & 0x10) return "Confidential";
+ if (clr & 0x20) return "Sensitive";
+ if (clr & 0x40) return "Unclassified";
+ return "None";
+}
+
+static char *sec_FortezzaMessagePrivilege(SECItem *priv) {
+ unsigned char clr = 0;
+
+ if (priv->len > 0) { clr = (priv->data[0]) & 0x78; }
+
+ if (clr == 0x00) {
+ return "None";
+ } else {
+
+ return PR_smprintf("%s%s%s%s%s%s%s",
+
+ clr&0x40?"Critical/Flash":"",
+ (clr&0x40) && (clr&0x38) ? ", " : "" ,
+
+ clr&0x20?"Immediate/Priority":"",
+ (clr&0x20) && (clr&0x18) ? ", " : "" ,
+
+ clr&0x10?"Routine/Deferred":"",
+ (clr&0x10) && (clr&0x08) ? ", " : "" ,
+
+ clr&0x08?"Rekey Agent":"");
+ }
+
+}
+
+static char *sec_FortezzaCertPrivilege(SECItem *priv) {
+ unsigned char clr = 0;
+
+ if (priv->len > 0) { clr = priv->data[0]; }
+
+ return PR_smprintf("%s%s%s%s%s%s%s%s%s%s%s%s",
+ clr&0x40?"Organizational Releaser":"",
+ (clr&0x40) && (clr&0x3e) ? "," : "" ,
+ clr&0x20?"Policy Creation Authority":"",
+ (clr&0x20) && (clr&0x1e) ? "," : "" ,
+ clr&0x10?"Certificate Authority":"",
+ (clr&0x10) && (clr&0x0e) ? "," : "" ,
+ clr&0x08?"Local Managment Authority":"",
+ (clr&0x08) && (clr&0x06) ? "," : "" ,
+ clr&0x04?"Configuration Vector Authority":"",
+ (clr&0x04) && (clr&0x02) ? "," : "" ,
+ clr&0x02?"No Signature Capability":"",
+ clr&0x7e?"":"Signing Only"
+ );
+}
+
+static char *htmlcertstrings[] = {
+ "<table border=0 cellspacing=0 cellpadding=0><tr><td valign=top>"
+ "<font size=2><b>This Certificate belongs to:</b><br>"
+ "<table border=0 cellspacing=0 cellpadding=0><tr><td>",
+ 0, /* image goes here */
+ 0,
+ 0,
+ "</td><td width=10> </td><td><font size=2>",
+ 0, /* subject name goes here */
+ "</td></tr></table></font></td><td width=20> </td><td valign=top>"
+ "<font size=2><b>This Certificate was issued by:</b><br>"
+ "<table border=0 cellspacing=0 cellpadding=0><tr><td>",
+ 0, /* image goes here */
+ 0,
+ 0,
+ "</td><td width=10> </td><td><font size=2>",
+ 0, /* issuer name goes here */
+ "</td></tr></table></font></td></tr></table>"
+ "<b>Serial Number:</b> ",
+ 0,
+ "<br><b>This Certificate is valid from ",
+ 0, /* notBefore goes here */
+ " to ",
+ 0, /* notAfter does here */
+ "</b><br><b>Clearance:</b>",
+ 0,
+ "<br><b>DSS Privileges:</b>",
+ 0,
+ "<br><b>KEA Privileges:</b>",
+ 0,
+ "<br><b>KMID:</b>",
+ 0,
+ "<br><b>Certificate Fingerprint:</b>"
+ "<table border=0 cellspacing=0 cellpadding=0><tr>"
+ "<td width=10> </td><td><font size=2>",
+ 0, /* fingerprint goes here */
+ "</td></tr></table>",
+ 0, /* comment header goes here */
+ 0, /* comment goes here */
+ 0, /* comment trailer goes here */
+ 0
+};
+
+char *
+CERT_HTMLCertInfo(CERTCertificate *cert, PRBool showImages, PRBool showIssuer)
+{
+ SECStatus rv;
+ char *issuer, *subject, *serialNumber, *version;
+ char *notBefore, *notAfter;
+ char *ret;
+ char *nickname;
+ SECItem dummyitem;
+ unsigned char fingerprint[16]; /* result of MD5, always 16 bytes */
+ char *fpstr;
+ SECItem fpitem;
+ char *commentstring = NULL;
+ SECKEYPublicKey *pubk;
+ char *DSSPriv;
+ char *KMID = NULL;
+ char *servername;
+
+ if (!cert) {
+ return(0);
+ }
+
+ issuer = CERT_FormatName (&cert->issuer);
+ subject = CERT_FormatName (&cert->subject);
+ version = CERT_Hexify (&cert->version,1);
+ serialNumber = CERT_Hexify (&cert->serialNumber,1);
+ notBefore = DER_TimeChoiceDayToAscii(&cert->validity.notBefore);
+ notAfter = DER_TimeChoiceDayToAscii(&cert->validity.notAfter);
+ servername = CERT_FindNSStringExtension(cert,
+ SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME);
+
+ nickname = cert->nickname;
+ if ( nickname == NULL ) {
+ showImages = PR_FALSE;
+ }
+
+ dummyitem.data = NULL;
+ rv = CERT_FindCertExtension(cert, SEC_OID_NS_CERT_EXT_SUBJECT_LOGO,
+ &dummyitem);
+ if ( dummyitem.data ) {
+ PORT_Free(dummyitem.data);
+ }
+
+ if ( rv || !showImages ) {
+ htmlcertstrings[1] = "";
+ htmlcertstrings[2] = "";
+ htmlcertstrings[3] = "";
+ } else {
+ htmlcertstrings[1] = "<img src=\"about:security?subject-logo=";
+ htmlcertstrings[2] = nickname;
+ htmlcertstrings[3] = "\">";
+ }
+
+ if ( servername ) {
+ char *tmpstr;
+ tmpstr = (char *)PORT_Alloc(PORT_Strlen(subject) +
+ PORT_Strlen(servername) +
+ sizeof("<br>") + 1);
+ if ( tmpstr ) {
+ PORT_Strcpy(tmpstr, servername);
+ PORT_Strcat(tmpstr, "<br>");
+ PORT_Strcat(tmpstr, subject);
+ PORT_Free(subject);
+ subject = tmpstr;
+ }
+ }
+
+ htmlcertstrings[5] = subject;
+
+ dummyitem.data = NULL;
+
+ rv = CERT_FindCertExtension(cert, SEC_OID_NS_CERT_EXT_ISSUER_LOGO,
+ &dummyitem);
+ if ( dummyitem.data ) {
+ PORT_Free(dummyitem.data);
+ }
+
+ if ( rv || !showImages ) {
+ htmlcertstrings[7] = "";
+ htmlcertstrings[8] = "";
+ htmlcertstrings[9] = "";
+ } else {
+ htmlcertstrings[7] = "<img src=\"about:security?issuer-logo=";
+ htmlcertstrings[8] = nickname;
+ htmlcertstrings[9] = "\">";
+ }
+
+
+ if (showIssuer == PR_TRUE) {
+ htmlcertstrings[11] = issuer;
+ } else {
+ htmlcertstrings[11] = "";
+ }
+
+ htmlcertstrings[13] = serialNumber;
+ htmlcertstrings[15] = notBefore;
+ htmlcertstrings[17] = notAfter;
+
+ pubk = CERT_ExtractPublicKey(cert);
+ DSSPriv = NULL;
+ if (pubk && (pubk->keyType == fortezzaKey)) {
+ htmlcertstrings[18] = "</b><br><b>Clearance:</b>";
+ htmlcertstrings[19] = sec_FortezzaClearance(
+ &pubk->u.fortezza.clearance);
+ htmlcertstrings[20] = "<br><b>DSS Privileges:</b>";
+ DSSPriv = sec_FortezzaCertPrivilege(
+ &pubk->u.fortezza.DSSpriviledge);
+ htmlcertstrings[21] = DSSPriv;
+ htmlcertstrings[22] = "<br><b>KEA Privileges:</b>";
+ htmlcertstrings[23] = sec_FortezzaMessagePrivilege(
+ &pubk->u.fortezza.KEApriviledge);
+ htmlcertstrings[24] = "<br><b>KMID:</b>";
+ dummyitem.data = &pubk->u.fortezza.KMID[0];
+ dummyitem.len = sizeof(pubk->u.fortezza.KMID);
+ KMID = CERT_Hexify (&dummyitem,0);
+ htmlcertstrings[25] = KMID;
+ } else {
+ /* clear out the headers in the non-fortezza cases */
+ htmlcertstrings[18] = "";
+ htmlcertstrings[19] = "";
+ htmlcertstrings[20] = "";
+ htmlcertstrings[21] = "";
+ htmlcertstrings[22] = "";
+ htmlcertstrings[23] = "";
+ htmlcertstrings[24] = "";
+ htmlcertstrings[25] = "</b>";
+ }
+
+ if (pubk) {
+ SECKEY_DestroyPublicKey(pubk);
+ }
+
+#define HTML_OFF 27
+ rv = PK11_HashBuf(SEC_OID_MD5, fingerprint,
+ cert->derCert.data, cert->derCert.len);
+
+ fpitem.data = fingerprint;
+ fpitem.len = sizeof(fingerprint);
+
+ fpstr = CERT_Hexify (&fpitem,1);
+
+ htmlcertstrings[HTML_OFF] = fpstr;
+
+ commentstring = CERT_GetCertCommentString(cert);
+
+ if (commentstring == NULL) {
+ htmlcertstrings[HTML_OFF+2] = "";
+ htmlcertstrings[HTML_OFF+3] = "";
+ htmlcertstrings[HTML_OFF+4] = "";
+ } else {
+ htmlcertstrings[HTML_OFF+2] =
+ "<b>Comment:</b>"
+ "<table border=0 cellspacing=0 cellpadding=0><tr>"
+ "<td width=10> </td><td><font size=3>"
+ "<textarea name=foobar rows=4 cols=55 onfocus=\"this.blur()\">";
+ htmlcertstrings[HTML_OFF+3] = commentstring;
+ htmlcertstrings[HTML_OFF+4] = "</textarea></font></td></tr></table>";
+ }
+
+ ret = gatherStrings(htmlcertstrings);
+
+ if ( issuer ) {
+ PORT_Free(issuer);
+ }
+
+ if ( subject ) {
+ PORT_Free(subject);
+ }
+
+ if ( version ) {
+ PORT_Free(version);
+ }
+
+ if ( serialNumber ) {
+ PORT_Free(serialNumber);
+ }
+
+ if ( notBefore ) {
+ PORT_Free(notBefore);
+ }
+
+ if ( notAfter ) {
+ PORT_Free(notAfter);
+ }
+
+ if ( fpstr ) {
+ PORT_Free(fpstr);
+ }
+ if (DSSPriv) {
+ PORT_Free(DSSPriv);
+ }
+
+ if (KMID) {
+ PORT_Free(KMID);
+ }
+
+ if ( commentstring ) {
+ PORT_Free(commentstring);
+ }
+
+ if ( servername ) {
+ PORT_Free(servername);
+ }
+
+ return(ret);
+}
+
diff --git a/security/nss/lib/certhigh/certreq.c b/security/nss/lib/certhigh/certreq.c
new file mode 100644
index 000000000..1588c1896
--- /dev/null
+++ b/security/nss/lib/certhigh/certreq.c
@@ -0,0 +1,230 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#include "cert.h"
+#include "secder.h"
+#include "key.h"
+#include "secitem.h"
+#include "secasn1.h"
+
+const SEC_ASN1Template CERT_AttributeTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTAttribute) },
+ { SEC_ASN1_OBJECT_ID, offsetof(CERTAttribute, attrType) },
+ { SEC_ASN1_SET_OF, offsetof(CERTAttribute, attrValue),
+ 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;
+ PRArenaPool *arena;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+
+ if ( !arena ) {
+ return(0);
+ }
+
+ c = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate));
+
+ if (c) {
+ 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;
+}
+
+/************************************************************************/
+
+CERTCertificateRequest *
+CERT_CreateCertificateRequest(CERTName *subject,
+ CERTSubjectPublicKeyInfo *spki,
+ SECItem **attributes)
+{
+ CERTCertificateRequest *certreq;
+ PRArenaPool *arena;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ return NULL;
+ }
+
+ certreq = (CERTCertificateRequest *)
+ PORT_ArenaZAlloc(arena, sizeof(CERTCertificateRequest));
+
+ if (certreq != NULL) {
+ 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;
+
+ /* Copy over attribute information */
+ if (attributes) {
+ int i = 0;
+
+ /* allocate space for attributes */
+ while(attributes[i] != NULL) i++;
+ certreq->attributes = (SECItem**)PORT_ArenaZAlloc(arena,
+ sizeof(SECItem *) * (i + 1));
+ if(!certreq->attributes) {
+ goto loser;
+ }
+
+ /* copy attributes */
+ i = 0;
+ while(attributes[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.
+ */
+ certreq->attributes[i] = (SECItem*)PORT_ArenaZAlloc(arena,
+ sizeof(SECItem));
+ if(!certreq->attributes[i]) {
+ goto loser;
+ };
+ rv = SECITEM_CopyItem(arena, certreq->attributes[i],
+ attributes[i]);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ i++;
+ }
+ certreq->attributes[i] = NULL;
+ } else {
+ /*
+ ** 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 = (SECItem**)PORT_ArenaZAlloc(arena, sizeof(SECItem *));
+ if(!certreq->attributes) {
+ goto loser;
+ }
+ certreq->attributes[0] = NULL;
+ }
+ } else {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return certreq;
+
+loser:
+ CERT_DestroyCertificateRequest(certreq);
+ return NULL;
+}
+
+void
+CERT_DestroyCertificateRequest(CERTCertificateRequest *req)
+{
+ if (req && req->arena) {
+ PORT_FreeArena(req->arena, PR_FALSE);
+ }
+ return;
+}
+
diff --git a/security/nss/lib/certhigh/certvfy.c b/security/nss/lib/certhigh/certvfy.c
new file mode 100644
index 000000000..21b9a439e
--- /dev/null
+++ b/security/nss/lib/certhigh/certvfy.c
@@ -0,0 +1,2111 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+#include "nspr.h"
+#include "secerr.h"
+#include "secport.h"
+#include "seccomon.h"
+#include "secoid.h"
+#include "sslerr.h"
+#include "genname.h"
+#include "keyhi.h"
+#include "cert.h"
+#include "certdb.h"
+#include "cryptohi.h"
+
+#ifndef NSS_3_4_CODE
+#define NSS_3_4_CODE
+#endif /* NSS_3_4_CODE */
+#include "nsspki.h"
+#include "pkitm.h"
+#include "pkim.h"
+#include "pki3hack.h"
+#include "base.h"
+
+#define PENDING_SLOP (24L*60L*60L)
+
+/*
+ * WARNING - this function is depricated, and will go away in the near future.
+ * It has been superseded by CERT_CheckCertValidTimes().
+ *
+ * Check the validity times of a certificate
+ */
+SECStatus
+CERT_CertTimesValid(CERTCertificate *c)
+{
+ int64 now, notBefore, notAfter, pendingSlop;
+ SECStatus rv;
+
+ /* if cert is already marked OK, then don't bother to check */
+ if ( c->timeOK ) {
+ return(SECSuccess);
+ }
+
+ /* get current time */
+ now = PR_Now();
+ rv = CERT_GetCertTimes(c, &notBefore, &notAfter);
+
+ if (rv) {
+ return(SECFailure);
+ }
+
+ LL_I2L(pendingSlop, PENDING_SLOP);
+ LL_SUB(notBefore, notBefore, pendingSlop);
+
+ if (LL_CMP(now, <, notBefore) || LL_CMP(now, >, notAfter)) {
+ PORT_SetError(SEC_ERROR_EXPIRED_CERTIFICATE);
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+/*
+ * verify the signature of a signed data object with the given DER publickey
+ */
+SECStatus
+CERT_VerifySignedDataWithPublicKey(CERTSignedData *sd,
+ SECKEYPublicKey *pubKey,
+ void *wincx)
+{
+ SECStatus rv;
+ SECOidTag algid;
+ SECItem sig;
+
+ if ( !pubKey || !sd ) {
+ PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
+ return SECFailure;
+ }
+
+ /* check the signature */
+ sig = sd->signature;
+ /* convert sig->len from bit counts to byte count. */
+ DER_ConvertBitString(&sig);
+
+ algid = SECOID_GetAlgorithmTag(&sd->signatureAlgorithm);
+ rv = VFY_VerifyData(sd->data.data, sd->data.len, pubKey, &sig,
+ algid, wincx);
+
+ return rv ? SECFailure : 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,
+ int64 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;
+}
+
+
+/*
+ * This must only be called on a cert that is known to have an issuer
+ * with an invalid time
+ */
+CERTCertificate *
+CERT_FindExpiredIssuer(CERTCertDBHandle *handle, CERTCertificate *cert)
+{
+ CERTCertificate *issuerCert = NULL;
+ CERTCertificate *subjectCert;
+ int count;
+ SECStatus rv;
+ SECComparison rvCompare;
+
+ subjectCert = CERT_DupCertificate(cert);
+ if ( subjectCert == NULL ) {
+ goto loser;
+ }
+
+ for ( count = 0; count < CERT_MAX_CERT_CHAIN; count++ ) {
+ /* find the certificate of the issuer */
+ issuerCert = CERT_FindCertByName(handle, &subjectCert->derIssuer);
+
+ if ( ! issuerCert ) {
+ goto loser;
+ }
+
+ rv = CERT_CertTimesValid(issuerCert);
+ if ( rv == SECFailure ) {
+ /* this is the invalid issuer */
+ CERT_DestroyCertificate(subjectCert);
+ return(issuerCert);
+ }
+
+ /* make sure that the issuer is not self signed. If it is, then
+ * stop here to prevent looping.
+ */
+ rvCompare = SECITEM_CompareItem(&issuerCert->derSubject,
+ &issuerCert->derIssuer);
+ if (rvCompare == SECEqual) {
+ PORT_Assert(0); /* No expired issuer! */
+ goto loser;
+ }
+ CERT_DestroyCertificate(subjectCert);
+ subjectCert = issuerCert;
+ }
+
+loser:
+ if ( issuerCert ) {
+ CERT_DestroyCertificate(issuerCert);
+ }
+
+ if ( subjectCert ) {
+ CERT_DestroyCertificate(subjectCert);
+ }
+
+ return(NULL);
+}
+
+/* Software FORTEZZA installation hack. The software fortezza installer does
+ * not have access to the krl and cert.db file. Accept FORTEZZA Certs without
+ * KRL's in this case.
+ */
+static int dont_use_krl = 0;
+/* not a public exposed function... */
+void sec_SetCheckKRLState(int value) { dont_use_krl = value; }
+
+SECStatus
+SEC_CheckKRL(CERTCertDBHandle *handle,SECKEYPublicKey *key,
+ CERTCertificate *rootCert, int64 t, void * wincx)
+{
+ CERTSignedCrl *crl = NULL;
+ SECStatus rv = SECFailure;
+ SECStatus rv2;
+ CERTCrlEntry **crlEntry;
+ SECCertTimeValidity validity;
+ CERTCertificate *issuerCert = NULL;
+
+ if (dont_use_krl) return SECSuccess;
+
+ /* first look up the KRL */
+ crl = SEC_FindCrlByName(handle,&rootCert->derSubject, SEC_KRL_TYPE);
+ if (crl == NULL) {
+ PORT_SetError(SEC_ERROR_NO_KRL);
+ goto done;
+ }
+
+ /* get the issuing certificate */
+ issuerCert = CERT_FindCertByName(handle, &crl->crl.derName);
+ if (issuerCert == NULL) {
+ PORT_SetError(SEC_ERROR_KRL_BAD_SIGNATURE);
+ goto done;
+ }
+
+
+ /* now verify the KRL signature */
+ rv2 = CERT_VerifySignedData(&crl->signatureWrap, issuerCert, t, wincx);
+ if (rv2 != SECSuccess) {
+ PORT_SetError(SEC_ERROR_KRL_BAD_SIGNATURE);
+ goto done;
+ }
+
+ /* Verify the date validity of the KRL */
+ validity = SEC_CheckCrlTimes(&crl->crl, t);
+ if (validity == secCertTimeExpired) {
+ PORT_SetError(SEC_ERROR_KRL_EXPIRED);
+ goto done;
+ }
+
+ /* now make sure the key in this cert is still valid */
+ if (key->keyType != fortezzaKey) {
+ PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
+ goto done; /* This should be an assert? */
+ }
+
+ /* now make sure the key is not on the revocation list */
+ for (crlEntry = crl->crl.entries; crlEntry && *crlEntry; crlEntry++) {
+ if (PORT_Memcmp((*crlEntry)->serialNumber.data,
+ key->u.fortezza.KMID,
+ (*crlEntry)->serialNumber.len) == 0) {
+ PORT_SetError(SEC_ERROR_REVOKED_KEY);
+ goto done;
+ }
+ }
+ rv = SECSuccess;
+
+done:
+ if (issuerCert) CERT_DestroyCertificate(issuerCert);
+ if (crl) SEC_DestroyCrl(crl);
+ return rv;
+}
+
+SECStatus
+SEC_CheckCRL(CERTCertDBHandle *handle,CERTCertificate *cert,
+ CERTCertificate *caCert, int64 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, int64 validTime, SECCertUsage usage)
+{
+#ifdef NSS_CLASSIC
+ CERTAuthKeyID * authorityKeyID = NULL;
+ CERTCertificate * issuerCert = NULL;
+ SECItem * caName;
+ PRArenaPool *tmpArena = NULL;
+
+ tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+
+ if ( !tmpArena ) {
+ goto loser;
+ }
+ authorityKeyID = CERT_FindAuthKeyIDExten(tmpArena,cert);
+
+ if ( authorityKeyID != NULL ) {
+ /* has the authority key ID extension */
+ if ( authorityKeyID->keyID.data != NULL ) {
+ /* extension contains a key ID, so lookup based on it */
+ issuerCert = CERT_FindCertByKeyID(cert->dbhandle, &cert->derIssuer,
+ &authorityKeyID->keyID);
+ if ( issuerCert == NULL ) {
+ PORT_SetError (SEC_ERROR_UNKNOWN_ISSUER);
+ goto loser;
+ }
+
+ } else if ( authorityKeyID->authCertIssuer != NULL ) {
+ /* no key ID, so try issuer and serial number */
+ caName = (SECItem*)CERT_GetGeneralNameByType(authorityKeyID->authCertIssuer,
+ certDirectoryName, PR_TRUE);
+
+ /*
+ * caName is NULL when the authCertIssuer field is not
+ * being used, or other name form is used instead.
+ * If the directoryName format and serialNumber fields are
+ * used, we use them to find the CA cert.
+ * Note:
+ * By the time it gets here, we known for sure that if the
+ * authCertIssuer exists, then the authCertSerialNumber
+ * must also exists (CERT_DecodeAuthKeyID() ensures this).
+ * We don't need to check again.
+ */
+
+ if (caName != NULL) {
+ CERTIssuerAndSN issuerSN;
+
+ issuerSN.derIssuer.data = caName->data;
+ issuerSN.derIssuer.len = caName->len;
+ issuerSN.serialNumber.data =
+ authorityKeyID->authCertSerialNumber.data;
+ issuerSN.serialNumber.len =
+ authorityKeyID->authCertSerialNumber.len;
+ issuerCert = CERT_FindCertByIssuerAndSN(cert->dbhandle,
+ &issuerSN);
+ if ( issuerCert == NULL ) {
+ PORT_SetError (SEC_ERROR_UNKNOWN_ISSUER);
+ goto loser;
+ }
+ }
+ }
+ }
+ if ( issuerCert == NULL ) {
+ /* if there is not authorityKeyID, then try to find the issuer */
+ /* find a valid CA cert with correct usage */
+ issuerCert = CERT_FindMatchingCert(cert->dbhandle,
+ &cert->derIssuer,
+ certOwnerCA, usage, PR_TRUE,
+ validTime, PR_TRUE);
+
+ /* if that fails, then fall back to grabbing any cert with right name*/
+ if ( issuerCert == NULL ) {
+ issuerCert = CERT_FindCertByName(cert->dbhandle, &cert->derIssuer);
+ if ( issuerCert == NULL ) {
+ PORT_SetError (SEC_ERROR_UNKNOWN_ISSUER);
+ }
+ }
+ }
+
+loser:
+ if (tmpArena != NULL) {
+ PORT_FreeArena(tmpArena, PR_FALSE);
+ tmpArena = NULL;
+ }
+
+ return(issuerCert);
+#else
+ NSSCertificate *me;
+ NSSTime *nssTime;
+ NSSUsage nssUsage;
+ NSSCertificate *chain[3];
+ PRStatus status;
+ me = STAN_GetNSSCertificate(cert);
+ nssTime = NSSTime_SetPRTime(NULL, validTime);
+ nssUsage.anyUsage = PR_FALSE;
+ nssUsage.nss3usage = usage;
+ nssUsage.nss3lookingForCA = PR_TRUE;
+ memset(chain, 0, 3*sizeof(NSSCertificate *));
+ if (!me) {
+ PORT_SetError (SEC_ERROR_BAD_DATABASE);
+ return NULL;
+ }
+ (void)NSSCertificate_BuildChain(me, nssTime, &nssUsage, NULL,
+ chain, 2, NULL, &status);
+ nss_ZFreeIf(nssTime);
+ if (status == PR_SUCCESS) {
+ /* 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;
+ } else {
+ CERT_DestroyCertificate(cert); /* the first cert in the chain */
+ return STAN_GetCERTCertificate(chain[1]); /* return the 2nd */
+ }
+ } else {
+ if (chain[0]) {
+ CERT_DestroyCertificate(cert);
+ }
+ PORT_SetError (SEC_ERROR_UNKNOWN_ISSUER);
+ }
+ return NULL;
+#endif
+}
+
+/*
+ * 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 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);
+}
+
+
+
+
+
+static void
+AddToVerifyLog(CERTVerifyLog *log, CERTCertificate *cert, unsigned 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 ) { \
+ AddToVerifyLog(log, cert, PORT_GetError(), depth, (void *)arg); \
+ } else { \
+ goto loser; \
+ }
+
+#define LOG_ERROR(log,cert,depth,arg) \
+ if ( log != NULL ) { \
+ AddToVerifyLog(log, cert, PORT_GetError(), depth, (void *)arg); \
+ }
+
+
+typedef enum { cbd_None, cbd_User, cbd_CA } cbd_FortezzaType;
+
+static SECStatus
+cert_VerifyFortezzaV1Cert(CERTCertDBHandle *handle, CERTCertificate *cert,
+ cbd_FortezzaType *next_type, cbd_FortezzaType last_type,
+ int64 t, void *wincx)
+{
+ unsigned char priv = 0;
+ SECKEYPublicKey *key;
+ SECStatus rv;
+
+ *next_type = cbd_CA;
+
+ /* read the key */
+ key = CERT_ExtractPublicKey(cert);
+
+ /* Cant' get Key? fail. */
+ if (key == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return SECFailure;
+ }
+
+
+ /* if the issuer is not an old fortezza cert, we bail */
+ if (key->keyType != fortezzaKey) {
+ SECKEY_DestroyPublicKey(key);
+ /* CA Cert not fortezza */
+ PORT_SetError(SEC_ERROR_NOT_FORTEZZA_ISSUER);
+ return SECFailure;
+ }
+
+ /* get the privilege mask */
+ if (key->u.fortezza.DSSpriviledge.len > 0) {
+ priv = key->u.fortezza.DSSpriviledge.data[0];
+ }
+
+ /*
+ * make sure the CA's keys are OK
+ */
+
+ rv = SEC_CheckKRL(handle, key, NULL, t, wincx);
+ SECKEY_DestroyPublicKey(key);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ switch (last_type) {
+ case cbd_User:
+ /* first check for subordination */
+ /*rv = FortezzaSubordinateCheck(cert,issuerCert);*/
+ rv = SECSuccess;
+
+ /* now check for issuer privilege */
+ if ((rv != SECSuccess) || ((priv & 0x10) == 0)) {
+ /* bail */
+ PORT_SetError (SEC_ERROR_CA_CERT_INVALID);
+ return SECFailure;
+ }
+ break;
+ case cbd_CA:
+ if ((priv & 0x20) == 0) {
+ /* bail */
+ PORT_SetError (SEC_ERROR_CA_CERT_INVALID);
+ return SECFailure;
+ }
+ break;
+ case cbd_None:
+ *next_type = (priv & 0x30) ? cbd_CA : cbd_User;
+ break;
+ default:
+ /* bail */ /* shouldn't ever happen */
+ PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+
+static SECStatus
+cert_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool checkSig, PRBool* sigerror,
+ SECCertUsage certUsage, int64 t, void *wincx,
+ CERTVerifyLog *log, PRBool* revoked)
+{
+ SECTrustType trustType;
+ CERTBasicConstraints basicConstraint;
+ CERTCertificate *issuerCert = NULL;
+ CERTCertificate *subjectCert = NULL;
+ CERTCertificate *badCert = NULL;
+ PRBool isca;
+ PRBool isFortezzaV1 = PR_FALSE;
+ SECStatus rv;
+ SECStatus rvFinal = SECSuccess;
+ int count;
+ int currentPathLen = -1;
+ int flags;
+ unsigned int caCertType;
+ unsigned int requiredCAKeyUsage;
+ unsigned int requiredFlags;
+ PRArenaPool *arena = NULL;
+ CERTGeneralName *namesList = NULL;
+ CERTGeneralName *subjectNameList = NULL;
+ CERTCertificate **certsList = NULL;
+ int certsListLen = 16;
+ int namesCount = 0;
+
+ cbd_FortezzaType last_type = cbd_None;
+
+ 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 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;
+ }
+
+ subjectCert = CERT_DupCertificate(cert);
+ if ( subjectCert == NULL ) {
+ goto loser;
+ }
+
+ /* determine if the cert is fortezza.
+ */
+ isFortezzaV1 = (PRBool)
+ (CERT_GetCertKeyType(&subjectCert->subjectPublicKeyInfo)
+ == fortezzaKey);
+
+ if (isFortezzaV1) {
+ rv = cert_VerifyFortezzaV1Cert(handle, subjectCert, &last_type,
+ cbd_None, t, wincx);
+ if (rv == SECFailure) {
+ /**** PORT_SetError is already set by cert_VerifyFortezzaV1Cert **/
+ LOG_ERROR_OR_EXIT(log,subjectCert,0,0);
+ }
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+
+ certsList = PORT_ZNewArray(CERTCertificate *, certsListLen);
+ if (certsList == NULL)
+ goto loser;
+
+ for ( count = 0; count < CERT_MAX_CERT_CHAIN; count++ ) {
+ int subjectNameListLen;
+ int i;
+ PRBool validCAOverride = PR_FALSE;
+
+ /* Construct a list of names for the current and all previous
+ * certifcates to be verified against the name constraints extension
+ * of the issuer certificate.
+ */
+ subjectNameList = CERT_GetCertificateNames(subjectCert, arena);
+ subjectNameListLen = CERT_GetNamesLength(subjectNameList);
+ if (certsListLen <= namesCount + subjectNameListLen) {
+ certsListLen = (namesCount + subjectNameListLen) * 2;
+ certsList =
+ (CERTCertificate **)PORT_Realloc(certsList,
+ certsListLen * sizeof(CERTCertificate *));
+ if (certsList == NULL) {
+ goto loser;
+ }
+ }
+ for (i = 0; i < subjectNameListLen; i++) {
+ certsList[namesCount + i] = subjectCert;
+ }
+ namesCount += subjectNameListLen;
+ namesList = cert_CombineNamesLists(namesList, subjectNameList);
+
+ /* 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 {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ LOG_ERROR_OR_EXIT(log,subjectCert,count,0);
+ }
+ }
+ }
+
+ /*
+ * XXX - fortezza may need error logging stuff added
+ */
+ if (isFortezzaV1) {
+ rv = cert_VerifyFortezzaV1Cert(handle, issuerCert, &last_type,
+ last_type, t, wincx);
+ if (rv == SECFailure) {
+ /**** PORT_SetError is already set by *
+ * cert_VerifyFortezzaV1Cert **/
+ LOG_ERROR_OR_EXIT(log,subjectCert,0,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).
+ *
+ * In the fortezza (V1) case, we've already checked the CA bits
+ * in the key, so we're presumed to be a CA; however we really don't
+ * want to bypass Basic constraint or netscape extension parsing.
+ *
+ * In Fortezza V2, basicConstraint will be set for every CA,PCA,PAA
+ */
+
+ 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);
+ } else {
+ currentPathLen = CERT_UNLIMITED_PATH_CONSTRAINT;
+ }
+ /* no basic constraints found, if we're fortezza, CA bit is already
+ * verified (isca = PR_TRUE). otherwise, we aren't (yet) a ca
+ * isca = PR_FALSE */
+ isca = isFortezzaV1;
+ } else {
+ if ( basicConstraint.isCA == PR_FALSE ) {
+ PORT_SetError (SEC_ERROR_CA_CERT_INVALID);
+ LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);
+ }
+
+ /* make sure that the path len constraint is properly set.
+ */
+ if ( basicConstraint.pathLenConstraint ==
+ CERT_UNLIMITED_PATH_CONSTRAINT ) {
+ currentPathLen = CERT_UNLIMITED_PATH_CONSTRAINT;
+ } else if ( currentPathLen == CERT_UNLIMITED_PATH_CONSTRAINT ) {
+ /* error if the previous CA's path length constraint is
+ * unlimited but its CA's path is not.
+ */
+ PORT_SetError (SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID);
+ LOG_ERROR_OR_EXIT(log,issuerCert,count+1,basicConstraint.pathLenConstraint);
+ } else if (basicConstraint.pathLenConstraint > currentPathLen) {
+ currentPathLen = basicConstraint.pathLenConstraint;
+ } else {
+ PORT_SetError (SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID);
+ LOG_ERROR_OR_EXIT(log,issuerCert,count+1,basicConstraint.pathLenConstraint);
+ }
+
+ isca = PR_TRUE;
+ }
+
+ /* 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 ( issuerCert->trust ) {
+ /* 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) {
+ /* XXX NSS has done this for years, but it seems incorrect. */
+ rv = rvFinal;
+ goto done;
+ }
+
+ /*
+ * check the trust parms of the issuer
+ */
+ 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(issuerCert->trust, trustType);
+
+ if (flags & CERTDB_VALID_CA) {
+ if ( ( flags & requiredFlags ) == requiredFlags) {
+ /* we found a trusted one, so return */
+ rv = rvFinal;
+ goto done;
+ }
+ validCAOverride = PR_TRUE;
+ }
+ }
+
+ 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 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;
+ }
+ /* 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;
+ }
+
+ CERT_DestroyCertificate(subjectCert);
+ subjectCert = issuerCert;
+ }
+
+ subjectCert = NULL;
+ PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
+ LOG_ERROR(log,issuerCert,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, SECCertUsage certUsage, int64 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, int64 t,
+ void *wincx, CERTVerifyLog *log)
+{
+ SECTrustType trustType;
+ CERTBasicConstraints basicConstraint;
+ PRBool isca;
+ PRBool validCAOverride = PR_FALSE;
+ SECStatus rv;
+ SECComparison rvCompare;
+ SECStatus rvFinal = SECSuccess;
+ int flags;
+ unsigned int caCertType;
+ unsigned int requiredCAKeyUsage;
+ unsigned int requiredFlags;
+ CERTCertificate *issuerCert;
+
+
+ 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 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).
+ *
+ * In the fortezza (V1) case, we've already checked the CA bits
+ * in the key, so we're presumed to be a CA; however we really don't
+ * want to bypass Basic constraint or netscape extension parsing.
+ *
+ * In Fortezza V2, basicConstraint will be set for every CA,PCA,PAA
+ */
+
+ 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, if we're fortezza, CA bit is already
+ * verified (isca = PR_TRUE). otherwise, we aren't (yet) a ca
+ * isca = PR_FALSE */
+ 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->trust ) {
+ /* 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 parms of the issuer
+ */
+ flags = SEC_GET_TRUST_FLAGS(cert->trust, trustType);
+
+ if (flags & CERTDB_VALID_CA) {
+ if ( ( flags & requiredFlags ) == requiredFlags) {
+ /* we found a trusted one, so return */
+ rv = rvFinal;
+ goto done;
+ }
+ validCAOverride = PR_TRUE;
+ }
+ }
+ 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.
+ */
+ rvCompare = SECITEM_CompareItem(&cert->derSubject, &cert->derIssuer);
+ if (rvCompare == SECEqual) {
+ 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_ITERATION() { \
+ i*=2; \
+ certUsage++; \
+ continue; \
+}
+
+#define VALID_USAGE() { \
+ NEXT_ITERATION(); \
+}
+
+#define INVALID_USAGE() { \
+ if (returnedUsages) { \
+ *returnedUsages &= (~i); \
+ } \
+ if (PR_TRUE == requiredUsage) { \
+ valid = SECFailure; \
+ } \
+ NEXT_ITERATION(); \
+}
+
+/*
+ * 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, int64 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;
+
+ 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 */
+
+#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(log,cert,0,0);
+ return SECFailure;
+ }
+#endif
+
+ /* make sure that the cert is valid at time t */
+ allowOverride = (PRBool)((requiredUsages & certificateUsageSSLServer) ||
+ (requiredUsages & certificateUsageSSLServerWithStepUp));
+ validity = CERT_CheckCertValidTimes(cert, t, allowOverride);
+ if ( validity != secCertTimeValid ) {
+ LOG_ERROR(log,cert,0,validity);
+ return SECFailure;
+ }
+
+ /* check key usage and netscape cert type */
+ cert_GetCertType(cert);
+ certType = cert->nsCertType;
+
+ for (i=1;i<=certificateUsageHighest && !(SECFailure == valid && !returnedUsages) ;) {
+ PRBool requiredUsage = (i & requiredUsages) ? PR_TRUE : PR_FALSE;
+ if (PR_FALSE == requiredUsage && PR_FALSE == checkAllUsages) {
+ NEXT_ITERATION();
+ }
+ 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:
+ 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_ITERATION();
+
+ 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();
+ }
+
+ /* check trust flags to see if this cert is directly trusted */
+ if ( cert->trust ) { /* the cert is in the DB */
+ switch ( certUsage ) {
+ case certUsageSSLClient:
+ case certUsageSSLServer:
+ flags = cert->trust->sslFlags;
+
+ /* is the cert directly trusted or not trusted ? */
+ if ( flags & CERTDB_VALID_PEER ) {/*the trust record is valid*/
+ if ( flags & CERTDB_TRUSTED ) { /* trust this cert */
+ VALID_USAGE();
+ } else { /* don't trust this cert */
+ if (PR_TRUE == requiredUsage) {
+ PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
+ }
+ LOG_ERROR(log,cert,0,flags);
+ INVALID_USAGE();
+ }
+ }
+ break;
+ case certUsageSSLServerWithStepUp:
+ /* XXX - step up certs can't be directly trusted */
+ break;
+ case certUsageSSLCA:
+ break;
+ case certUsageEmailSigner:
+ case certUsageEmailRecipient:
+ flags = cert->trust->emailFlags;
+
+ /* is the cert directly trusted or not trusted ? */
+ if ( ( flags & ( CERTDB_VALID_PEER | CERTDB_TRUSTED ) ) ==
+ ( CERTDB_VALID_PEER | CERTDB_TRUSTED ) ) {
+ VALID_USAGE();
+ }
+ break;
+ case certUsageObjectSigner:
+ flags = cert->trust->objectSigningFlags;
+
+ /* is the cert directly trusted or not trusted ? */
+ if ( flags & CERTDB_VALID_PEER ) {/*the trust record is valid*/
+ if ( flags & CERTDB_TRUSTED ) { /* trust this cert */
+ VALID_USAGE();
+ } else { /* don't trust this cert */
+ if (PR_TRUE == requiredUsage) {
+ PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
+ }
+ LOG_ERROR(log,cert,0,flags);
+ INVALID_USAGE();
+ }
+ }
+ break;
+ case certUsageVerifyCA:
+ case certUsageStatusResponder:
+ flags = cert->trust->sslFlags;
+ /* is the cert directly trusted or not trusted ? */
+ if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
+ ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
+ VALID_USAGE();
+ }
+ flags = cert->trust->emailFlags;
+ /* is the cert directly trusted or not trusted ? */
+ if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
+ ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
+ VALID_USAGE();
+ }
+ flags = cert->trust->objectSigningFlags;
+ /* is the cert directly trusted or not trusted ? */
+ if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
+ ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
+ VALID_USAGE();
+ }
+ break;
+ case certUsageAnyCA:
+ case certUsageProtectedObjectSigner:
+ case certUsageUserCertImport:
+ /* XXX to make the compiler happy. Should these be
+ * explicitly handled?
+ */
+ break;
+ }
+ }
+
+ 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 reponder 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_ITERATION();
+ }
+
+ return(valid);
+}
+
+/* obsolete, do not use for new code */
+SECStatus
+CERT_VerifyCert(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool checkSig, SECCertUsage certUsage, int64 t,
+ void *wincx, CERTVerifyLog *log)
+{
+ SECStatus rv;
+ unsigned int requiredKeyUsage;
+ unsigned int requiredCertType;
+ unsigned int flags;
+ unsigned int certType;
+ 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));
+ 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 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:
+ 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);
+ }
+
+ /* check trust flags to see if this cert is directly trusted */
+ if ( cert->trust ) { /* the cert is in the DB */
+ switch ( certUsage ) {
+ case certUsageSSLClient:
+ case certUsageSSLServer:
+ flags = cert->trust->sslFlags;
+
+ /* is the cert directly trusted or not trusted ? */
+ if ( flags & CERTDB_VALID_PEER ) {/*the trust record is valid*/
+ if ( flags & CERTDB_TRUSTED ) { /* trust this cert */
+ goto winner;
+ } else { /* don't trust this cert */
+ PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
+ LOG_ERROR_OR_EXIT(log,cert,0,flags);
+ }
+ }
+ break;
+ case certUsageSSLServerWithStepUp:
+ /* XXX - step up certs can't be directly trusted */
+ break;
+ case certUsageSSLCA:
+ break;
+ case certUsageEmailSigner:
+ case certUsageEmailRecipient:
+ flags = cert->trust->emailFlags;
+
+ /* is the cert directly trusted or not trusted ? */
+ if ( ( flags & ( CERTDB_VALID_PEER | CERTDB_TRUSTED ) ) ==
+ ( CERTDB_VALID_PEER | CERTDB_TRUSTED ) ) {
+ goto winner;
+ }
+ break;
+ case certUsageObjectSigner:
+ flags = cert->trust->objectSigningFlags;
+
+ /* is the cert directly trusted or not trusted ? */
+ if ( flags & CERTDB_VALID_PEER ) {/*the trust record is valid*/
+ if ( flags & CERTDB_TRUSTED ) { /* trust this cert */
+ goto winner;
+ } else { /* don't trust this cert */
+ PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
+ LOG_ERROR_OR_EXIT(log,cert,0,flags);
+ }
+ }
+ break;
+ case certUsageVerifyCA:
+ case certUsageStatusResponder:
+ flags = cert->trust->sslFlags;
+ /* is the cert directly trusted or not trusted ? */
+ if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
+ ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
+ goto winner;
+ }
+ flags = cert->trust->emailFlags;
+ /* is the cert directly trusted or not trusted ? */
+ if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
+ ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
+ goto winner;
+ }
+ flags = cert->trust->objectSigningFlags;
+ /* is the cert directly trusted or not trusted ? */
+ if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
+ ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
+ goto winner;
+ }
+ break;
+ case certUsageAnyCA:
+ case certUsageProtectedObjectSigner:
+ case certUsageUserCertImport:
+ /* XXX to make the compiler happy. Should these be
+ * explicitly handled?
+ */
+ break;
+ }
+ }
+
+ 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 reponder 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, which
+ * is all done inside VerifyCertChain) by bypassing this code.
+ */
+ statusConfig = CERT_GetStatusConfig(handle);
+ if (certUsage != certUsageStatusResponder && statusConfig != NULL) {
+ if (statusConfig->statusChecker != NULL) {
+ rv = (* statusConfig->statusChecker)(handle, cert,
+ t, wincx);
+ if (rv != SECSuccess) {
+ LOG_ERROR_OR_EXIT(log,cert,0,0);
+ }
+ }
+ }
+
+winner:
+ 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
+ * certUsageEmailSigner
+ * certUsageEmailRecipient
+ * certUsageObjectSigner
+ */
+
+CERTCertificate *
+CERT_FindMatchingCert(CERTCertDBHandle *handle, SECItem *derName,
+ CERTCertOwner owner, SECCertUsage usage,
+ PRBool preferTrusted, int64 validTime, PRBool validOnly)
+{
+ CERTCertList *certList = NULL;
+ CERTCertificate *cert = NULL;
+ 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->trust == NULL ) {
+ flags = 0;
+ } else {
+ flags = SEC_GET_TRUST_FLAGS(cert->trust, 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;
+ int64 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(PRArenaPool *arena, CERTCertificate *cert,
+ char *expiredString, char *notYetGoodString)
+{
+ SECCertTimeValidity validity;
+ char *nickname, *tmpstr;
+
+ 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(cert->nickname);
+ } else {
+ nickname = PORT_ArenaStrdup(arena, cert->nickname);
+ }
+
+ 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", cert->nickname,
+ expiredString);
+ } else {
+ /* not yet valid */
+ tmpstr = PR_smprintf("%s%s", cert->nickname,
+ notYetGoodString);
+ }
+ 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;
+ PRArenaPool *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, int64 time, SECCertUsage usage)
+{
+ CERTCertList *chain = NULL;
+
+ 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) {
+ if (SECSuccess != CERT_AddCertToListTail(chain, cert)) {
+ /* return partial chain */
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return chain;
+ }
+
+ if (SECITEM_CompareItem(&cert->derIssuer, &cert->derSubject)
+ == SECEqual) {
+ /* 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/config.mk b/security/nss/lib/certhigh/config.mk
new file mode 100644
index 000000000..0a00dc61e
--- /dev/null
+++ b/security/nss/lib/certhigh/config.mk
@@ -0,0 +1,43 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+#
+# Override TARGETS variable so that only static libraries
+# are specifed as dependencies within rules.mk.
+#
+
+TARGETS = $(LIBRARY)
+SHARED_LIBRARY =
+IMPORT_LIBRARY =
+PROGRAM =
+
diff --git a/security/nss/lib/certhigh/crlv2.c b/security/nss/lib/certhigh/crlv2.c
new file mode 100644
index 000000000..2600d9878
--- /dev/null
+++ b/security/nss/lib/certhigh/crlv2.c
@@ -0,0 +1,126 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Code for dealing with x.509 v3 crl and crl entries extensions.
+ *
+ * $Id$
+ */
+
+#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));
+}
+
+
+SECStatus CERT_FindCRLNumberExten (CERTCrl *crl, CERTCrlNumber *value)
+{
+ SECItem encodedExtenValue;
+ SECStatus rv;
+
+ encodedExtenValue.data = NULL;
+ encodedExtenValue.len = 0;
+
+ rv = cert_FindExtension
+ (crl->extensions, SEC_OID_X509_CRL_NUMBER, &encodedExtenValue);
+ if ( rv != SECSuccess )
+ return (rv);
+
+ rv = SEC_ASN1DecodeItem (NULL, value, SEC_IntegerTemplate,
+ &encodedExtenValue);
+ PORT_Free (encodedExtenValue.data);
+ return (rv);
+}
+
+SECStatus CERT_FindCRLReasonExten (CERTCrl *crl, SECItem *value)
+{
+ return (CERT_FindBitStringExtension
+ (crl->extensions, SEC_OID_X509_REASON_CODE, value));
+}
+
+SECStatus CERT_FindInvalidDateExten (CERTCrl *crl, int64 *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_GeneralizedTimeTemplate, &encodedExtenValue);
+ if (rv != SECSuccess)
+ return (rv);
+ rv = DER_GeneralizedTimeToTime(value, &encodedExtenValue);
+ PORT_Free (decodedExtenValue.data);
+ PORT_Free (encodedExtenValue.data);
+ return (rv);
+}
diff --git a/security/nss/lib/certhigh/manifest.mn b/security/nss/lib/certhigh/manifest.mn
new file mode 100644
index 000000000..80deff5bb
--- /dev/null
+++ b/security/nss/lib/certhigh/manifest.mn
@@ -0,0 +1,59 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+CORE_DEPTH = ../../..
+
+EXPORTS = \
+ ocsp.h \
+ ocspt.h \
+ $(NULL)
+
+PRIVATE_EXPORTS = \
+ ocspti.h \
+ $(NULL)
+
+MODULE = nss
+
+CSRCS = \
+ certhtml.c \
+ certreq.c \
+ crlv2.c \
+ ocsp.c \
+ certhigh.c \
+ certvfy.c \
+ xcrldist.c \
+ $(NULL)
+
+REQUIRES = dbm
+
+LIBRARY_NAME = certhi
+
diff --git a/security/nss/lib/certhigh/ocsp.c b/security/nss/lib/certhigh/ocsp.c
new file mode 100644
index 000000000..96ee31f5a
--- /dev/null
+++ b/security/nss/lib/certhigh/ocsp.c
@@ -0,0 +1,3998 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Implementation of OCSP services, for both client and server.
+ * (XXX, really, mostly just for client right now, but intended to do both.)
+ *
+ * $Id$
+ */
+
+#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 "xconst.h"
+#include "secerr.h"
+#include "secoid.h"
+#include "hasht.h"
+#include "sechash.h"
+#include "secasn1.h"
+#include "keyhi.h"
+#include "cryptohi.h"
+#include "ocsp.h"
+#include "ocspti.h"
+#include "genname.h"
+#include "certxutl.h"
+#include "pk11func.h" /* for PK11_HashBuf */
+#include <stdarg.h>
+
+
+/*
+ * 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;
+
+
+/*
+ * 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 | 0,
+ offsetof(ocspTBSRequest, version),
+ SEC_IntegerTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(ocspTBSRequest, derRequestorName),
+ 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,
+ offsetof(ocspSignature, signatureAlgorithm),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_BIT_STRING,
+ offsetof(ocspSignature, signature) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(ocspSignature, derCerts),
+ 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,
+ offsetof(CERTOCSPCertID, hashAlgorithm),
+ 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 }
+ */
+static 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 }
+ */
+static 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_POINTER,
+ offsetof(ocspBasicOCSPResponse, tbsResponseData),
+ ocsp_ResponseDataTemplate },
+ { SEC_ASN1_INLINE,
+ offsetof(ocspBasicOCSPResponse, responseSignature.signatureAlgorithm),
+ SECOID_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),
+ 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 | 0,
+ offsetof(ocspResponseData, version),
+ 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:
+ */
+static const SEC_ASN1Template ocsp_ResponderIDByNameTemplate[] = {
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(ocspResponderID, responderIDValue.name),
+ CERT_NameTemplate }
+};
+static const SEC_ASN1Template ocsp_ResponderIDByKeyTemplate[] = {
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2,
+ offsetof(ocspResponderID, responderIDValue.keyHash),
+ SEC_OctetStringTemplate }
+};
+static const SEC_ASN1Template ocsp_ResponderIDOtherTemplate[] = {
+ { SEC_ASN1_ANY,
+ offsetof(ocspResponderID, responderIDValue.other) }
+};
+
+/*
+ * 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 | 0,
+ offsetof(CERTOCSPSingleResponse, nextUpdate),
+ 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 | 0,
+ offsetof(ocspCertStatus, certStatusInfo.goodInfo),
+ 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 | 2,
+ offsetof(ocspCertStatus, certStatusInfo.unknownInfo),
+ SEC_NullTemplate }
+};
+static const SEC_ASN1Template ocsp_CertStatusOtherTemplate[] = {
+ { SEC_ASN1_POINTER,
+ offsetof(ocspCertStatus, certStatusInfo.otherInfo),
+ 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 | 0,
+ offsetof(ocspRevokedInfo, revocationReason),
+ 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:
+ * PRArenaPool *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(PRArenaPool *arena, CERTOCSPRequest *request,
+ void *pwArg)
+{
+ ocspTBSRequest *tbsRequest;
+ SECStatus rv;
+
+ /* XXX All of these should generate errors if they fail. */
+ PORT_Assert(request);
+ PORT_Assert(request->tbsRequest);
+
+ tbsRequest = 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(SECItem *src)
+{
+ PRArenaPool *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->poolp) {
+ PORT_FreeArena(certID->poolp, PR_FALSE);
+ return SECSuccess;
+ }
+ return SECFailure;
+}
+
+
+/*
+ * 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(PRArenaPool *arena, CERTCertificate *cert, int64 time)
+{
+ CERTOCSPCertID *certID;
+ CERTCertificate *issuerCert = NULL;
+ SECItem *tempItem = 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;
+ }
+
+ tempItem = SEC_ASN1EncodeItem(NULL, NULL, &issuerCert->subject,
+ CERT_NameTemplate);
+ if (tempItem == NULL) {
+ goto loser;
+ }
+
+ if (SECITEM_AllocItem(arena, &(certID->issuerNameHash),
+ SHA1_LENGTH) == NULL) {
+ goto loser;
+ }
+ rv = PK11_HashBuf(SEC_OID_SHA1, certID->issuerNameHash.data,
+ tempItem->data, tempItem->len);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ certID->issuerSHA1NameHash.data = certID->issuerNameHash.data;
+ certID->issuerSHA1NameHash.len = certID->issuerNameHash.len;
+ /* cache the other two hash algorithms as well */
+ if (SECITEM_AllocItem(arena, &(certID->issuerMD5NameHash),
+ MD5_LENGTH) == NULL) {
+ goto loser;
+ }
+ rv = PK11_HashBuf(SEC_OID_MD5, certID->issuerMD5NameHash.data,
+ tempItem->data, tempItem->len);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ if (SECITEM_AllocItem(arena, &(certID->issuerMD2NameHash),
+ MD2_LENGTH) == NULL) {
+ goto loser;
+ }
+ rv = PK11_HashBuf(SEC_OID_MD2, certID->issuerMD2NameHash.data,
+ tempItem->data, tempItem->len);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ SECITEM_FreeItem(tempItem, PR_TRUE);
+ tempItem = NULL;
+
+ if (CERT_SPKDigestValueForCert(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_SPKDigestValueForCert(arena, issuerCert, SEC_OID_MD5,
+ &(certID->issuerMD5KeyHash)) == NULL) {
+ goto loser;
+ }
+ if (CERT_SPKDigestValueForCert(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);
+ }
+ if (tempItem != NULL) {
+ SECITEM_FreeItem(tempItem, PR_TRUE);
+ }
+ PORT_ArenaRelease(arena, mark);
+ return NULL;
+}
+
+CERTOCSPCertID*
+CERT_CreateOCSPCertID(CERTCertificate *cert, int64 time)
+{
+ PRArenaPool *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;
+}
+
+
+
+/*
+ * 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;
+
+ 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(PRArenaPool *arena, CERTCertList *certList,
+ int64 time, PRBool includeLocator)
+{
+ ocspSingleRequest **requestList = NULL;
+ CERTCertListNode *node;
+ 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;
+
+ 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;
+}
+
+
+/*
+ * 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.
+ * int64 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, int64 time,
+ PRBool addServiceLocator,
+ CERTCertificate *signerCert)
+{
+ PRArenaPool *arena = NULL;
+ CERTOCSPRequest *request = NULL;
+ ocspTBSRequest *tbsRequest = NULL;
+
+ /*
+ * XXX This should set an error, but since it is only temporary and
+ * since PSM will not initially provide a way to turn on signing of
+ * requests anyway, I figure we can just skip defining an error that
+ * will be obsolete in the next release. When we are prepared to
+ * put signing of requests back in, this entire check will go away,
+ * and later in this function 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) {
+ return 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 */
+
+ /*
+ * Now create the list of single requests, one for each cert.
+ */
+ tbsRequest->requestList = ocsp_CreateSingleRequestList(arena, certList,
+ time,
+ addServiceLocator);
+ if (tbsRequest->requestList == NULL) {
+ goto loser;
+ }
+ return request;
+
+loser:
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+
+/*
+ * 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;
+}
+
+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_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;
+}
+
+
+/*
+ * 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(ocspResponderIDType 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(PRArenaPool *arena,
+ CERTOCSPSingleResponse **responses)
+{
+ ocspCertStatus *certStatus;
+ ocspCertStatusType certStatusType;
+ const SEC_ASN1Template *certStatusTemplate;
+ int derTag;
+ int i;
+ SECStatus rv = SECFailure;
+
+ if (responses == NULL) /* nothing to do */
+ return SECSuccess;
+
+ for (i = 0; responses[i] != NULL; i++) {
+ /*
+ * 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(arena, sizeof(ocspCertStatus));
+ if (certStatus == NULL) {
+ goto loser;
+ }
+ rv = SEC_ASN1DecodeItem(arena, certStatus, certStatusTemplate,
+ &responses[i]->derCertStatus);
+ 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 ocspResponderIDType
+ocsp_ResponderIDTypeByTag(int derTag)
+{
+ ocspResponderIDType 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(PRArenaPool *arena, SECItem *src)
+{
+ void *mark;
+ ocspBasicOCSPResponse *basicResponse;
+ ocspResponseData *responseData;
+ ocspResponderID *responderID;
+ ocspResponderIDType 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(PRArenaPool *arena, ocspResponseBytes *rbytes)
+{
+ PORT_Assert(rbytes != NULL); /* internal error, really */
+ if (rbytes == NULL)
+ PORT_SetError(SEC_ERROR_INVALID_ARGS); /* XXX set better error? */
+
+ 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(SECItem *src)
+{
+ PRArenaPool *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).
+ */
+static ocspResponseData *
+ocsp_GetResponseData(CERTOCSPResponse *response)
+{
+ 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);
+
+ return responseData;
+}
+
+/*
+ * Much like the routine above, except it returns the response signature.
+ * Again, no copy is done.
+ */
+static ocspSignature *
+ocsp_GetResponseSignature(CERTOCSPResponse *response)
+{
+ ocspBasicOCSPResponse *basic;
+
+ PORT_Assert(response != NULL);
+ if (NULL == response->responseBytes) {
+ return 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);
+
+ 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(char *url, char **pHostname, PRUint16 *pPort, char **pPath)
+{
+ unsigned short port = 80; /* default, in case not in url */
+ char *hostname = NULL;
+ char *path = NULL;
+ 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("/");
+ }
+
+ *pHostname = hostname;
+ *pPort = port;
+ *pPath = path;
+ return SECSuccess;
+
+loser:
+ if (hostname != NULL)
+ PORT_Free(hostname);
+ if (path != NULL)
+ PORT_Free(path);
+ 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.
+ */
+static PRFileDesc *
+ocsp_SendEncodedRequest(char *location, SECItem *encodedRequest)
+{
+ char *hostname = NULL;
+ char *path = NULL;
+ PRUint16 port;
+ SECStatus rv;
+ PRFileDesc *sock = NULL;
+ PRFileDesc *returnSock = NULL;
+ char *header = NULL;
+
+ /*
+ * 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;
+
+ header = PR_smprintf("POST %s HTTP/1.0\r\n"
+ "Host: %s:%d\r\n"
+ "Content-Type: application/ocsp-request\r\n"
+ "Content-Length: %u\r\n\r\n",
+ path, hostname, port, 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(PRArenaPool *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;
+}
+
+
+/*
+ * FUNCTION: CERT_GetEncodedOCSPResponse
+ * Creates and sends a request to an OCSP responder, then reads and
+ * returns the (encoded) response.
+ * INPUTS:
+ * PRArenaPool *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.
+ * char *location
+ * The location of the OCSP responder (a URL).
+ * int64 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_GetEncodedOCSPResponse(PRArenaPool *arena, CERTCertList *certList,
+ char *location, int64 time,
+ PRBool addServiceLocator,
+ CERTCertificate *signerCert, void *pwArg,
+ CERTOCSPRequest **pRequest)
+{
+ CERTOCSPRequest *request = NULL;
+ SECItem *encodedRequest = NULL;
+ SECItem *encodedResponse = NULL;
+ PRFileDesc *sock = NULL;
+ SECStatus rv;
+
+ request = CERT_CreateOCSPRequest(certList, time, addServiceLocator,
+ signerCert);
+ if (request == NULL)
+ 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;
+
+ sock = ocsp_SendEncodedRequest(location, encodedRequest);
+ if (sock == NULL)
+ goto loser;
+
+ encodedResponse = ocsp_GetEncodedResponse(arena, sock);
+ 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);
+ if (sock != NULL)
+ PR_Close(sock);
+
+ return encodedResponse;
+}
+
+
+/* Checks a certificate for the key usage extension of OCSP signer. */
+static PRBool
+ocsp_CertIsOCSPSigner(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;
+ 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;
+ SECItem extItem;
+
+ extItem.data = NULL;
+ rv = CERT_FindCertExtension(cert, SEC_OID_PKIX_OCSP_NO_CHECK,
+ &extItem);
+ if (extItem.data != NULL) {
+ PORT_Free(extItem.data);
+ }
+ 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_SPKDigestValueForCert(NULL,testCert,SEC_OID_SHA1, &item) == NULL) {
+ return PR_FALSE;
+ }
+ if (SECITEM_ItemsAreEqual(certIndex,&item)) {
+ return PR_TRUE;
+ }
+ if (CERT_SPKDigestValueForCert(NULL,testCert,SEC_OID_MD5, &item) == NULL) {
+ return PR_FALSE;
+ }
+ if (SECITEM_ItemsAreEqual(certIndex,&item)) {
+ return PR_TRUE;
+ }
+ if (CERT_SPKDigestValueForCert(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);
+
+/*
+ * Check the signature on some OCSP data. This is a helper function that
+ * can be used to check either a request or a response. The result is
+ * saved in the signature structure itself for future reference (to avoid
+ * repeating the expensive verification operation), as well as returned.
+ * In addition to checking the signature, the certificate (and its chain)
+ * are also checked for validity (at the specified time) and usage.
+ *
+ * The type of cert lookup to be performed is specified by "lookupByName":
+ * if true, then "certIndex" is actually a CERTName; otherwise it is a
+ * SECItem which contains a key hash.
+ *
+ * If the signature verifies okay, and the argument "pSignerCert" is not
+ * null, that parameter will be filled-in with a pointer to the signer's
+ * certificate. The caller is then responsible for destroying the cert.
+ *
+ * A return of SECSuccess means the verification succeeded. If not,
+ * an error will be set with the reason. Most likely are:
+ * 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.)
+ */
+static SECStatus
+ocsp_CheckSignature(ocspSignature *signature, void *tbs,
+ const SEC_ASN1Template *encodeTemplate,
+ CERTCertDBHandle *handle, SECCertUsage certUsage,
+ int64 checkTime, PRBool lookupByName, void *certIndex,
+ void *pwArg, CERTCertificate **pSignerCert,
+ CERTCertificate *issuer)
+{
+ SECItem rawSignature;
+ SECItem *encodedTBS = NULL;
+ CERTCertificate *responder = NULL;
+ CERTCertificate *signerCert = NULL;
+ SECKEYPublicKey *signerKey = NULL;
+ CERTCertificate **certs = NULL;
+ SECStatus rv = SECFailure;
+ int certCount;
+ int i;
+
+ /*
+ * 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;
+ }
+
+ /*
+ * 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.
+ */
+ certCount = 0;
+ if (signature->derCerts != NULL) {
+ for (; signature->derCerts[certCount] != NULL; certCount++) {
+ /* just counting */
+ /*IMPORT CERT TO SPKI TABLE */
+ }
+ }
+ rv = CERT_ImportCerts(handle, certUsage, 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 *encodedName;
+
+ encodedName = SEC_ASN1EncodeItem(NULL, NULL, certIndex,
+ CERT_NameTemplate);
+ if (encodedName == NULL)
+ goto finish;
+
+ signerCert = CERT_FindCertByName(handle, encodedName);
+ SECITEM_FreeItem(encodedName, PR_TRUE);
+ } 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.
+ */
+ 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) {
+ rv = SECFailure;
+ if (PORT_GetError() == SEC_ERROR_UNKNOWN_CERT) {
+ /* Make the error a little more specific. */
+ PORT_SetError(SEC_ERROR_UNKNOWN_SIGNER);
+ }
+ 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;
+
+ /*
+ * Just because we have a cert does not mean it is any good; check
+ * it for validity, trust and usage.
+ */
+ rv = CERT_VerifyCert(handle, signerCert, PR_TRUE, certUsage, checkTime,
+ pwArg, NULL);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
+ goto finish;
+ }
+
+ /*
+ * Now get the public key from the signer's certificate; we need
+ * it to perform the verification.
+ */
+ signerKey = CERT_ExtractPublicKey(signerCert);
+ if (signerKey == NULL)
+ goto finish;
+
+ /*
+ * Prepare the data to be verified; it needs to be DER encoded first.
+ */
+ encodedTBS = SEC_ASN1EncodeItem(NULL, NULL, tbs, encodeTemplate);
+ if (encodedTBS == NULL)
+ goto finish;
+
+ /*
+ * 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.
+ */
+ rawSignature = signature->signature;
+ /*
+ * The raw signature is a bit string, but we need to represent its
+ * length in bytes, because that is what the verify function expects.
+ */
+ DER_ConvertBitString(&rawSignature);
+
+ rv = VFY_VerifyData(encodedTBS->data, encodedTBS->len, signerKey,
+ &rawSignature,
+ SECOID_GetAlgorithmTag(&signature->signatureAlgorithm),
+ 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);
+ }
+ }
+
+ if (encodedTBS != NULL)
+ SECITEM_FreeItem(encodedTBS, PR_TRUE);
+
+ if (signerKey != NULL)
+ SECKEY_DestroyPublicKey(signerKey);
+
+ if (certs != NULL)
+ CERT_DestroyCertArray(certs, certCount);
+ /* Free CERTS from SPKDigest Table */
+
+ 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)
+{
+ ocspResponseData *tbsData; /* this is what is signed */
+ PRBool byName;
+ void *certIndex;
+ int64 producedAt;
+ SECStatus rv;
+
+ tbsData = ocsp_GetResponseData(response);
+
+ PORT_Assert(tbsData->responderID != NULL);
+ switch (tbsData->responderID->responderIDType) {
+ case ocspResponderID_byName:
+ byName = PR_TRUE;
+ certIndex = &tbsData->responderID->responderIDValue.name;
+ break;
+ case ocspResponderID_byKey:
+ byName = PR_FALSE;
+ certIndex = &tbsData->responderID->responderIDValue.keyHash;
+ break;
+ case ocspResponderID_other:
+ default:
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+ return SECFailure;
+ }
+
+ /*
+ * ocsp_CheckSignature 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)
+ return rv;
+
+ return ocsp_CheckSignature(ocsp_GetResponseSignature(response),
+ tbsData, ocsp_ResponseDataTemplate,
+ handle, certUsageStatusResponder, producedAt,
+ byName, certIndex, pwArg, pSignerCert, issuer);
+}
+
+/*
+ * See if two certIDs match. This can be easy or difficult, depending
+ * on whether the same hash algorithm was used.
+ */
+static PRBool
+ocsp_CertIDsMatch(CERTCertDBHandle *handle,
+ CERTOCSPCertID *certID1, CERTOCSPCertID *certID2)
+{
+ PRBool match = PR_FALSE;
+ SECItem *foundHash = NULL;
+ 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(&certID1->serialNumber,
+ &certID2->serialNumber) != SECEqual) {
+ goto done;
+ }
+
+ if (SECOID_CompareAlgorithmID(&certID1->hashAlgorithm,
+ &certID2->hashAlgorithm) == SECEqual) {
+ /*
+ * If the hash algorithms match then we can do a simple compare
+ * of the hash values themselves.
+ */
+ if ((SECITEM_CompareItem(&certID1->issuerNameHash,
+ &certID2->issuerNameHash) == SECEqual)
+ && (SECITEM_CompareItem(&certID1->issuerKeyHash,
+ &certID2->issuerKeyHash) == SECEqual)) {
+ match = PR_TRUE;
+ }
+ goto done;
+ }
+
+ hashAlg = SECOID_FindOIDTag(&certID2->hashAlgorithm.algorithm);
+ switch (hashAlg) {
+ case SEC_OID_SHA1:
+ keyHash = &certID1->issuerSHA1KeyHash;
+ nameHash = &certID1->issuerSHA1NameHash;
+ break;
+ case SEC_OID_MD5:
+ keyHash = &certID1->issuerMD5KeyHash;
+ nameHash = &certID1->issuerMD5NameHash;
+ break;
+ case SEC_OID_MD2:
+ keyHash = &certID1->issuerMD2KeyHash;
+ nameHash = &certID1->issuerMD2NameHash;
+ break;
+ default:
+ foundHash = NULL;
+ break;
+ }
+
+ if (foundHash == NULL) {
+ goto done;
+ }
+ PORT_Assert(keyHash && nameHash);
+
+ if ((SECITEM_CompareItem(nameHash, &certID2->issuerNameHash) == SECEqual)
+ && (SECITEM_CompareItem(keyHash, &certID2->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(handle, certID, single->certID) == PR_TRUE) {
+ 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 true if the given signerCert is the default responder for
+ * the given certID. If not, or if any error, return false.
+ */
+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 given signerCert is the default responder for
+ * the given certID. If not, or if any error, return false.
+ */
+static PRBool
+ocsp_CertIsDefaultResponderForCertID(CERTCertDBHandle *handle,
+ CERTCertificate *signerCert,
+ CERTOCSPCertID *certID)
+{
+ CERTCertificate *defaultResponderCert;
+
+ defaultResponderCert = ocsp_CertGetDefaultResponder(handle, certID);
+ return (PRBool) (defaultResponderCert == signerCert);
+}
+
+/*
+ * 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,
+ int64 thisUpdate)
+{
+ CERTCertificate *issuerCert = NULL;
+ SECItem *issuerKeyHash = NULL;
+ SECOidTag hashAlg;
+ PRBool okay = PR_FALSE;
+
+ /*
+ * Check first for a trusted responder, which overrides everything else.
+ */
+ if (ocsp_CertIsDefaultResponderForCertID(handle, signerCert, certID))
+ 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.
+ */
+
+ if (ocsp_CertIsOCSPSigner(signerCert)) {
+ /*
+ * 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);
+ goto loser;
+ }
+ } else {
+ /*
+ * The signer must *be* the issuer of the cert being checked.
+ */
+ issuerCert = signerCert;
+ }
+
+ hashAlg = SECOID_FindOIDTag(&certID->hashAlgorithm.algorithm);
+ issuerKeyHash = CERT_SPKDigestValueForCert(NULL, issuerCert, hashAlg, NULL);
+ if (issuerKeyHash == NULL)
+ goto loser;
+
+ if (SECITEM_CompareItem(issuerKeyHash,
+ &certID->issuerKeyHash) != SECEqual) {
+ PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE);
+ goto loser;
+ }
+
+ okay = PR_TRUE;
+
+loser:
+ if (issuerKeyHash != NULL)
+ SECITEM_FreeItem(issuerKeyHash, PR_TRUE);
+
+ if (issuerCert != NULL && issuerCert != signerCert)
+ CERT_DestroyCertificate(issuerCert);
+
+ return okay;
+}
+
+/*
+ * 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(int64 checkTime)
+{
+ int64 now = PR_Now();
+ int64 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 */
+
+/*
+ * 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,
+ int64 producedAt)
+{
+ CERTOCSPCertID *certID = single->certID;
+ int64 now, thisUpdate, nextUpdate, tmstamp, tmp;
+ SECStatus rv;
+
+ /*
+ * 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(tmstamp, tmstamp, tmp); /* convert the slop time to PRTime */
+ LL_ADD(tmstamp, tmstamp, 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;
+
+ if (LL_CMP(nextUpdate, <, now) || LL_CMP(producedAt, >, nextUpdate)) {
+ PORT_SetError(SEC_ERROR_OCSP_OLD_RESPONSE);
+ return SECFailure;
+ }
+ } else if (ocsp_TimeIsRecent(thisUpdate) != PR_TRUE) {
+ PORT_SetError(SEC_ERROR_OCSP_OLD_RESPONSE);
+ return SECFailure;
+ }
+
+ 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_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.
+ */
+char *
+CERT_GetOCSPAuthorityInfoAccessLocation(CERTCertificate *cert)
+{
+ CERTGeneralName *locname = NULL;
+ SECItem *location = NULL;
+ SECItem *encodedAuthInfoAccess = NULL;
+ CERTAuthInfoAccess **authInfoAccess = NULL;
+ char *locURI = NULL;
+ PRArenaPool *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)
+ 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_EXTENSION_NOT_FOUND);
+ 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_EXTENSION_NOT_FOUND);
+ 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 a default responder is set up, 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.
+ */
+static char *
+ocsp_GetResponderLocation(CERTCertDBHandle *handle, CERTCertificate *cert,
+ PRBool *isDefault)
+{
+ ocspCheckingContext *ocspcx;
+
+ 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;
+ return CERT_GetOCSPAuthorityInfoAccessLocation(cert);
+}
+
+/*
+ * Return SECSuccess if the cert was revoked *after* "time",
+ * SECFailure otherwise.
+ */
+static SECStatus
+ocsp_CertRevokedAfter(ocspRevokedInfo *revokedInfo, int64 time)
+{
+ int64 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.
+ */
+static SECStatus
+ocsp_CertHasGoodStatus(CERTOCSPSingleResponse *single, int64 time)
+{
+ ocspCertStatus *status;
+ SECStatus rv;
+
+ status = single->certStatus;
+
+ 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;
+}
+
+
+/*
+ * 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)
+ * int64 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,
+ int64 time, void *pwArg)
+{
+ char *location = NULL;
+ PRBool locationIsDefault;
+ CERTCertList *certList = NULL;
+ SECItem *encodedResponse = NULL;
+ CERTOCSPRequest *request = NULL;
+ CERTOCSPResponse *response = NULL;
+ CERTCertificate *signerCert = NULL;
+ CERTCertificate *issuerCert = NULL;
+ CERTOCSPCertID *certID;
+ SECStatus rv = SECFailure;
+
+
+ /*
+ * 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, &locationIsDefault);
+ if (location == NULL) {
+ if (PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND)
+ return SECSuccess;
+ else
+ return SECFailure;
+ }
+
+ /*
+ * For now, create a cert-list of one.
+ * 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.)
+ */
+
+ certList = CERT_NewCertList();
+ if (certList == NULL)
+ goto loser;
+
+ /* dup it because freeing the list will destroy the cert, too */
+ cert = CERT_DupCertificate(cert);
+ if (cert == NULL)
+ goto loser;
+
+ if (CERT_AddCertToListTail(certList, cert) != SECSuccess) {
+ CERT_DestroyCertificate(cert);
+ goto loser;
+ }
+
+ /*
+ * 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.
+ */
+ encodedResponse = CERT_GetEncodedOCSPResponse(NULL, certList, location,
+ time, locationIsDefault,
+ NULL, pwArg, &request);
+ if (encodedResponse == NULL) {
+ goto loser;
+ }
+
+ response = CERT_DecodeOCSPResponse(encodedResponse);
+ if (response == NULL) {
+ goto loser;
+ }
+
+ /*
+ * 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(response) != 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(response, 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. It will need
+ * to loop, and for each certID in the request, find the matching
+ * single response and check the status specified by it.
+ *
+ * We are helped here in that we know that the requests are made with
+ * the request list in the same order as the order of the certs we hand
+ * to it. This is why I can directly access the first member of the
+ * single request array for the one cert I care about.
+ */
+
+ certID = request->tbsRequest->requestList[0]->reqCert;
+ rv = CERT_GetOCSPStatusForCertID(handle, response, certID,
+ signerCert, time);
+loser:
+ if (issuerCert != NULL)
+ CERT_DestroyCertificate(issuerCert);
+ if (signerCert != NULL)
+ CERT_DestroyCertificate(signerCert);
+ if (response != NULL)
+ CERT_DestroyOCSPResponse(response);
+ if (request != NULL)
+ CERT_DestroyOCSPRequest(request);
+ if (encodedResponse != NULL)
+ SECITEM_FreeItem(encodedResponse, PR_TRUE);
+ if (certList != NULL)
+ CERT_DestroyCertList(certList);
+ if (location != NULL)
+ PORT_Free(location);
+ return rv;
+}
+
+SECStatus
+CERT_GetOCSPStatusForCertID(CERTCertDBHandle *handle,
+ CERTOCSPResponse *response,
+ CERTOCSPCertID *certID,
+ CERTCertificate *signerCert,
+ int64 time)
+{
+ SECStatus rv;
+ ocspResponseData *responseData;
+ int64 producedAt;
+ CERTOCSPSingleResponse *single;
+
+ /*
+ * The ResponseData part is the real guts of the response.
+ */
+ responseData = ocsp_GetResponseData(response);
+ 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;
+
+ /*
+ * Okay, the last step is to check whether the status says revoked,
+ * and if so how that compares to the time value passed into this routine.
+ */
+
+ rv = ocsp_CertHasGoodStatus(single, time);
+loser:
+ 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;
+ }
+
+ /*
+ * 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 (statusContext != NULL)
+ PORT_Free(statusContext);
+ 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;
+ } else {
+ PORT_Assert(statusContext->useDefaultResponder == PR_FALSE);
+ CERT_DestroyCertificate(cert);
+ }
+
+ 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;
+
+ 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;
+
+ /*
+ * And hang onto it.
+ */
+ statusContext->defaultResponderCert = cert;
+
+ /*
+ * 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;
+
+ 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;
+
+ if (statusContext->defaultResponderCert != NULL) {
+ CERT_DestroyCertificate(statusContext->defaultResponderCert);
+ statusContext->defaultResponderCert = NULL;
+ }
+
+ /*
+ * Finally, record the fact.
+ */
+ statusContext->useDefaultResponder = PR_FALSE;
+ return SECSuccess;
+}
+
+/*
+ * 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_SPKDigestValueForCert(PRArenaPool *arena, CERTCertificate *cert,
+ SECOidTag digestAlg, SECItem *fill)
+{
+ const SECHashObject *digestObject;
+ void *digestContext;
+ SECItem *result = NULL;
+ void *mark = NULL;
+ SECItem spk;
+
+ 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;
+ }
+ fill = result;
+ }
+
+ /*
+ * 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);
+
+ /*
+ * Now digest the value, using the specified algorithm.
+ */
+ digestContext = digestObject->create();
+ if ( digestContext == NULL ) {
+ goto loser;
+ }
+ digestObject->begin(digestContext);
+ digestObject->update(digestContext, spk.data, spk.len);
+ digestObject->end(digestContext, fill->data, &(fill->len), fill->len);
+ digestObject->destroy(digestContext, PR_TRUE);
+
+ if ( arena != NULL ) {
+ PORT_ArenaUnmark(arena, mark);
+ }
+ return(fill);
+
+loser:
+ if ( arena != NULL ) {
+ PORT_ArenaRelease(arena, mark);
+ } else {
+ if ( result != NULL ) {
+ SECITEM_FreeItem(result, (fill == NULL) ? PR_TRUE : PR_FALSE);
+ }
+ }
+ return(NULL);
+}
+
+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_other:
+ 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 000000000..428c624d4
--- /dev/null
+++ b/security/nss/lib/certhigh/ocsp.h
@@ -0,0 +1,534 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Interface to the OCSP implementation.
+ *
+ * $Id$
+ */
+
+#ifndef _OCSP_H_
+#define _OCSP_H_
+
+
+#include "plarena.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "keyt.h"
+#include "certt.h"
+#include "ocspt.h"
+
+
+/************************************************************************/
+SEC_BEGIN_PROTOS
+
+/*
+ * 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.
+ * 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.).
+ */
+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);
+
+/*
+ * -------------------------------------------------------
+ * 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.
+ * int64 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, int64 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:
+ * PRArenaPool *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(PRArenaPool *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(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(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:
+ * PRArenaPool *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.
+ * char *location
+ * The location of the OCSP responder (a URL).
+ * int64 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(PRArenaPool *arena, CERTCertList *certList,
+ char *location, int64 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(CERTCertificate *cert);
+
+/*
+ * 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)
+ * int64 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,
+ int64 time, void *pwArg);
+/*
+ * FUNCTION: CERT_GetOCSPStatusForCertID
+ * Returns the OCSP status contained in the passed in paramter 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.
+ * int64 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,
+ int64 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.
+ * int64 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, int64 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);
+/************************************************************************/
+SEC_END_PROTOS
+
+#endif /* _OCSP_H_ */
diff --git a/security/nss/lib/certhigh/ocspt.h b/security/nss/lib/certhigh/ocspt.h
new file mode 100644
index 000000000..3f1563855
--- /dev/null
+++ b/security/nss/lib/certhigh/ocspt.h
@@ -0,0 +1,59 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Public header for exported OCSP types.
+ *
+ * $Id$
+ */
+
+#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 CERTOCSPCertStatusStr CERTOCSPCertStatus;
+typedef struct CERTOCSPSingleResponseStr CERTOCSPSingleResponse;
+
+#endif /* _OCSPT_H_ */
diff --git a/security/nss/lib/certhigh/ocspti.h b/security/nss/lib/certhigh/ocspti.h
new file mode 100644
index 000000000..2bb7bfe72
--- /dev/null
+++ b/security/nss/lib/certhigh/ocspti.h
@@ -0,0 +1,405 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Private header defining OCSP types.
+ *
+ * $Id$
+ */
+
+#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 {
+ PRArenaPool *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 {
+ PRArenaPool *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;
+ PRArenaPool *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_successful = 0,
+ ocspResponse_malformedRequest = 1,
+ ocspResponse_internalError = 2,
+ ocspResponse_tryLater = 3,
+ ocspResponse_unused = 4,
+ ocspResponse_sigRequired = 5,
+ ocspResponse_unauthorized = 6,
+ ocspResponse_other /* unknown/unrecognized value */
+} 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 {
+ PRArenaPool *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 {
+ 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;
+};
+
+/*
+ * 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_byName,
+ ocspResponderID_byKey,
+ ocspResponderID_other /* unknown kind of responderID */
+} ocspResponderIDType;
+
+struct ocspResponderIDStr {
+ ocspResponderIDType 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 {
+ PRArenaPool *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 000000000..d428d73c8
--- /dev/null
+++ b/security/nss/lib/certhigh/xcrldist.c
@@ -0,0 +1,230 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Code for dealing with x.509 v3 CRL Distribution Point extension.
+ */
+#include "genname.h"
+#include "certt.h"
+#include "secerr.h"
+
+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 CRLDistributionPointTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CRLDistributionPoint) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | 0,
+ offsetof(CRLDistributionPoint,derDistPoint), SEC_AnyTemplate},
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(CRLDistributionPoint,bitsmap), 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 (PRArenaPool *arena, CERTCrlDistributionPoints *value,
+ SECItem *derValue)
+{
+ CRLDistributionPoint **pointList, *point;
+ PRArenaPool *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;
+
+ if (point->distPointType == generalName) {
+ point->derFullName = cert_EncodeGeneralNames
+ (ourPool, point->distPoint.fullName);
+
+ if (point->derFullName) {
+ rv = (SEC_ASN1EncodeItem (ourPool, &point->derDistPoint,
+ point, FullNameTemplate) == NULL) ? SECFailure : SECSuccess;
+ } else {
+ rv = SECFailure;
+ }
+ }
+ else if (point->distPointType == relativeDistinguishedName) {
+ if (SEC_ASN1EncodeItem
+ (ourPool, &point->derDistPoint,
+ point, RelativeNameTemplate) == NULL)
+ rv = SECFailure;
+ }
+ /* distributionPointName is omitted */
+ else if (point->distPointType != 0) {
+ PORT_SetError (SEC_ERROR_EXTENSION_VALUE_INVALID);
+ rv = SECFailure;
+ }
+ 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->crlIssuer)
+ break;
+ }
+
+ ++pointList;
+ }
+ if (rv != SECSuccess)
+ break;
+ if (SEC_ASN1EncodeItem
+ (arena, derValue, value, CERTCRLDistributionPointsTemplate) == NULL) {
+ rv = SECFailure;
+ break;
+ }
+ } while (0);
+ PORT_FreeArena (ourPool, PR_FALSE);
+ return (rv);
+}
+
+CERTCrlDistributionPoints *
+CERT_DecodeCRLDistributionPoints (PRArenaPool *arena, SECItem *encodedValue)
+{
+ CERTCrlDistributionPoints *value = NULL;
+ CRLDistributionPoint **pointList, *point;
+ SECStatus rv;
+ SECItem newEncodedValue;
+
+ PORT_Assert (arena);
+ do {
+ value = (CERTCrlDistributionPoints*)PORT_ArenaZAlloc (arena, sizeof (*value));
+ 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 (*pointList) {
+ point = *pointList;
+
+ /* get the data if the distributionPointName is not omitted */
+ if (point->derDistPoint.data != NULL) {
+ point->distPointType = (DistributionPointTypes)
+ ((point->derDistPoint.data[0] & 0x1f) +1);
+ if (point->distPointType == generalName) {
+ SECItem innerDER;
+
+ innerDER.data = NULL;
+ rv = SEC_QuickDERDecodeItem
+ (arena, point, FullNameTemplate, &(point->derDistPoint));
+ if (rv != SECSuccess)
+ break;
+ point->distPoint.fullName = cert_DecodeGeneralNames
+ (arena, point->derFullName);
+
+ if (!point->distPoint.fullName)
+ break;
+ }
+ else if ( relativeDistinguishedName) {
+ rv = SEC_QuickDERDecodeItem
+ (arena, point, RelativeNameTemplate, &(point->derDistPoint));
+ if (rv != SECSuccess)
+ break;
+ }
+ else {
+ PORT_SetError (SEC_ERROR_EXTENSION_VALUE_INVALID);
+ break;
+ }
+ }
+
+ /* Get the reason code if it's not omitted in the encoding */
+ if (point->bitsmap.data != NULL) {
+ point->reasons.data = (unsigned char*) PORT_ArenaAlloc
+ (arena, (point->bitsmap.len + 7) >> 3);
+ if (!point->reasons.data) {
+ rv = SECFailure;
+ break;
+ }
+ PORT_Memcpy (point->reasons.data, point->bitsmap.data,
+ point->reasons.len = ((point->bitsmap.len + 7) >> 3));
+ }
+
+ /* 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;
+ }
+ } while (0);
+ return (rv == SECSuccess ? value : NULL);
+}