📄 ext_rd.c
字号:
/****************************************************************************
* *
* Certificate Attribute Read Routines *
* Copyright Peter Gutmann 1996-2005 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "cert.h"
#include "certattr.h"
#include "asn1.h"
#include "asn1_ext.h"
#elif defined( INC_CHILD )
#include "cert.h"
#include "certattr.h"
#include "../misc/asn1.h"
#include "../misc/asn1_ext.h"
#else
#include "cert/cert.h"
#include "cert/certattr.h"
#include "misc/asn1.h"
#include "misc/asn1_ext.h"
#endif /* Compiler-specific includes */
/* Define the following to print a trace of the cert fields being parsed,
useful for debugging broken certs */
#if !defined( NDEBUG ) && 0
#define TRACE_FIELDTYPE( attributeInfoPtr, stackPos ) \
{ \
int i; \
\
for( i = 0; i < stackPos; i++ ) \
printf( " " ); \
if( ( attributeInfoPtr ) != NULL && \
( attributeInfoPtr )->description != NULL ) \
puts( ( attributeInfoPtr )->description ); \
}
#else
#define TRACE_FIELDTYPE( attributeInfoPtr, stackPos )
#endif /* NDEBUG */
/* Prototypes for functions in certcomp.c */
int oidToText( const BYTE *binaryOID, char *oid );
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Get the tag for a field from the attribute field definition */
static int getFieldTag( STREAM *stream,
const ATTRIBUTE_INFO *attributeInfoPtr )
{
int tag;
assert( isReadPtr( attributeInfoPtr, sizeof( ATTRIBUTE_INFO ) ) );
/* If it's a tagged field, the actual tag is stored as the encoded-type
value */
if( attributeInfoPtr->fieldEncodedType > 0 )
{
tag = attributeInfoPtr->fieldEncodedType;
/* If it's an implictly tagged SET/SEQUENCE then it's constructed */
if( ( attributeInfoPtr->fieldType == BER_SEQUENCE ||
attributeInfoPtr->fieldType == BER_SET ||
attributeInfoPtr->fieldType == FIELDTYPE_DN ||
( attributeInfoPtr->flags & FL_EXPLICIT ) ) )
tag |= BER_CONSTRUCTED;
assert( tag > 0 && tag < 0xF0 );
return( tag );
}
/* It's a non-tagged field, the tag is the same as the field type */
tag = attributeInfoPtr->fieldType;
if( tag == FIELDTYPE_DISPLAYSTRING )
{
/* This is a variable-tag field that can have one of a number of
tags. To handle this we peek ahead into the stream to see if an
acceptable tag is present, and if not, set the value to a non-
matching tag value */
tag = peekTag( stream );
if( ( tag != BER_STRING_IA5 ) && \
( tag != BER_STRING_ISO646 ) && \
( tag != BER_STRING_BMP ) && ( tag != BER_STRING_UTF8 ) )
tag++; /* Make sure that it doesn't match */
}
assert( tag > 0 && tag < 0xF0 );
return( tag );
}
/* Find the end of an item (either primitive or constructed) in the attribute
table. Sometimes we may have already entered a constructed object (for
example when an attribute has a version number so we don't know until we've
started processing it that we can't do anything with it), if this is the
case the depth parameter indicates how many nesting levels we have to
undo */
static int findItemEnd( const ATTRIBUTE_INFO **attributeInfoPtrPtr,
const int depth )
{
const ATTRIBUTE_INFO *attributeInfoPtr = *attributeInfoPtrPtr;
BOOLEAN attributeContinues;
int currentDepth = depth, iterationCount = 0;
assert( isReadPtr( attributeInfoPtrPtr, sizeof( ATTRIBUTE_INFO * ) ) );
assert( isReadPtr( *attributeInfoPtrPtr, sizeof( ATTRIBUTE_INFO ) ) );
assert( depth >= 0 && depth < 3 );
/* Skip to the end of the (potentially) constructed item by recording the
nesting level and continuing until either it reaches zero or we reach
the end of the item */
do
{
/* If it's a sequence/set, increment the depth; if it's an end-of-
constructed-item marker, decrement it by the appropriate amount */
if( attributeInfoPtr->fieldType == BER_SEQUENCE || \
attributeInfoPtr->fieldType == BER_SET )
currentDepth++;
currentDepth -= decodeNestingLevel( attributeInfoPtr->flags );
/* Move to the next entry */
attributeContinues = ( attributeInfoPtr->flags & FL_MORE ) ? TRUE : FALSE;
attributeInfoPtr++;
}
while( currentDepth > 0 && attributeContinues && \
iterationCount++ < CERT_MAX_ITERATIONS );
if( iterationCount >= CERT_MAX_ITERATIONS )
{
assert( NOTREACHED );
return( CRYPT_ERROR_FAILED );
}
/* We return the previous entry, since we're going to move on to the
next entry once we return */
*attributeInfoPtrPtr = attributeInfoPtr - 1;
return( CRYPT_OK );
}
/****************************************************************************
* *
* SET/SEQUENCE Management Routines *
* *
****************************************************************************/
/* When we're processing SETs/SEQUENCEs (generically referred to as a SET
OF), we need to maintain a stack of state information to handle a nested
SET OF. The following code implements the state stack */
#define SETOF_STATE_STACKSIZE 16
#define SETOF_FLAG_NONE 0x00 /* No flag value */
#define SETOF_FLAG_SUBTYPED 0x01 /* SET ends on a subtyped value */
#define SETOF_FLAG_RESTARTPOINT 0x02 /* SET OF rather than SET */
#define SETOF_FLAG_ISEMPTY 0x04 /* SET OF contains at least one entry */
typedef struct {
/* SET OF state information */
const ATTRIBUTE_INFO *infoStart; /* Start of SET OF attribute info */
int endPos; /* End position of SET OF */
int flags; /* SET OF flags */
/* Subtype information */
CRYPT_ATTRIBUTE_TYPE subtypeParent; /* Parent type if this is subtyped */
int inheritedFlags; /* Flags inherited from parent if subtyped */
} SETOF_STATE_INFO;
typedef struct {
SETOF_STATE_INFO stateInfo[ SETOF_STATE_STACKSIZE ];
int stackPos; /* Current position in stack */
} SETOF_STACK;
static void setofStackInit( SETOF_STACK *setofStack )
{
memset( setofStack, 0, sizeof( SETOF_STACK ) );
}
static BOOLEAN setofStackPush( SETOF_STACK *setofStack )
{
const int newPos = setofStack->stackPos + 1;
/* Increment the stack pointer and make sure that we don't overflow */
if( newPos < 1 || newPos >= SETOF_STATE_STACKSIZE )
{
assert( NOTREACHED );
return( FALSE );
}
setofStack->stackPos = newPos;
/* Initialise the new entry */
memset( &setofStack->stateInfo[ newPos ], 0, \
sizeof( SETOF_STATE_INFO ) );
return( TRUE );
}
static BOOLEAN setofStackPop( SETOF_STACK *setofStack )
{
const int newPos = setofStack->stackPos - 1;
/* Decrement the stack pointer and make sure that we don't underflow */
if( newPos < 0 || newPos >= SETOF_STATE_STACKSIZE - 1 )
{
assert( NOTREACHED );
return( FALSE );
}
setofStack->stackPos = newPos;
return( TRUE );
}
static SETOF_STATE_INFO *setofTOS( const SETOF_STACK *setofStack )
{
return( ( SETOF_STATE_INFO * ) \
&setofStack->stateInfo[ setofStack->stackPos ] );
}
/* Process the start of a SET/SET OF/SEQUENCE/SEQUENCE OF */
static int beginSetof( STREAM *stream, SETOF_STACK *setofStack,
const ATTRIBUTE_INFO *attributeInfoPtr,
CRYPT_ATTRIBUTE_TYPE *errorLocus,
CRYPT_ERRTYPE_TYPE *errorType )
{
SETOF_STATE_INFO *setofInfoPtr = setofTOS( setofStack );
CRYPT_ATTRIBUTE_TYPE oldSubtypeParent;
int oldInheritedFlags, setofLength, status;
assert( isWritePtr( setofStack, sizeof( SETOF_STACK ) ) );
assert( isReadPtr( attributeInfoPtr, sizeof( ATTRIBUTE_INFO ) ) );
assert( !( attributeInfoPtr->flags & FL_EXPLICIT ) );
/* Determine the length and start position of the SET OF items. Some
broken Verisign certs suddenly break into BER inside the cert policy
extension, so if the length evaluates to zero we have to determine it
by burrowing into the ASN.1 */
#if 0 /* 22/11/03 Removed since these Verisign certs have now expired */
objectPtr = sMemBufPtr( stream );
#endif /* 0 */
if( attributeInfoPtr->fieldEncodedType > 0 )
status = readConstructed( stream, &setofLength,
attributeInfoPtr->fieldEncodedType );
else
if( attributeInfoPtr->fieldType == BER_SET )
status = readSet( stream, &setofLength );
else
status = readSequence( stream, &setofLength );
#if 0 /* 22/11/03 Removed since these Verisign certs have now expired */
if( cryptStatusOK( status ) && setofLength == CRYPT_UNUSED )
{
/* Get the overall length without the tag + indef.length */
status = setofLength = getObjectLength( objectPtr, \
sMemDataLeft( stream ) );
setofLength -= 2;
setEndEOC = 2; /* Two bytes of EOC at end of object */
}
#endif /* 0 */
if( cryptStatusError( status ) )
return( status );
/* When processing a SET/SEQUENCE with default values for the elements,
the result may be a zero-length object, in which case we don't take
any action */
if( setofLength <= 0 )
return( CRYPT_OK );
/* Remember assorted info such as where the SET/SEQUENCE ends. In
addition if this is a SET OF/SEQUENCE OF, remember this as a restart
point for when we're parsing the next item in the SET/SEQUENCE OF */
oldSubtypeParent = setofInfoPtr->subtypeParent;
oldInheritedFlags = setofInfoPtr->inheritedFlags;
if( !setofStackPush( setofStack ) )
/* Stack overflow, there's a problem with the cert */
return( CRYPT_ERROR_OVERFLOW );
setofInfoPtr = setofTOS( setofStack );
setofInfoPtr->infoStart = attributeInfoPtr;
if( attributeInfoPtr->flags & FL_SETOF )
setofInfoPtr->flags |= SETOF_FLAG_RESTARTPOINT;
if( attributeInfoPtr->flags & FL_NONEMPTY )
setofInfoPtr->flags |= SETOF_FLAG_ISEMPTY;
setofInfoPtr->subtypeParent = oldSubtypeParent;
setofInfoPtr->inheritedFlags = oldInheritedFlags;
setofInfoPtr->endPos = stell( stream ) + setofLength;
#if 0 /* 22/11/03 Removed since these Verisign certs have now expired */
setofInfoPtr->endPos = stell( stream ) + setofLength - setEndEOC;
setofInfoPtr->endEOC = setEndEOC ? TRUE : FALSE;
#endif /* 0 */
return( CRYPT_OK );
}
/* Check whether we've reached the end of a SET/SEQUENCE */
static int checkSetofEnd( STREAM *stream, SETOF_STACK *setofStack,
const ATTRIBUTE_INFO **attributeInfoPtrPtr )
{
const SETOF_STATE_INFO *setofInfoPtr = setofTOS( setofStack );
const ATTRIBUTE_INFO *oldAttributeInfoPtr = *attributeInfoPtrPtr;
const ATTRIBUTE_INFO *attributeInfoPtr = *attributeInfoPtrPtr;
const int currentPos = stell( stream );
assert( isReadPtr( setofStack, sizeof( SETOF_STACK ) ) );
assert( isReadPtr( attributeInfoPtrPtr, sizeof( ATTRIBUTE_INFO * ) ) );
assert( isReadPtr( *attributeInfoPtrPtr, sizeof( ATTRIBUTE_INFO ) ) );
/* If we're still within the SET/SEQUENCE, we're done */
if( setofStack->stackPos <= 0 || currentPos < setofInfoPtr->endPos )
return( FALSE );
/* We've reached the end of or or more layers of SET/SEQUENCE, keep
popping SET/SEQUENCE state info until we can continue */
while( setofStack->stackPos > 0 && currentPos >= setofInfoPtr->endPos )
{
const int flags = setofInfoPtr->flags;
#if 0 /* 22/11/03 Removed since these Verisign certs have now expired */
/* If the extension drops into BER, make sure that the EOC is
present */
if( setofInfoPtr->endEOC > 0 && checkEOC( stream ) != TRUE )
return( CRYPT_ERROR_BADDATA );
#endif /* 0 */
/* Pop one level of parse state */
if( !setofStackPop( setofStack ) )
/* Stack underflow, there's a problem with the cert */
return( CRYPT_ERROR_UNDERFLOW );
setofInfoPtr = setofTOS( setofStack );
attributeInfoPtr = setofInfoPtr->infoStart;
assert( setofInfoPtr->endPos > 0 && setofInfoPtr->endPos < 65536L );
/* If it's a pure SET/SEQUENCE (not a SET OF/SEQUENCE OF) and there
are no more elements present, go to the end of the SET/SEQUENCE
info in the decoding table */
if( !( flags & SETOF_FLAG_RESTARTPOINT ) && \
currentPos >= setofInfoPtr->endPos )
{
int status;
status = findItemEnd( &attributeInfoPtr, 0 );
if( cryptStatusError( status ) )
return( status );
}
}
*attributeInfoPtrPtr = attributeInfoPtr;
return( ( attributeInfoPtr != oldAttributeInfoPtr ) ? TRUE : FALSE );
}
/****************************************************************************
* *
* Identified Item Management Routines *
* *
****************************************************************************/
/* Given a pointer to a set of SEQUENCE { type, value } entries, return a
pointer to the { value } entry appropriate for the data in the stream.
If the entry contains user data in the { value } portion then the
returned pointer points to this, if it contains a fixed value or isn't
present at all then the returned pointer points to the { type } portion */
static const ATTRIBUTE_INFO *findIdentifiedItem( STREAM *stream,
const ATTRIBUTE_INFO *attributeInfoPtr )
{
BYTE oid[ MAX_OID_SIZE ];
int oidLength, sequenceLength, status;
assert( isReadPtr( attributeInfoPtr, sizeof( ATTRIBUTE_INFO ) ) );
assert( attributeInfoPtr->flags & FL_IDENTIFIER );
/* Skip the header and read the OID. We only check for a sane total
length in the debug version since this isn't a fatal error */
readSequence( stream, &sequenceLength );
status = readRawObject( stream, oid, &oidLength, MAX_OID_SIZE,
BER_OBJECT_IDENTIFIER );
if( cryptStatusError( status ) )
return( NULL );
sequenceLength -= oidLength;
assert( sequenceLength >= 0 );
/* Walk down the list of entries trying to match it to an allowed value */
while( attributeInfoPtr->flags & FL_IDENTIFIER )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -