📄 certrev.c
字号:
/****************************************************************************
* *
* Certificate Revocation Routines *
* Copyright Peter Gutmann 1996-2007 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "cert.h"
#include "asn1.h"
#include "asn1_ext.h"
#else
#include "cert/cert.h"
#include "misc/asn1.h"
#include "misc/asn1_ext.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 certificate 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 */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int findRevocationEntry( const REVOCATION_INFO *listPtr,
OUT_OPT_PTR REVOCATION_INFO **insertPoint,
IN_BUFFER( valueLength ) const void *value,
IN_LENGTH_SHORT const int valueLength,
const BOOLEAN sortEntries )
{
const REVOCATION_INFO *prevElement = NULL;
const int idCheck = checksumData( value, valueLength );
int iterationCount;
assert( isReadPtr( listPtr, sizeof( REVOCATION_INFO ) ) );
assert( insertPoint == NULL || \
isWritePtr( insertPoint, sizeof( REVOCATION_INFO * ) ) );
assert( isReadPtr( value, valueLength ) );
REQUIRES( valueLength > 0 && valueLength < MAX_INTLENGTH_SHORT );
/* 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). In addition we bound
the loop with FAILSAFE_ITERATIONS_MAX since CRLs can grow enormous */
for( iterationCount = 0;
listPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
listPtr = listPtr->next, iterationCount++ )
{
if( ( sortEntries || idCheck == listPtr->idCheck ) && \
listPtr->idLength == valueLength )
{
const int compareStatus = memcmp( listPtr->id,
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->idLength > valueLength )
break; /* Insert before this point */
}
prevElement = listPtr;
}
ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
/* 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 certificate has been revoked */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int checkRevocation( const CERT_INFO *certInfoPtr,
INOUT CERT_INFO *revocationInfoPtr )
{
CERT_REV_INFO *certRevInfo = revocationInfoPtr->cCertRev;
REVOCATION_INFO *revocationEntry = DUMMY_INIT_PTR;
int status;
assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
assert( isWritePtr( revocationInfoPtr, sizeof( CERT_INFO ) ) );
/* If there's no revocation information present then the certificate
can't have been revoked */
if( certRevInfo->revocations == NULL )
return( CRYPT_OK );
/* Check whether the certificate is present in the revocation list */
if( revocationInfoPtr->type == CRYPT_CERTTYPE_CRL )
{
/* If the issuers differ then the certificate 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 certificate in the list */
status = findRevocationEntry( certRevInfo->revocations,
&revocationEntry,
certInfoPtr->cCertCert->serialNumber,
certInfoPtr->cCertCert->serialNumberLength,
FALSE );
if( cryptStatusError( status ) )
{
/* No CRL entry, the certificate is OK */
return( CRYPT_OK );
}
}
else
{
BYTE certHash[ CRYPT_MAX_HASHSIZE + 8 ];
int certHashLength;
ENSURES( revocationInfoPtr->type == CRYPT_CERTTYPE_OCSP_RESPONSE );
/* Get the certificate hash and use it to check whether there's an
entry for this certificate in the list. We read the certificate
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, CRYPT_MAX_HASHSIZE,
&certHashLength );
if( cryptStatusOK( status ) )
{
status = findRevocationEntry( certRevInfo->revocations,
&revocationEntry, certHash,
certHashLength, FALSE );
}
if( cryptStatusError( status ) )
{
/* No entry, either good or bad, we can't report anything about
the certificate */
return( status );
}
}
ENSURES( revocationEntry != NULL );
/* Select the entry that contains the revocation information and return
the certificate's status. For CRLs the presence of an entry means
that the certificate is invalid, for OCSP the validity information is
contained in the entry. The unknown status is a bit difficult to
report, the best that we can do is report notfound although the
notfound occurred at the responder rather than here */
certRevInfo->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 */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
int addRevocationEntry( INOUT_PTR REVOCATION_INFO **listHeadPtrPtr,
OUT_PTR REVOCATION_INFO **newEntryPosition,
IN_KEYID const CRYPT_KEYID_TYPE valueType,
IN_BUFFER( valueLength ) const void *value,
IN_LENGTH_SHORT const int valueLength,
const BOOLEAN noCheck )
{
REVOCATION_INFO *newElement, *insertPoint;
assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
assert( isWritePtr( newEntryPosition, sizeof( REVOCATION_INFO * ) ) );
assert( isReadPtr( value, valueLength ) );
REQUIRES( valueType == CRYPT_KEYID_NONE || \
valueType == CRYPT_IKEYID_CERTID || \
valueType == CRYPT_IKEYID_ISSUERID || \
valueType == CRYPT_IKEYID_ISSUERANDSERIALNUMBER );
REQUIRES( valueLength > 0 && valueLength < MAX_INTLENGTH_SHORT );
/* Clear return value */
*newEntryPosition = NULL;
/* 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 && *listHeadPtrPtr != NULL && \
cryptStatusOK( \
findRevocationEntry( *listHeadPtrPtr, &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 );
}
else
{
/* It's an empty list, insert the new element at the start */
insertPoint = NULL;
}
/* 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 > MAX_ID_SIZE )
{
if( ( newElement->idPtr = clDynAlloc( "addRevocationEntry",
valueLength ) ) == NULL )
{
clFree( "addRevocationEntry", newElement );
return( CRYPT_ERROR_MEMORY );
}
}
else
newElement->idPtr = newElement->id;
newElement->idType = valueType;
memcpy( newElement->idPtr, value, valueLength );
newElement->idLength = valueLength;
newElement->idCheck = 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 = *listHeadPtrPtr;
*listHeadPtrPtr = newElement;
}
else
insertSingleListElement( listHeadPtrPtr, insertPoint, newElement );
*newEntryPosition = newElement;
return( CRYPT_OK );
}
/* Delete a revocation list */
STDC_NONNULL_ARG( ( 1 ) ) \
void deleteRevocationEntries( INOUT_PTR REVOCATION_INFO **listHeadPtrPtr )
{
REVOCATION_INFO *entryListPtr = *listHeadPtrPtr;
int iterationCount;
assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
*listHeadPtrPtr = NULL;
/* Destroy any remaining list items */
for( iterationCount = 0;
entryListPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
iterationCount++ )
{
REVOCATION_INFO *itemToFree = entryListPtr;
entryListPtr = entryListPtr->next;
if( itemToFree->idPtr != itemToFree->id )
{
zeroise( itemToFree->idPtr, itemToFree->idLength );
clFree( "deleteRevocationEntries", itemToFree->idPtr );
}
if( itemToFree->attributes != NULL )
deleteAttributes( &itemToFree->attributes );
zeroise( itemToFree, sizeof( REVOCATION_INFO ) );
clFree( "deleteRevocationEntries", itemToFree );
}
}
/* Copy a revocation list */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int copyRevocationEntries( INOUT_PTR REVOCATION_INFO **destListHeadPtrPtr,
const REVOCATION_INFO *srcListPtr )
{
const REVOCATION_INFO *srcListCursor;
REVOCATION_INFO *destListCursor = DUMMY_INIT_PTR;
int iterationCount;
assert( isWritePtr( destListHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
assert( *destListHeadPtrPtr == NULL ); /* Dest.should be empty */
assert( isReadPtr( srcListPtr, sizeof( REVOCATION_INFO ) ) );
/* Sanity check to make sure that the destination list doesn't already
exist, which would cause the copy loop below to fail */
REQUIRES( *destListHeadPtrPtr == NULL );
/* Copy all revocation entries from source to destination */
for( srcListCursor = srcListPtr, iterationCount = 0;
srcListCursor != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
srcListCursor = srcListCursor->next, iterationCount++ )
{
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->idLength > MAX_ID_SIZE )
{
/* If the ID information doesn't fit into the fixed buffer,
allocate a variable-length one and copy it across */
if( ( newElement->idPtr = \
clDynAlloc( "copyRevocationEntries",
srcListCursor->idLength ) ) == NULL )
{
clFree( "copyRevocationEntries", newElement );
return( CRYPT_ERROR_MEMORY );
}
memcpy( newElement->idPtr, srcListCursor->id,
srcListCursor->idLength );
}
else
newElement->idPtr = newElement->id;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -