📄 certexrw.c
字号:
/****************************************************************************
* *
* Certificate Attribute Read/Write Routines *
* Copyright Peter Gutmann 1996-2002 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL ) || defined( INC_CHILD )
#include "asn1.h"
#include "asn1objs.h"
#include "asn1oid.h"
#include "cert.h"
#include "certattr.h"
#else
#include "keymgmt/asn1.h"
#include "keymgmt/asn1objs.h"
#include "keymgmt/asn1oid.h"
#include "keymgmt/cert.h"
#include "keymgmt/certattr.h"
#endif /* Compiler-specific includes */
/* Context-specific tags for assorted extensions and CMS attributes */
#define CTAG_CE_EXTENSIONS 3 /* Cert */
#define CTAG_CR_EXTENSIONS 0 /* CRL */
#define CTAG_OR_EXTENSIONS 2 /* OCSP request */
#define CTAG_OP_EXTENSIONS 1 /* OCSP response */
#define CTAG_SI_AUTHENTICATEDATTRIBUTES 0
/* Prototypes for functions in certcomp.c */
int oidToText( const BYTE *binaryOID, char *oid );
/****************************************************************************
* *
* Attribute Read Routines *
* *
****************************************************************************/
/* 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 const ATTRIBUTE_INFO *findItemEnd( const ATTRIBUTE_INFO *attributeInfoPtr,
const int depth )
{
BOOLEAN attributeContinues;
int currentDepth = depth;
assert( isReadPtr( attributeInfoPtr, ATTRIBUTE_INFO ) );
/* 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 );
return( attributeInfoPtr - 1 );
}
/* 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, ATTRIBUTE_INFO ) );
/* 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 )
{
const BYTE *oidPtr;
/* Skip the SEQUENCE and OID */
attributeInfoPtr++;
oidPtr = attributeInfoPtr->oid;
if( !( attributeInfoPtr->flags & FL_NONENCODING ) )
attributeInfoPtr++;
else
/* If this is a blob field, we've hit a dont-care value (usually
the last in a series of type-and-value pairs) which ensures
that new additions don't get processed as errors */
if( attributeInfoPtr->fieldType == FIELDTYPE_BLOB )
{
/* If there's a { value } attached to the type, skip it */
if( sequenceLength > 0 )
sSkip( stream, sequenceLength );
return( attributeInfoPtr );
}
/* If the OID matches, return a pointer to the value entry */
if( !memcmp( oidPtr, oid, sizeofOID( oidPtr ) ) )
{
/* If this is a fixed field and there's a value attached, skip
it */
if( ( attributeInfoPtr->flags & FL_NONENCODING ) && \
sequenceLength > 0 )
sSkip( stream, sequenceLength );
return( attributeInfoPtr );
}
/* The OID doesn't match, skip the value entry and continue. We set
the current nesting depth parameter to 1 since we've already
entered the SEQUENCE above */
attributeInfoPtr = findItemEnd( attributeInfoPtr, 1 ) + 1;
}
/* We reached the end of the set of entries without matching the OID */
return( NULL );
}
/* Read a sequence of identifier fields. Returns the number of fields read */
static int readIdentifierFields( STREAM *stream, ATTRIBUTE_LIST **attributeListPtrPtr,
const ATTRIBUTE_INFO **attributeInfoPtrPtr, const int flags,
const CRYPT_ATTRIBUTE_TYPE fieldID, CRYPT_ATTRIBUTE_TYPE *errorLocus,
CRYPT_ERRTYPE_TYPE *errorType )
{
int count = 0;
assert( !( flags & ATTR_FLAG_INVALID ) );
assert( isWritePtr( attributeListPtrPtr, ATTRIBUTE_LIST * ) );
assert( isReadPtr( attributeInfoPtrPtr, ATTRIBUTE_INFO * ) && \
isReadPtr( *attributeInfoPtrPtr, ATTRIBUTE_INFO ) );
while( peekTag( stream ) == BER_OBJECT_IDENTIFIER )
{
ATTRIBUTE_INFO *attributeInfoPtr = ( ATTRIBUTE_INFO * ) *attributeInfoPtrPtr;
BYTE oid[ MAX_OID_SIZE ];
const int dummy = CRYPT_UNUSED;
int oidLength, status;
/* Read the OID and walk down the list of possible OIDs up to the end
of the group of alternatives trying to match it to an allowed
value */
status = readRawObject( stream, oid, &oidLength, MAX_OID_SIZE,
BER_OBJECT_IDENTIFIER );
if( cryptStatusError( status ) )
return( status );
while( oidLength != sizeofOID( attributeInfoPtr->oid ) || \
memcmp( attributeInfoPtr->oid, oid, oidLength ) )
{
/* If we've reached the end of the list and the OID wasn't
matched, exit */
if( ( attributeInfoPtr->flags & FL_SEQEND_MASK ) || \
!( attributeInfoPtr->flags & FL_MORE ) )
return( CRYPT_ERROR_BADDATA );
attributeInfoPtr++;
}
/* The OID matches, add this field as an identifier field (this
will catch duplicate OIDs since we can't add the same identifier
field twice) */
if( fieldID != CRYPT_ATTRIBUTE_NONE )
/* If there's a field value present then this is a CHOICE of
attributes whose value is the field value, so we add it with
this value */
status = addAttributeField( attributeListPtrPtr,
fieldID, CRYPT_ATTRIBUTE_NONE,
&attributeInfoPtr->fieldID, CRYPT_UNUSED,
flags, errorLocus, errorType );
else
/* It's a standard field */
status = addAttributeField( attributeListPtrPtr,
attributeInfoPtr->fieldID, CRYPT_ATTRIBUTE_NONE,
&dummy, CRYPT_UNUSED, flags, errorLocus,
errorType );
if( cryptStatusError( status ) )
return( status );
count++;
/* If there's more than one OID present in a CHOICE, it's an error */
if( fieldID != CRYPT_ATTRIBUTE_NONE && count > 1 )
{
*errorLocus = attributeInfoPtr->fieldID,
*errorType = CRYPT_ERRTYPE_ATTR_PRESENT;
return( CRYPT_ERROR_BADDATA );
}
}
/* We've processed the non-data field(s), move on to the next field.
We move to the last valid non-data field rather than the start of the
field following it since the caller needs to be able to check whether
there are more fields to follow using the current fields flags */
while( !( ( *attributeInfoPtrPtr )->flags & FL_SEQEND_MASK ) && \
( ( *attributeInfoPtrPtr )->flags & FL_MORE ) )
( *attributeInfoPtrPtr )++;
return( CRYPT_OK );
}
/* Read an attribute field */
static int fieldErrorReturn( CRYPT_ATTRIBUTE_TYPE *errorLocus,
CRYPT_ERRTYPE_TYPE *errorType, const int status,
const CRYPT_ATTRIBUTE_TYPE fieldID )
{
/* Since some fields are internal-use only (eg meaningless blob data,
version numbers, and other paraphernalia) we only set the locus if
it has a meaningful value */
*errorLocus = ( fieldID > CRYPT_CERTINFO_FIRST && \
fieldID < CRYPT_CERTINFO_LAST ) ? \
fieldID : CRYPT_ATTRIBUTE_NONE;
*errorType = CRYPT_ERRTYPE_ATTR_VALUE;
return( status );
}
static int readAttributeField( STREAM *stream, ATTRIBUTE_LIST **attributeListPtrPtr,
const ATTRIBUTE_INFO *attributeInfoPtr,
const CRYPT_ATTRIBUTE_TYPE subtypeParent, const int flags,
CRYPT_ATTRIBUTE_TYPE *errorLocus, CRYPT_ERRTYPE_TYPE *errorType )
{
CRYPT_ATTRIBUTE_TYPE fieldID, subFieldID;
int length, status;
assert( !( flags & ATTR_FLAG_INVALID ) );
assert( isWritePtr( attributeListPtrPtr, ATTRIBUTE_LIST * ) );
assert( isReadPtr( attributeInfoPtr, ATTRIBUTE_INFO ) );
/* Set up the field identifiers depending on whether it's a normal field
or a subfield of a parent field */
if( subtypeParent == CRYPT_ATTRIBUTE_NONE )
{
fieldID = attributeInfoPtr->fieldID;
subFieldID = CRYPT_ATTRIBUTE_NONE;
}
else
{
fieldID = subtypeParent;
subFieldID = attributeInfoPtr->fieldID;
}
/* If it's a sequence/set, there's no data to read so we skip the length
and move on to the fields within the sequence */
if( attributeInfoPtr->fieldType == BER_SEQUENCE || \
attributeInfoPtr->fieldType == BER_SET )
{
const int status = readShortLength( stream );
return( cryptStatusError( status ) ? status : CRYPT_OK );
}
/* If it's an integer or time type, read it */
if( attributeInfoPtr->fieldType == BER_INTEGER || \
attributeInfoPtr->fieldType == BER_ENUMERATED || \
attributeInfoPtr->fieldType == BER_BITSTRING || \
attributeInfoPtr->fieldType == BER_BOOLEAN || \
attributeInfoPtr->fieldType == BER_NULL )
{
BOOLEAN boolean;
long longValue;
int value;
/* Read the data as appropriate */
switch( attributeInfoPtr->fieldType )
{
case BER_BITSTRING:
status = readBitStringData( stream, &value );
break;
case BER_BOOLEAN:
status = readBooleanData( stream, &boolean );
value = boolean;
break;
case BER_ENUMERATED:
status = readEnumeratedData( stream, &value );
break;
case BER_INTEGER:
status = readShortIntegerData( stream, &longValue );
value = ( int ) longValue;
break;
case BER_NULL:
/* NULL values have no associated data so we explicitly set
the value to CRYPT_UNUSED to ensure that this is returned
on any attempt to read it */
value = CRYPT_UNUSED;
break;
default:
assert( NOTREACHED );
return( CRYPT_ERROR );
}
if( cryptStatusError( status ) )
return( fieldErrorReturn( errorLocus, errorType, status,
attributeInfoPtr->fieldID ) );
/* Add the data for this attribute field */
return( addAttributeField( attributeListPtrPtr, fieldID, subFieldID,
&value, CRYPT_UNUSED, flags, errorLocus,
errorType ) );
}
if( attributeInfoPtr->fieldType == BER_TIME_GENERALIZED || \
attributeInfoPtr->fieldType == BER_TIME_UTC )
{
time_t timeVal;
if( attributeInfoPtr->fieldType == BER_TIME_GENERALIZED )
status = readGeneralizedTimeData( stream, &timeVal );
else
status = readUTCTimeData( stream, &timeVal );
if( cryptStatusError( status ) )
return( fieldErrorReturn( errorLocus, errorType, status,
attributeInfoPtr->fieldID ) );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -