Files
mars-matrixssl/crypto/keyformat/crl.c
2016-11-28 09:48:14 +02:00

1125 lines
29 KiB
C

/**
* @file crl.c
* @version $Format:%h%d$
*
* Certificate Revocation List tools
*/
/*
* Copyright (c) 2013-2016 INSIDE Secure Corporation
* All Rights Reserved
*
* The latest version of this code is available at http://www.matrixssl.org
*
* This software is open source; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This General Public License does NOT permit incorporating this software
* into proprietary programs. If you are unable to comply with the GPL, a
* commercial license for this software may be purchased from INSIDE at
* http://www.insidesecure.com/
*
* This program is distributed in WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* http://www.gnu.org/copyleft/gpl.html
*/
/******************************************************************************/
#include "../cryptoApi.h"
#ifdef USE_CRL
#ifdef USE_CERT_PARSE
#ifdef USE_MULTITHREADING
static psMutex_t g_crlTableLock;
#endif
/* Seems like many CRLs are not adhering to the specification that this
extension be present. That just leaves us with the DN to match against
if we disable this define. Not a big concern to disable it because the
authentication is either going to pass or fail based on sig validation */
//#define ENFORCE_CRL_AUTH_KEY_ID_EXT
static void internalFreeCRL(psX509Crl_t *crl);
/* The global CRL cache is a linked list of psX509Crl_t structures. A
psX509Crl_t structure represents a single CRL file */
static psX509Crl_t *g_CRL = NULL;
/* Invoked from psCryptoOpen */
int32_t psCrlOpen()
{
#ifdef USE_MULTITHREADING
psCreateMutex(&g_crlTableLock, 0);
#endif
return PS_SUCCESS;
}
/* Invoked from psCryptoClose */
void psCrlClose()
{
psCRL_DeleteAll();
#ifdef USE_MULTITHREADING
psDestroyMutex(&g_crlTableLock);
#endif
}
/* Helper for CRL insert */
static int internalCRLInsert(psX509Crl_t *crl)
{
psX509Crl_t *next;
if (crl == NULL) {
return 0;
}
if (g_CRL == NULL) {
/* first one */
g_CRL = crl;
return 1;
}
/* append */
next = g_CRL;
if (g_CRL == crl) {
return 0; /* no pointer dups */
}
while (next->next) {
next = next->next;
if (next == crl) { /* no pointer dups */
return 0;
}
}
next->next = crl;
return 1;
}
/* Blindly append a CRL to the g_CRL list. Consider psCRL_Update instead.
1 - Added, 0 - Didn't */
int psCRL_Insert(psX509Crl_t *crl)
{
int rc;
#ifdef USE_MULTITHREADING
psLockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
rc = internalCRLInsert(crl);
#ifdef USE_MULTITHREADING
psUnlockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
return rc;
}
/* Helper for Remove and Delete to take a CRL out of g_CRL */
static int internalShrinkCRLtable(psX509Crl_t *crl, int delete)
{
psX509Crl_t *prev, *curr, *next;
/* Return whether or not it was found in the list to help with the
standalone psX509FreeCRL call logic */
if (g_CRL == NULL || crl == NULL) {
return 0;
}
prev = NULL;
curr = g_CRL;
next = curr->next;
while (curr) {
if (curr == crl) {
if (delete) {
internalFreeCRL(crl);
} else {
curr->next = NULL;
}
if (prev == NULL && next == NULL) {
/* Only one in list */
g_CRL = NULL;
} else if (prev == NULL && next != NULL) {
/* Removed first one in list */
g_CRL = next;
} else if (prev != NULL) {
/* Removed middle or end */
prev->next = next;
}
return 1;
}
prev = curr;
curr = curr->next;
if (curr) {
/* curr can be NULL if we never found crl */
next = curr->next;
}
}
return 0;
}
/* Remove a CRL from g_CRL but don't free the associated CRL */
int psCRL_Remove(psX509Crl_t *crl)
{
int rc;
#ifdef USE_MULTITHREADING
psLockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
rc = internalShrinkCRLtable(crl, 0);
#ifdef USE_MULTITHREADING
psUnlockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
return rc;
}
/* Remove a CRL from g_CRL and free the associated CRL */
int psCRL_Delete(psX509Crl_t *crl)
{
int rc;
#ifdef USE_MULTITHREADING
psLockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
rc = internalShrinkCRLtable(crl, 1);
#ifdef USE_MULTITHREADING
psUnlockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
return rc;
}
/* Remove all CRLs from g_CRL but don't free the associated memory. Assumes
the user will be using psX509FreeCRL later */
void psCRL_RemoveAll()
{
psX509Crl_t *curr, *next;
#ifdef USE_MULTITHREADING
psLockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
curr = g_CRL;
next = curr->next;
while (next) {
next = curr->next;
curr->next = NULL;
curr = next;
}
g_CRL = NULL;
#ifdef USE_MULTITHREADING
psUnlockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
}
/* Remove all CRLs from g_CRL and free the associated memory */
void psCRL_DeleteAll()
{
psX509Crl_t *curr, *next;
#ifdef USE_MULTITHREADING
psLockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
curr = g_CRL;
while (curr) {
next = curr->next;
internalShrinkCRLtable(curr, 1);
curr = next;
}
psAssert(g_CRL == NULL);
#ifdef USE_MULTITHREADING
psUnlockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
}
/* Helpers to see if the two CRLs are from the same issuer */
int32 internalCRLmatch(psX509Crl_t *existing, psX509Crl_t *new)
{
/* Same DN? */
if (memcmpct(existing->issuer.hash, new->issuer.hash, SHA1_HASH_SIZE) != 0){
return -1;
}
#ifdef ENFORCE_CRL_AUTH_KEY_ID_EXT
/* Same AuthKeyId? */
if (existing->extensions.ak.keyId == NULL ||
new->extensions.ak.keyId == NULL) {
/* Should never be possible */
return PS_PARSE_FAIL;
}
if (existing->extensions.ak.keyLen != new->extensions.ak.keyLen) {
return -1;
}
if (memcmpct(existing->extensions.ak.keyId, new->extensions.ak.keyId,
new->extensions.ak.keyLen) != 0) {
return -1;
}
#endif
/* Looks like a match */
return PS_TRUE;
}
/* Remove existing CRL if it exists. Append this one.
FUTURE: Support Delta CRL */
int psCRL_Update(psX509Crl_t *crl, int deleteExisting)
{
psX509Crl_t *curr, *next;
int rc;
if (crl == NULL) {
return 0;
}
#ifdef USE_MULTITHREADING
psLockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
/* Currently no Delta CRL support so replace the CRL if we find the
same issuer. Add otherwise. */
curr = g_CRL;
while (curr) {
next = curr->next;
if (internalCRLmatch(curr, crl) == PS_TRUE) {
/* Just do a check to make sure the user isn't trying to update
with the exact same CRL pointer */
if (curr == crl) {
#ifdef USE_MULTITHREADING
psUnlockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
return 0;
}
internalShrinkCRLtable(curr, deleteExisting);
break;
}
curr = next;
}
rc = internalCRLInsert(crl);
#ifdef USE_MULTITHREADING
psUnlockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
return rc;
}
/* Helper to see if we have a matching CRL for the given subject cert. So
this means we are looking at the issuer/authority fields of the cert */
static int32 internalMatchSubject(psX509Cert_t *cert, psX509Crl_t *CRL)
{
/* Same DN? */
if (memcmpct(CRL->issuer.hash, cert->issuer.hash, SHA1_HASH_SIZE) != 0) {
return PS_CERT_AUTH_FAIL_DN;
}
#ifdef ENFORCE_CRL_AUTH_KEY_ID_EXT
/* Same AuthKeyId? */
if (CRL->extensions.ak.keyId == NULL) {
return PS_CERT_AUTH_FAIL_EXTENSION;
}
if (cert->extensions.ak.keyId == NULL) {
return PS_CERT_AUTH_FAIL_EXTENSION;
}
if (CRL->extensions.ak.keyLen != cert->extensions.ak.keyLen) {
return PS_CERT_AUTH_FAIL_EXTENSION;
}
if (memcmpct(CRL->extensions.ak.keyId, cert->extensions.ak.keyId,
CRL->extensions.ak.keyLen) != 0) {
return PS_CERT_AUTH_FAIL_EXTENSION;
}
#endif
/* Looks good */
return 1;
}
/* Check if nextUpdate time appears correct. Returns -1 when
timestamp was unparseable or the CRL was expired. 0 for success. */
static int32_t nextUpdateTest(const char *c, int32 timeType)
{
int32 err;
psBrokenDownTime_t timeNow;
psBrokenDownTime_t nextTime;
psBrokenDownTime_t nextTimeLinger;
err = psGetBrokenDownGMTime(&timeNow, 0);
if (err != PS_SUCCESS)
return -1;
err = psBrokenDownTimeImport(
&nextTime, c, strlen(c),
timeType == ASN_UTCTIME ?
PS_BROKENDOWN_TIME_IMPORT_2DIGIT_YEAR : 0);
if (err != PS_SUCCESS)
return -1;
memcpy(&nextTimeLinger, &nextTime, sizeof nextTimeLinger);
err = psBrokenDownTimeAdd(&nextTimeLinger, PS_CRL_TIME_LINGER);
if (err != PS_SUCCESS)
return -1;
if (psBrokenDownTimeCmp(&timeNow, &nextTimeLinger) > 0) {
/* nextTime is in past. */
return -1;
}
return 0;
}
static psX509Crl_t* internalGetCrlForCert(psX509Cert_t *cert)
{
psX509Crl_t *curr;
if (cert == NULL) {
return NULL;
}
curr = g_CRL;
while (curr) {
if (internalMatchSubject(cert, curr) == PS_TRUE) {
/* This is the point where we want to make sure this CRL isn't
expired. We do this by looking at the nextUpdate time and
seeing if we are beyond that */
if (nextUpdateTest(curr->nextUpdate, curr->nextUpdateType) < 0) {
/* Got it, but it's expired */
curr->expired = 1;
}
return curr;
}
curr = curr->next;
}
return NULL;
}
/* Given a cert, do we have a CRL match for the issuer?
Return if so or NULL if not */
psX509Crl_t* psCRL_GetCRLForCert(psX509Cert_t *cert)
{
psX509Crl_t *rc = NULL;
#ifdef USE_MULTITHREADING
psLockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
rc = internalGetCrlForCert(cert);
#ifdef USE_MULTITHREADING
psUnlockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
return rc;
}
/*
-1 no entry in the cache for this cert at all
0 entry is found and cert is NOT revoked
1 entry is found and cert IS revoked
A CRL may be passed in if that specific one is being tested. Otherwise
pass NULL to search the g_CRL
*/
int32_t internalCrlIsRevoked(psX509Cert_t *cert, psX509Crl_t *CRL)
{
psX509Crl_t *crl;
x509revoked_t *entry;
if (cert == NULL) {
return -1;
}
if (CRL) {
crl = CRL;
} else {
if ((crl = internalGetCrlForCert(cert)) == NULL) {
return -1;
}
}
if (crl->revoked == NULL) {
/* It is totally reasonable to have a CRL with no revoked certs */
return 0;
}
for (entry = crl->revoked; entry != NULL; entry = entry->next) {
if (cert->serialNumberLen == entry->serialLen) {
if (memcmpct(cert->serialNumber, entry->serial, entry->serialLen)
== 0) {
return 1; /* REVOKED! */
}
}
}
return 0; /* never found it. good to go */
}
/*
Not sure this needs to be public. The "determine" API is actually
doing the full check
-1 no entry in the cache for this cert at all
0 entry is found and cert is NOT revoked
1 entry is found and cert IS revoked
A CRL may be passed in if that specific one is being tested. Otherwise
pass NULL to search the g_CRL
*/
int32_t psCRL_isRevoked(psX509Cert_t *cert, psX509Crl_t *CRL)
{
int32_t rc;
#ifdef USE_MULTITHREADING
psLockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
rc = internalCrlIsRevoked(cert, CRL);
#ifdef USE_MULTITHREADING
psUnlockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
return rc;
}
static int doesCertExpectCRL(psX509Cert_t *cert)
{
if (cert->extensions.crlDist) {
return PS_TRUE;
}
return PS_FALSE;
}
/*
Uses the psCRL_ format to highlight the use of g_CRL cache
Updates the "revokedStatus" member of a psX509Cert_t based on information
from within the cert itself and on the revocation status if a g_CRL entry
is found.
*/
int32_t psCRL_determineRevokedStatus(psX509Cert_t *cert)
{
psX509Crl_t *crl;
int expectCrl;
int32_t revoked;
if (cert == NULL) {
return 0;
}
#ifdef USE_MULTITHREADING
psLockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
crl = internalGetCrlForCert(cert);
if (crl) {
/* Not going to move along if the CRL has expired */
if (crl->expired) {
cert->revokedStatus = CRL_CHECK_CRL_EXPIRED;
#ifdef USE_MULTITHREADING
psUnlockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
return cert->revokedStatus;
}
/* If we now have a CRL that is not authenticated yet, let's see if
if our subject happens to have a parent that we can try against.
This case happens if a CRL for an child certificate was
fetched out-of-handshake and now a reconnection attempt is being
made. We now have the parent for that child cert and can
attempt to authenticate */
if (crl->authenticated == 0 && cert->next) {
psX509AuthenticateCRL(cert->next, crl, NULL);
}
/* test it and set the status */
revoked = internalCrlIsRevoked(cert, crl);
if (revoked == 0 && crl->authenticated == 1) {
cert->revokedStatus = CRL_CHECK_PASSED_AND_AUTHENTICATED;
} else if (revoked == 0 && crl->authenticated == 0) {
cert->revokedStatus = CRL_CHECK_PASSED_BUT_NOT_AUTHENTICATED;
} else if (revoked == 1 && crl->authenticated == 1) {
cert->revokedStatus = CRL_CHECK_REVOKED_AND_AUTHENTICATED;
} else if (revoked == 1 && crl->authenticated == 0) {
cert->revokedStatus = CRL_CHECK_REVOKED_BUT_NOT_AUTHENTICATED;
} else {
psTraceCrypto("Unexpected revoked/authenticated combo\n");
}
} else {
expectCrl = doesCertExpectCRL(cert);
if (expectCrl) {
cert->revokedStatus = CRL_CHECK_EXPECTED; /* but no entry */
} else {
cert->revokedStatus = CRL_CHECK_NOT_EXPECTED;
}
}
#ifdef USE_MULTITHREADING
psUnlockMutex(&g_crlTableLock);
#endif /* USE_MULTITHREADING */
return cert->revokedStatus;
}
/********************* end of psCRL_ family of APIs ***************************/
/******************************************************************************/
static void x509FreeRevoked(x509revoked_t **revoked, psPool_t *pool)
{
x509revoked_t *next, *curr = *revoked;
while (curr) {
next = curr->next;
psFree(curr->serial, pool);
psFree(curr, pool);
curr = next;
}
*revoked = NULL;
}
static void internalFreeCRL(psX509Crl_t *crl)
{
psPool_t *pool;
if (crl == NULL) {
return;
}
/* test all components for NULL. This is used for freeing during
parse so some might not be there at all */
pool = crl->pool;
psX509FreeDNStruct(&crl->issuer, pool);
x509FreeExtensions(&crl->extensions);
x509FreeRevoked(&crl->revoked, pool);
psFree(crl->sig, pool);
psFree(crl->nextUpdate, pool);
memset(crl, 0, sizeof(psX509Crl_t));
psFree(crl, pool);
}
void psX509FreeCRL(psX509Crl_t *crl)
{
if (crl == NULL) {
return;
}
/* Try to delete from g_CRL list first. Will lock table */
if (psCRL_Delete(crl)) {
return;
}
internalFreeCRL(crl);
}
/* Helper for psX509AuthenticateCRL to see if we have a matching CRL for
the given issuer cert */
static int32 internalMatchIssuer(psX509Cert_t *CA, psX509Crl_t *CRL)
{
/* Ensure crlSign flag of KeyUsage for the given CA. */
if ( ! (CA->extensions.keyUsageFlags & KEY_USAGE_CRL_SIGN)) {
return PS_CERT_AUTH_FAIL_EXTENSION;
}
/* Same DN? */
if (memcmpct(CRL->issuer.hash, CA->subject.hash, SHA1_HASH_SIZE) != 0) {
psTraceCrypto("CRL not issued by this CA\n");
return PS_CERT_AUTH_FAIL_DN;
}
#ifdef ENFORCE_CRL_AUTH_KEY_ID_EXT
/* Same AuthKeyId? */
if (CRL->extensions.ak.keyId == NULL) {
psTraceCrypto("CRL does not have a AuthKeyId extension\n");
return PS_CERT_AUTH_FAIL_EXTENSION;
}
if (CA->extensions.sk.id == NULL) {
psTraceCrypto("CA does not have a SubjectKeyId extension\n");
return PS_CERT_AUTH_FAIL_EXTENSION;
}
if (CRL->extensions.ak.keyLen != CA->extensions.sk.len) {
psTraceCrypto("CRL issuer does not have same AuthKeyId as CA\n");
return PS_CERT_AUTH_FAIL_EXTENSION;
}
if (memcmpct(CRL->extensions.ak.keyId, CA->extensions.sk.id,
CRL->extensions.ak.keyLen) != 0) {
psTraceCrypto("CRL issuer does not have same AuthKeyId as CA\n");
return PS_CERT_AUTH_FAIL_EXTENSION;
}
#endif
/* Looks good */
return 1;
}
/*
NO g_CRL used at all
Worth noting that the authenticated state is reset each time this is
called so it shouldn't be called blindly in a loop hoping the status
will come out correctly.
poolUserPtr is for the TMP_PKI pool
*/
int32_t psX509AuthenticateCRL(psX509Cert_t *CA, psX509Crl_t *CRL,
void *poolUserPtr)
{
int32 rc, sigType;
unsigned char sigOut[SHA512_HASH_SIZE];
psPool_t *pkiPool = MATRIX_NO_POOL;
if (CA == NULL || CRL == NULL) {
return PS_ARG_FAIL;
}
if (CRL->authenticated == 1) {
/* Going to have to assume caller knows what they are doing */
psTraceCrypto("WARNING: this CRL has already been authenticated\n");
}
CRL->authenticated = 0;
/* A few tests to see if this CA is the true issuer of the CRL */
if ((rc = internalMatchIssuer(CA, CRL)) < 0) {
return rc;
}
/* Determine digest and signature algorithms. */
switch (CRL->sigAlg) {
#ifdef ENABLE_MD5_SIGNED_CERTS
case OID_MD5_RSA_SIG:
sigType = PS_RSA;
break;
#endif
#ifdef ENABLE_SHA1_SIGNED_CERTS
case OID_SHA1_RSA_SIG:
sigType = PS_RSA;
break;
#ifdef USE_ECC
case OID_SHA1_ECDSA_SIG:
sigType = PS_ECC;
break;
#endif /* USE_ECC */
#endif /* ENABLE_SHA1_SIGNED_CERTS */
#ifdef USE_SHA256
case OID_SHA256_RSA_SIG:
sigType = PS_RSA;
break;
#ifdef USE_ECC
case OID_SHA256_ECDSA_SIG:
sigType = PS_ECC;
break;
#endif /* USE_ECC */
#endif /* USE_SHA256 */
#ifdef USE_SHA384
case OID_SHA384_RSA_SIG:
sigType = PS_RSA;
break;
#ifdef USE_ECC
case OID_SHA384_ECDSA_SIG:
sigType = PS_ECC;
break;
#endif /* USE_ECC */
#endif /* USE_SHA384 */
#ifdef USE_SHA512
case OID_SHA512_RSA_SIG:
sigType = PS_RSA;
break;
#ifdef USE_ECC
case OID_SHA512_ECDSA_SIG:
sigType = PS_ECC;
break;
#endif /* USE_ECC */
#endif /* USE_SHA512 */
default:
psTraceCrypto("Need more signature alg support for CRL\n");
return PS_UNSUPPORTED_FAIL;
}
/* Perform the signature verification. */
if (sigType == PS_RSA) {
rc = pubRsaDecryptSignedElement(pkiPool, &CA->publicKey.key.rsa,
CRL->sig, CRL->sigLen, sigOut, CRL->sigHashLen, NULL);
if (rc < 0) {
psTraceCrypto("Unable to RSA decrypt CRL signature\n");
return rc;
}
if (memcmpct(CRL->sigHash, sigOut, CRL->sigHashLen) != 0) {
psTraceCrypto("Unable to verify CRL signature\n");
return PS_CERT_AUTH_FAIL_SIG;
}
} else if (sigType == PS_ECC) {
int32_t status;
#ifdef USE_ECC
rc = psEccDsaVerify(pkiPool, &CA->publicKey.key.ecc,
CRL->sigHash, CRL->sigHashLen, CRL->sig, CRL->sigLen, &status,
NULL);
#else
rc = PS_DISABLED_FEATURE_FAIL;
#endif /* USE_ECC */
if (status != 1) {
psTraceCrypto("Unable to verify ECDSA CRL signature\n");
return PS_CERT_AUTH_FAIL_SIG;
}
}
CRL->authenticated = PS_TRUE;
return PS_SUCCESS;
}
/*
Parse a CRL.
*/
int32 psX509ParseCRL(psPool_t *pool, psX509Crl_t **crl, unsigned char *crlBin,
int32 crlBinLen)
{
const unsigned char *end, *start, *sigStart, *sigEnd, *revStart, *p = crlBin;
int32 oi, version, rc;
x509revoked_t *curr, *next;
psDigestContext_t hashCtx;
psX509Crl_t *lcrl;
uint32_t glen, ilen, tbsCertLen;
uint16_t timelen, plen;
if (crlBin == NULL || crlBinLen <= 0) {
return PS_ARG_FAIL;
}
end = p + crlBinLen;
/*
CertificateList ::= SEQUENCE {
tbsCertList TBSCertList,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
TBSCertList ::= SEQUENCE {
version Version OPTIONAL,
-- if present, shall be v2
signature AlgorithmIdentifier,
issuer Name,
thisUpdate Time,
nextUpdate Time OPTIONAL,
revokedCertificates SEQUENCE OF SEQUENCE {
userCertificate CertificateSerialNumber,
revocationDate Time,
crlEntryExtensions Extensions OPTIONAL
-- if present, shall be v2
} OPTIONAL,
crlExtensions [0] EXPLICIT Extensions OPTIONAL
-- if present, shall be v2
}
*/
if (getAsnSequence32(&p, (uint32)(end - p), &glen, 0) < 0) {
psTraceCrypto("Initial parse error in psX509ParseCRL\n");
return PS_PARSE_FAIL;
}
/* Track tbsCert for signature purposes and for encoding where there
is no revokedCertificate entry */
sigStart = p;
if (getAsnSequence32(&p, (uint32)(end - p), &tbsCertLen, 0) < 0) {
psTraceCrypto("Initial parse error in psX509ParseCRL\n");
return PS_PARSE_FAIL;
}
start = p;
if (*p == ASN_INTEGER) {
version = 0;
if (getAsnInteger(&p, (uint32)(end - p), &version) < 0 || version != 1){
psTraceIntCrypto("Version parse error in psX509ParseCRL %d\n",
version);
return PS_PARSE_FAIL;
}
}
/* looking correct. Allocate the psX509Crl_t */
if ((lcrl = psMalloc(pool, sizeof(psX509Crl_t))) == NULL) {
return PS_MEM_FAIL;
}
memset(lcrl, 0, sizeof(psX509Crl_t));
lcrl->pool = pool;
/* signature */
if (getAsnAlgorithmIdentifier(&p, (int32)(end - p), &lcrl->sigAlg, &plen)
< 0) {
psTraceCrypto("Couldn't parse crl sig algorithm identifier\n");
psX509FreeCRL(lcrl);
return PS_PARSE_FAIL;
}
/*
Name ::= CHOICE { -- only one possibility for now --
rdnSequence RDNSequence }
RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
DistinguishedName ::= RDNSequence
RelativeDistinguishedName ::=
SET SIZE (1 .. MAX) OF AttributeTypeAndValue
*/
if ((rc = psX509GetDNAttributes(pool, &p, (uint32)(end - p),
&lcrl->issuer, 0)) < 0) {
psX509FreeCRL(lcrl);
psTraceCrypto("Couldn't parse crl issuer DN attributes\n");
return rc;
}
/* thisUpdate TIME */
if ((end - p) < 1 || ((*p != ASN_UTCTIME) && (*p != ASN_GENERALIZEDTIME))) {
psTraceCrypto("Malformed thisUpdate CRL\n");
psX509FreeCRL(lcrl);
return PS_PARSE_FAIL;
}
p++;
if (getAsnLength(&p, (uint32)(end - p), &timelen) < 0 ||
(uint32)(end - p) < timelen) {
psTraceCrypto("Malformed thisUpdate CRL\n");
psX509FreeCRL(lcrl);
return PS_PARSE_FAIL;
}
p += timelen; /* Skip it. Only concerned with expiration */
/* nextUpdateTIME - Optional... but required by spec */
if ((end - p) < 1 || ((*p == ASN_UTCTIME) || (*p == ASN_GENERALIZEDTIME))) {
lcrl->nextUpdateType = *p;
p++;
if (getAsnLength(&p, (uint32)(end - p), &timelen) < 0 ||
(uint32)(end - p) < timelen) {
psTraceCrypto("Malformed nextUpdateTIME CRL\n");
psX509FreeCRL(lcrl);
return PS_PARSE_FAIL;
}
if ((lcrl->nextUpdate = psMalloc(pool, timelen + 1)) == NULL) {
psX509FreeCRL(lcrl);
return PS_PARSE_FAIL;
}
memcpy(lcrl->nextUpdate, p, timelen);
lcrl->nextUpdate[timelen] = '\0';
p += timelen;
}
/* Need to see if any data left in tbsCertList. Could be no revocations */
if ((p - start) != tbsCertLen) {
/*
revokedCertificates SEQUENCE OF SEQUENCE {
userCertificate CertificateSerialNumber,
revocationDate Time,
crlEntryExtensions Extensions OPTIONAL
-- if present, shall be v2
} OPTIONAL,
*/
/* Need to peek at next byte to make sure there are some revoked
certs here. Could be jumping right to crlExtensions */
if (*p != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 0)) {
if (getAsnSequence32(&p, (uint32)(end - p), &glen, 0) < 0) {
psTraceCrypto("Initial revokedCert error in psX509ParseCRL\n");
psX509FreeCRL(lcrl);
return PS_PARSE_FAIL;
}
lcrl->revoked = curr = psMalloc(pool, sizeof(x509revoked_t));
if (curr == NULL) {
psX509FreeCRL(lcrl);
return PS_MEM_FAIL;
}
memset(curr, 0x0, sizeof(x509revoked_t));
while (glen > 0) {
revStart = p;
if (getAsnSequence32(&p, (uint32)(end - p), &ilen, 0) < 0) {
psTraceCrypto("Deep revokedCert error in psX509ParseCRL\n");
psX509FreeCRL(lcrl);
return PS_PARSE_FAIL;
}
start = p; /* reusing start */
if ((rc = getSerialNum(pool, &p, ilen, &curr->serial,
&curr->serialLen)) < 0) {
psTraceCrypto("ASN serial number parse error\n");
psX509FreeCRL(lcrl);
return rc;
}
/* skipping time and crlEntryExtensions */
p += ilen - (uint32)(p - start);
if (glen < (uint32)(p - revStart)) {
psTraceCrypto("Deeper revokedCert err in psX509ParseCRL\n");
psX509FreeCRL(lcrl);
return PS_PARSE_FAIL;
}
glen -= (uint32)(p - revStart);
// psTraceBytes("revoked", curr->serial, curr->serialLen);
if (glen > 0) {
if ((next = psMalloc(pool, sizeof(x509revoked_t))) == NULL){
psX509FreeCRL(lcrl);
return PS_MEM_FAIL;
}
memset(next, 0x0, sizeof(x509revoked_t));
curr->next = next;
curr = next;
}
}
}
/* Always treated as OPTIONAL */
if (getExplicitExtensions(pool, &p, (uint32)(end - p), 0,
&lcrl->extensions, 0) < 0) {
psTraceCrypto("Extension parse error in psX509ParseCRL\n");
psX509FreeCRL(lcrl);
return PS_PARSE_FAIL;
}
//if (lcrl->extensions.ak.keyId) {
// psTraceBytes("CRL ak", lcrl->extensions.ak.keyId, 20);
//}
} /* End tbsCertList */
sigEnd = p;
if (getAsnAlgorithmIdentifier(&p, (int32)(end - p), &oi, &plen) < 0) {
psX509FreeCRL(lcrl);
psTraceCrypto("Couldn't parse crl sig algorithm identifier\n");
return PS_PARSE_FAIL;
}
/* must match */
if (oi != lcrl->sigAlg) {
psTraceCrypto("Couldn't match crl sig algorithm identifier\n");
psX509FreeCRL(lcrl);
return PS_PARSE_FAIL;
}
if ((rc = psX509GetSignature(pool, &p, (uint32)(end - p), &lcrl->sig,
&lcrl->sigLen)) < 0) {
psX509FreeCRL(lcrl);
psTraceCrypto("Couldn't parse signature\n");
return rc;
}
/* Perform the hashing for later auth */
switch (lcrl->sigAlg) {
#ifdef ENABLE_MD5_SIGNED_CERTS
case OID_MD5_RSA_SIG:
lcrl->sigHashLen = MD5_HASH_SIZE;
break;
#endif
#ifdef ENABLE_SHA1_SIGNED_CERTS
case OID_SHA1_RSA_SIG:
lcrl->sigHashLen = SHA1_HASH_SIZE;
break;
#ifdef USE_ECC
case OID_SHA1_ECDSA_SIG:
lcrl->sigHashLen = SHA1_HASH_SIZE;
break;
#endif /* USE_ECC */
#endif /* ENABLE_SHA1_SIGNED_CERTS */
#ifdef USE_SHA256
case OID_SHA256_RSA_SIG:
lcrl->sigHashLen = SHA256_HASH_SIZE;
break;
#ifdef USE_ECC
case OID_SHA256_ECDSA_SIG:
lcrl->sigHashLen = SHA256_HASH_SIZE;
break;
#endif /* USE_ECC */
#endif /* USE_SHA256 */
#ifdef USE_SHA384
case OID_SHA384_RSA_SIG:
lcrl->sigHashLen = SHA384_HASH_SIZE;
break;
#ifdef USE_ECC
case OID_SHA384_ECDSA_SIG:
lcrl->sigHashLen = SHA384_HASH_SIZE;
break;
#endif /* USE_ECC */
#endif /* USE_SHA384 */
#ifdef USE_SHA512
case OID_SHA512_RSA_SIG:
lcrl->sigHashLen = SHA512_HASH_SIZE;
break;
#ifdef USE_ECC
case OID_SHA512_ECDSA_SIG:
lcrl->sigHashLen = SHA512_HASH_SIZE;
break;
#endif /* USE_ECC */
#endif /* USE_SHA512 */
default:
psX509FreeCRL(lcrl);
psTraceCrypto("Need more signature alg support for CRL\n");
return PS_UNSUPPORTED_FAIL;
}
switch(lcrl->sigHashLen) {
#ifdef ENABLE_MD5_SIGNED_CERTS
case MD5_HASH_SIZE:
psMd5Init(&hashCtx.md5);
psMd5Update(&hashCtx.md5, sigStart, (uint32)(sigEnd - sigStart));
psMd5Final(&hashCtx.md5, lcrl->sigHash);
break;
#endif /* ENABLE_MD5_SIGNED_CERTS */
#ifdef ENABLE_SHA1_SIGNED_CERTS
case SHA1_HASH_SIZE:
psSha1PreInit(&hashCtx.sha1);
psSha1Init(&hashCtx.sha1);
psSha1Update(&hashCtx.sha1, sigStart, (uint32)(sigEnd - sigStart));
psSha1Final(&hashCtx.sha1, lcrl->sigHash);
break;
#endif /* ENABLE_SHA1_SIGNED_CERTS */
#ifdef USE_SHA256
case SHA256_HASH_SIZE:
psSha256PreInit(&hashCtx.sha256);
psSha256Init(&hashCtx.sha256);
psSha256Update(&hashCtx.sha256, sigStart, (uint32)(sigEnd - sigStart));
psSha256Final(&hashCtx.sha256, lcrl->sigHash);
break;
#endif /* USE_SHA256 */
#ifdef USE_SHA384
case SHA384_HASH_SIZE:
psSha384PreInit(&hashCtx.sha384);
psSha384Init(&hashCtx.sha384);
psSha384Update(&hashCtx.sha384, sigStart, (uint32)(sigEnd - sigStart));
psSha384Final(&hashCtx.sha384, lcrl->sigHash);
break;
#endif /* USE_SHA384 */
#ifdef USE_SHA512
case SHA512_HASH_SIZE:
psSha512PreInit(&hashCtx.sha512);
psSha512Init(&hashCtx.sha512);
psSha512Update(&hashCtx.sha512, sigStart, (uint32)(sigEnd - sigStart));
psSha512Final(&hashCtx.sha512, lcrl->sigHash);
break;
#endif /* USE_SHA512 */
default:
psX509FreeCRL(lcrl);
psTraceCrypto("Unsupported digest algorithm in CRL\n");
return PS_UNSUPPORTED_FAIL;
}
*crl = lcrl;
return PS_SUCCESS;
}
/*
If the provided cert has a URL based CRL Distribution point, return
that. The url and urlLen point directly into the cert structure so
must not be modified.
*/
int32 psX509GetCRLdistURL(psX509Cert_t *cert, char **url, uint32_t *urlLen)
{
x509GeneralName_t *gn;
if (cert == NULL) {
return PS_ARG_FAIL;
}
*url = NULL;
*urlLen = 0;
if (cert->extensions.crlDist != NULL) {
gn = cert->extensions.crlDist;
while (gn) {
if (gn->id == 6) { /* Only pass on URI types */
*url = (char*)gn->data;
*urlLen = gn->dataLen;
return PS_TRUE;
} else {
psTraceIntCrypto("Unsupported CRL distro point format %d\n",
gn->id);
}
gn = gn->next;
}
}
return PS_FALSE;
}
/******************************************************************************/
#endif /* USE_CERT_PARSE */
#endif /* USE_CRL */