📄 certrev.c
字号:
/****************************************************************************
* *
* Certificate Revocation Routines *
* Copyright Peter Gutmann 1996-2003 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL ) || defined( INC_CHILD )
#include "cert.h"
#include "../misc/asn1_rw.h"
#include "../misc/asn1s_rw.h"
#else
#include "cert/cert.h"
#include "misc/asn1_rw.h"
#include "misc/asn1s_rw.h"
#endif /* Compiler-specific includes */
/* The maximum length of ID that can be stored in a REVOCATION_INFO entry.
Larger IDs require external storage */
#define MAX_ID_SIZE 128
/* Usually when we add revocation information we perform various checks such
as making sure we're not adding duplicate information, however when
processing the mega-CRLs from some CAs this becomes prohibitively
expensive. To solve this problem, we perform checking up to a certain
number of entries and after that just drop in any further entries as is
in order to provide same-day service. The following value defines the
CRL threshold size in bytes at which we stop performing checks when we
add new entries */
#define CRL_SORT_LIMIT 8192
/* Context-specific tags for OCSP certificate identifier types */
enum { CTAG_OI_CERTIFICATE, CTAG_OI_CERTIDWITHSIG, CTAG_OI_RTCS };
/* OCSP cert status values */
enum { OCSP_STATUS_NOTREVOKED, OCSP_STATUS_REVOKED, OCSP_STATUS_UNKNOWN };
/****************************************************************************
* *
* Add/Delete/Check Revocation Information *
* *
****************************************************************************/
/* Find an entry in a revocation list. This is done using a linear search,
which isn't very optimal but anyone trying to do anything useful with
mega-CRLs (or with CRLs in general) is in more trouble than basic search
algorithm choice. In other words it doesn't really make much difference
whether we have an optimal or suboptimal implementation of a
fundamentally broken mechanism like CRLs.
The value is either a serialNumber or a hash of some form (issuerID,
certHash), we don't bother distinguishing the exact type since the
chances of a hash collision are virtually nonexistant */
static int findRevocationEntry( const REVOCATION_INFO *listPtr,
REVOCATION_INFO **insertPoint,
const void *value, const int valueLength,
const BOOLEAN sortEntries )
{
const REVOCATION_INFO *prevElement = NULL;
const int dCheck = checksumData( value, valueLength );
assert( isReadPtr( listPtr, REVOCATION_INFO ) );
assert( insertPoint == NULL || \
isWritePtr( insertPoint, REVOCATION_INFO * ) );
/* Clear the return value */
if( insertPoint != NULL )
*insertPoint = NULL;
/* Find the correct place in the list to insert the new element and check
for duplicates. If requested we sort the entries by serial number
(or, more generally, data value) for no adequately explored reason
(some implementations can optimise the searching of CRLs based on
this, but since there's no agreement on whether to do it or not you
can't tell whether it's safe to rely on it) */
while( listPtr != NULL )
{
if( ( sortEntries || dCheck == listPtr->dCheck ) && \
listPtr->dataLength == valueLength )
{
const int compareStatus = memcmp( listPtr->data,
value, valueLength );
if( !compareStatus )
{
/* We found a matching entry, tell the caller which one it
is if required */
if( insertPoint != NULL )
*insertPoint = ( REVOCATION_INFO * ) listPtr;
return( CRYPT_OK );
}
if( sortEntries && compareStatus > 0 )
break; /* Insert before this point */
}
else
if( sortEntries && listPtr->dataLength > valueLength )
break; /* Insert before this point */
prevElement = listPtr;
listPtr = listPtr->next;
}
/* We can't find a matching entry, return the revocation entry after
which we should insert the new value */
if( insertPoint != NULL )
*insertPoint = ( REVOCATION_INFO * ) prevElement;
return( CRYPT_ERROR_NOTFOUND );
}
/* Check whether a cert has been revoked */
int checkRevocation( const CERT_INFO *certInfoPtr,
CERT_INFO *revocationInfoPtr )
{
REVOCATION_INFO *revocationEntry;
int status;
assert( isReadPtr( certInfoPtr, CERT_INFO ) );
assert( isWritePtr( revocationInfoPtr, CERT_INFO ) );
/* If there's no revocation information present, the cert can't have been
revoked */
if( revocationInfoPtr->revocations == NULL )
return( CRYPT_OK );
/* Check whether the cert is present in the revocation list */
if( revocationInfoPtr->type == CRYPT_CERTTYPE_CRL )
{
/* If the issuers differ, the cert can't be in this CRL */
if( ( revocationInfoPtr->issuerDNsize != certInfoPtr->issuerDNsize || \
memcmp( revocationInfoPtr->issuerDNptr, certInfoPtr->issuerDNptr,
revocationInfoPtr->issuerDNsize ) ) )
return( CRYPT_OK );
/* Check whether there's an entry for this cert in the list */
status = findRevocationEntry( revocationInfoPtr->revocations,
&revocationEntry,
certInfoPtr->serialNumber,
certInfoPtr->serialNumberLength,
FALSE );
if( status == CRYPT_ERROR_NOTFOUND )
/* No CRL entry, the certificate is OK */
return( CRYPT_OK );
}
else
{
BYTE certHash[ CRYPT_MAX_HASHSIZE ];
int certHashLength;
assert( revocationInfoPtr->type == CRYPT_CERTTYPE_OCSP_RESPONSE );
/* Get the cert hash and use it to check whether there's an entry
for this cert in the list. We read the cert hash indirectly
since it's computed on demand and may not have been evaluated
yet */
status = getCertComponent( ( CERT_INFO * ) certInfoPtr,
CRYPT_CERTINFO_FINGERPRINT_SHA,
certHash, &certHashLength );
if( cryptStatusOK( status ) )
status = findRevocationEntry( revocationInfoPtr->revocations,
&revocationEntry, certHash,
certHashLength, FALSE );
if( cryptStatusError( status ) )
/* No entry, either good or bad, we can't report anything about
the cert */
return( status );
}
/* Select the entry that contains the revocation information and return
the cert's status. For CRLs the presence of an entry means that the
cert is invalid, for OCSP the validity information is contained in
the entry. The unknown status is a bit difficult to report, the best
we can do is report notfound, although the notfound occurred at the
responder rather than here */
revocationInfoPtr->currentRevocation = revocationEntry;
if( revocationInfoPtr->type == CRYPT_CERTTYPE_CRL )
return( CRYPT_ERROR_INVALID );
return( ( revocationEntry->status == CRYPT_OCSPSTATUS_NOTREVOKED ) ? \
CRYPT_OK : \
( revocationEntry->status == CRYPT_OCSPSTATUS_REVOKED ) ? \
CRYPT_ERROR_INVALID : CRYPT_ERROR_NOTFOUND );
}
/* Add an entry to a revocation list */
int addRevocationEntry( REVOCATION_INFO **listHeadPtr,
REVOCATION_INFO **newEntryPosition,
const CRYPT_KEYID_TYPE valueType,
const void *value, const int valueLength,
const BOOLEAN noCheck )
{
REVOCATION_INFO *newElement, *insertPoint;
assert( isWritePtr( listHeadPtr, REVOCATION_INFO * ) );
assert( isWritePtr( newEntryPosition, REVOCATION_INFO * ) );
assert( sizeof( newElement->data ) == MAX_ID_SIZE );
assert( valueType == CRYPT_KEYID_NONE || \
valueType == CRYPT_IKEYID_CERTID || \
valueType == CRYPT_IKEYID_ISSUERID || \
valueType == CRYPT_IKEYID_ISSUERANDSERIALNUMBER );
assert( isReadPtr( value, valueLength ) );
/* Find the insertion point for the new entry, unless we're reading data
from a pre-encoded CRL, in which case we just drop it in at the start.
The absence of checking for data from an existing CRL is necessary in
order to provide same-day service for large CRLs */
if( !noCheck && *listHeadPtr != NULL && \
cryptStatusOK( \
findRevocationEntry( *listHeadPtr, &insertPoint, value,
valueLength, TRUE ) ) )
/* If we get an OK status it means that we've found an existing
entry that matches the one being added, we can't add it again */
return( CRYPT_ERROR_DUPLICATE );
/* Allocate memory for the new element and copy the information across */
if( ( newElement = ( REVOCATION_INFO * ) \
clAlloc( "addRevocationEntry", sizeof( REVOCATION_INFO ) ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
memset( newElement, 0, sizeof( REVOCATION_INFO ) );
if( valueLength > 128 )
{
if( ( newElement->dataPtr = clDynAlloc( "addRevocationEntry",
valueLength ) ) == NULL )
{
clFree( "addRevocationEntry", newElement );
return( CRYPT_ERROR_MEMORY );
}
}
else
newElement->dataPtr = newElement->data;
newElement->type = valueType;
memcpy( newElement->dataPtr, value, valueLength );
newElement->dataLength = valueLength;
newElement->dCheck = checksumData( value, valueLength );
/* Insert the new element into the list */
if( noCheck )
{
/* If we're adding data from an existing CRL, drop it in at the
quickest insert point. This is necessary for quick operation
when handling mega-CRLs */
newElement->next = *listHeadPtr;
*listHeadPtr = newElement;
}
else
insertSingleListElement( listHeadPtr, insertPoint, newElement );
*newEntryPosition = newElement;
return( CRYPT_OK );
}
/* Delete a revocation list */
void deleteRevocationEntries( REVOCATION_INFO **listHeadPtr )
{
REVOCATION_INFO *entryListPtr = *listHeadPtr;
assert( isWritePtr( listHeadPtr, REVOCATION_INFO * ) );
*listHeadPtr = NULL;
/* Destroy any remaining list items */
while( entryListPtr != NULL )
{
REVOCATION_INFO *itemToFree = entryListPtr;
entryListPtr = entryListPtr->next;
if( itemToFree->dataPtr != itemToFree->data )
{
zeroise( itemToFree->dataPtr, itemToFree->dataLength );
clFree( "deleteRevocationEntries", itemToFree->dataPtr );
}
if( itemToFree->attributes != NULL )
deleteAttributes( &itemToFree->attributes );
zeroise( itemToFree, sizeof( REVOCATION_INFO ) );
clFree( "deleteRevocationEntries", itemToFree );
}
}
/* Copy a revocation list */
int copyRevocationEntries( REVOCATION_INFO **destListHeadPtr,
const REVOCATION_INFO *srcListPtr,
CRYPT_ATTRIBUTE_TYPE *errorLocus,
CRYPT_ERRTYPE_TYPE *errorType )
{
const REVOCATION_INFO *srcListCursor;
REVOCATION_INFO *destListCursor;
assert( isWritePtr( destListHeadPtr, REVOCATION_INFO * ) );
assert( *destListHeadPtr == NULL ); /* Dest.should be empty */
/* Copy all revocation entries from source to destination */
for( srcListCursor = srcListPtr; srcListCursor != NULL;
srcListCursor = srcListCursor->next )
{
REVOCATION_INFO *newElement;
/* Allocate the new entry and copy the data from the existing one
across. We don't copy the attributes because there aren't any
that should be carried from request to response */
if( ( newElement = ( REVOCATION_INFO * ) \
clAlloc( "copyRevocationEntries",
sizeof( REVOCATION_INFO ) ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
memcpy( newElement, srcListCursor, sizeof( REVOCATION_INFO ) );
if( srcListCursor->dataLength > 128 )
{
/* If the ID information doesn't fit into the fixed buffer,
allocate a variable-length one and copy it across */
if( ( newElement->dataPtr = \
clDynAlloc( "copyRevocationEntries",
srcListCursor->dataLength ) ) == NULL )
{
clFree( "copyRevocationEntries", newElement );
return( CRYPT_ERROR_MEMORY );
}
memcpy( newElement->dataPtr, srcListCursor->data,
srcListCursor->dataLength );
}
else
newElement->dataPtr = newElement->data;
newElement->attributes = NULL;
newElement->next = NULL;
/* Set the status to 'unknown' by default, this means that any
entries that we can't do anything with automatically get the
correct status associated with them */
newElement->status = CRYPT_OCSPSTATUS_UNKNOWN;
/* Link the new element into the list */
if( *destListHeadPtr == NULL )
*destListHeadPtr = destListCursor = newElement;
else
{
destListCursor->next = newElement;
destListCursor = newElement;
}
}
return( CRYPT_OK );
}
/****************************************************************************
* *
* Read/write CRL Information *
* *
****************************************************************************/
/* Read/write CRL entries:
RevokedCert ::= SEQUENCE {
userCertificate CertificalSerialNumber,
revocationDate UTCTime
extensions Extensions OPTIONAL,
} */
int sizeofCRLentry( REVOCATION_INFO *crlEntry )
{
assert( isWritePtr( crlEntry, REVOCATION_INFO ) );
/* Remember the encoded attribute size for later when we write the
attributes */
crlEntry->attributeSize = sizeofAttributes( crlEntry->attributes );
return( ( int ) sizeofObject( \
sizeofInteger( crlEntry->data, crlEntry->dataLength ) + \
sizeofUTCTime() + \
( ( crlEntry->attributeSize > 0 ) ? \
( int ) sizeofObject( crlEntry->attributeSize ) : 0 ) ) );
}
int readCRLentry( STREAM *stream, REVOCATION_INFO **listHeadPtr,
CRYPT_ATTRIBUTE_TYPE *errorLocus,
CRYPT_ERRTYPE_TYPE *errorType )
{
REVOCATION_INFO *currentEntry;
BYTE serialNumber[ MAX_SERIALNO_SIZE ];
int serialNumberLength, endPos, length, status;
time_t revocationTime;
assert( isWritePtr( listHeadPtr, REVOCATION_INFO * ) );
/* Determine the overall size of the entry */
readSequence( stream, &length );
endPos = stell( stream ) + length;
/* Read the integer component of the serial number (limited to a sane
length) and the revocation time */
readInteger( stream, serialNumber, &serialNumberLength,
MAX_SERIALNO_SIZE );
status = readUTCTime( stream, &revocationTime );
if( cryptStatusError( status ) )
return( status );
/* Add the entry to the revocation information list. The ID type isn't
quite an issueAndSerialNumber, but the checking code eventually
converts it into this form using the supplied issuer cert DN */
status = addRevocationEntry( listHeadPtr, ¤tEntry,
CRYPT_IKEYID_ISSUERANDSERIALNUMBER,
serialNumber, serialNumberLength,
( endPos > CRL_SORT_LIMIT ) ? TRUE : FALSE );
if( cryptStatusError( status ) )
return( status );
currentEntry->revocationTime = revocationTime;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -