📄 certcomp.c
字号:
/****************************************************************************
* *
* Certificate Component Handling Routines *
* Copyright Peter Gutmann 1997-2001 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL ) || defined( INC_CHILD )
#include "asn1.h"
#include "asn1oid.h"
#include "cert.h"
#else
#include "keymgmt/asn1.h"
#include "keymgmt/asn1oid.h"
#include "keymgmt/cert.h"
#endif /* Compiler-specific includes */
/* This module includes (somewhat complex) handling for GeneralNames and
DN's, which are handled via indirect selection. There are four classes
of field types which cover these names:
GNSelection = EXCLUDEDSUBTREES | ...
GN = OTHERNAME | ... | DIRECTORYNAME
DNSelection = SUBJECTNAME | ISSUERNAME | DIRECTORYNAME
DN = C | O | OU | CN | ...
Note that DIRECTORYNAME is present twice since it's both a component of a
GeneralName and a DN in its own right. GNSelection and DNSelection
components merely select a composite component, the primitive elements are
read and written via the GN and DN values. The selection process is as
follows:
GNSelection --+ (default = subjectAltName)
|
v
GN -+----------------> non-DirectoryName field
|
+--+ DirectoryName
|
DNSelection --+ (default = subjectName)
|
v
DN ------------------> DN field
Selecting a component can therefore lead through a complex heirarchy of
explicit and implied selections, in the worst case being something like
subjectAltName -> directoryName -> DN field */
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Convert a binary OID to its text form */
int oidToText( const BYTE *binaryOID, char *oid )
{
char *outputPtr = oid;
int i, j, length = binaryOID[ 1 ];
long value;
#ifdef sun
BOOLEAN brokenSprintf = FALSE;
#endif /* sun */
/* Pick apart the OID. This assumes that no OID component will be
larger than LONG_MAX */
i = binaryOID[ 2 ] / 40;
j = binaryOID[ 2 ] % 40;
if( i > 2 )
{
/* Handle special case for large j if i = 2 */
j += ( i - 2 ) * 40;
i = 2;
}
#ifdef sun
if( sprintf( outputPtr, "X" ) != 1 )
{
/* SunOS/Slowaris has broken sprintf() handling, in SunOS 4.x this
was documented as returning a pointer to the output data as per
the Berkeley original, under Slowaris the manpage was changed so
it looks like any other sprintf() but it still returns the
pointer to the output buffer in some versions so we have to check
at runtime to see what we've got and adjust our behaviour
accordingly. In addition the standard sprintf()'s require casts
of the return value to avoid compile errors when building on SunOS
systems, these are safe because that code path is never taken on
these systems */
assert( sprintf( outputPtr, "123" ) >= 4 ); /* Double-check */
brokenSprintf = TRUE;
outputPtr += strlen( ( void * ) sprintf( outputPtr, "%d %d", i, j ) );
}
else
#endif /* Sun braindamage */
outputPtr += ( int ) sprintf( outputPtr, "%d %d", i, j );
value = 0;
for( i = 3; i < length + 2; i++ )
{
value = ( value << 7 ) | ( binaryOID[ i ] & 0x7F );
if( !( binaryOID[ i ] & 0x80 ) )
{
#ifdef sun
if( brokenSprintf )
outputPtr += strlen( ( void * ) sprintf( outputPtr, " %ld", value ) );
else
#endif /* Sun braindamage */
outputPtr += ( int ) sprintf( outputPtr, " %ld", value );
value = 0;
}
/* Make sure we don't overflow the buffer (the value 20 is the
maximum magnitude of a 64-bit int plus space plus '\0') */
if( outputPtr - oid > ( CRYPT_MAX_TEXTSIZE * 2 ) - 20 )
return( CRYPT_ERROR_BADDATA );
}
length = strlen( oid );
oid[ length ] = '\0'; /* Not really necessary, but nice */
return( length );
}
/* Check that a time value is valid */
static int checkTime( CERT_INFO *certInfoPtr, const CRYPT_ATTRIBUTE_TYPE type,
const time_t currentValue, const time_t newValue,
const int newValueLength )
{
/* Check to make sure the time given isn't before 1993, the start date
for X.509v2 (this is more of a consistency check than any real
requirement) */
if( newValue < 0x2B3C0000L )
{
setErrorInfo( certInfoPtr, type, CRYPT_ERRTYPE_ATTR_VALUE );
return( CRYPT_ARGERROR_STR1 );
}
if( newValueLength != sizeof( time_t ) )
{
setErrorInfo( certInfoPtr, type, CRYPT_ERRTYPE_ATTR_SIZE );
return( CRYPT_ARGERROR_NUM1 );
}
if( currentValue )
{
setErrorInfo( certInfoPtr, type, CRYPT_ERRTYPE_ATTR_PRESENT );
return( CRYPT_ERROR_INITED );
}
return( CRYPT_OK );
}
/* Option codes for the GeneralName/DN selection functions */
typedef enum {
SELECT_SELECT, /* Remember current value */
SELECT_INSTANTIATE_SET, /* Instatiate remembered value, set pointer */
SELECT_SET, /* Set pointer from remembered value */
SELECT_SYNC /* Clear pointer from remembered value if item
deleted */
} SELECT_OPTION;
/* Select a DN */
static int selectDN( CERT_INFO *certInfoPtr, const CRYPT_ATTRIBUTE_TYPE type,
const SELECT_OPTION option )
{
ATTRIBUTE_LIST *attributeListPtr;
/* If we're being asked to remember the setting, just save the value and
exit */
if( option == SELECT_SELECT )
{
if( certInfoPtr->currentDN != type )
certInfoPtr->currentDNptr = NULL;
certInfoPtr->currentDN = type;
return( CRYPT_OK );
}
/* If there's no selection made, default to the subject name */
if( certInfoPtr->currentDN == CRYPT_ATTRIBUTE_NONE )
certInfoPtr->currentDN = CRYPT_CERTINFO_SUBJECTNAME;
/* If we're being asked to sync the value to the current setting, make
sure the entry is still present and clear the settings if not */
if( option == SELECT_SYNC )
{
const CRYPT_ATTRIBUTE_TYPE dnType = ( type == CRYPT_ATTRIBUTE_NONE ) ? \
certInfoPtr->currentDN : type;
ATTRIBUTE_LIST *attributeListPtr;
/* If the subject name isn't present but is selected or specified,
clear the selection. We can never get a sync on an issuer name
since it's read-only */
if( dnType == CRYPT_CERTINFO_SUBJECTNAME )
{
if( certInfoPtr->subjectName == NULL )
{
certInfoPtr->currentDN = CRYPT_ATTRIBUTE_NONE;
certInfoPtr->currentDNptr = NULL;
}
return( CRYPT_OK );
}
/* It's a DN in an attribute, check that the attribute is still
present and clear the DN selection if not */
attributeListPtr = findAttributeField( certInfoPtr->attributes,
certInfoPtr->currentGeneralName, CRYPT_CERTINFO_DIRECTORYNAME );
if( attributeListPtr == NULL )
{
certInfoPtr->currentDN = CRYPT_ATTRIBUTE_NONE;
certInfoPtr->currentDNptr = NULL;
}
return( CRYPT_OK );
}
/* If the DN is already selected, return now */
if( certInfoPtr->currentDNptr != NULL )
return( CRYPT_OK );
/* The issuer and subject name fields are always present */
if( certInfoPtr->currentDN == CRYPT_CERTINFO_ISSUERNAME )
{
certInfoPtr->currentDNptr = &certInfoPtr->issuerName;
/* If it's a self-signed cert and the issuer name isn't explicitly
present then it must be implicitly present as the subject name */
if( certInfoPtr->issuerName == NULL && \
( certInfoPtr->flags & CERT_FLAG_SELFSIGNED ) )
certInfoPtr->currentDNptr = &certInfoPtr->subjectName;
return( CRYPT_OK );
}
if( certInfoPtr->currentDN == CRYPT_CERTINFO_SUBJECTNAME )
{
certInfoPtr->currentDNptr = &certInfoPtr->subjectName;
return( CRYPT_OK );
}
/* From here on we're handling a DN in a GeneralName. If there's
currently no GeneralName selected, default to the subject altName */
if( certInfoPtr->currentGeneralName == CRYPT_ATTRIBUTE_NONE )
certInfoPtr->currentGeneralName = CRYPT_CERTINFO_SUBJECTALTNAME;
/* Try and find the requested DN */
attributeListPtr = findAttributeField( certInfoPtr->attributes,
certInfoPtr->currentGeneralName,
CRYPT_CERTINFO_DIRECTORYNAME );
if( attributeListPtr == NULL )
{
int value = CRYPT_UNUSED, status;
/* If it's not present and we're not being asked to instantiate it,
return an error */
if( option != SELECT_INSTANTIATE_SET )
return( CRYPT_ERROR_NOTINITED );
/* We're being asked to instantiate the DN, create the attribute
field which contains it */
status = addAttributeField( &certInfoPtr->attributes,
certInfoPtr->currentGeneralName,
CRYPT_CERTINFO_DIRECTORYNAME, &value, CRYPT_UNUSED,
ATTR_FLAG_NONE, &certInfoPtr->errorLocus,
&certInfoPtr->errorType );
if( cryptStatusError( status ) )
return( status );
/* Find the field we just created. Since this can be a multivalued
attribute, we have to find the last component in the collection,
which will be the one we just added */
attributeListPtr = findAttributeField( certInfoPtr->attributes,
certInfoPtr->currentGeneralName, CRYPT_CERTINFO_DIRECTORYNAME );
moveAttributeCursor( &attributeListPtr,
CRYPT_CERTINFO_CURRENT_COMPONENT, CRYPT_CURSOR_LAST );
}
/* Obtain the DN pointer from the attribute field */
certInfoPtr->currentDNptr = ( void ** ) &attributeListPtr->data;
return( CRYPT_OK );
}
/* Select a GeneralName */
static ATTRIBUTE_LIST *selectGeneralName( CERT_INFO *certInfoPtr,
const CRYPT_ATTRIBUTE_TYPE certInfoType,
const SELECT_OPTION option )
{
ATTRIBUTE_LIST *attributeListPtr;
/* If it's a sync, make sure the selected GeneralName is still present */
if( option == SELECT_SYNC )
{
if( certInfoPtr->currentGeneralName != CRYPT_ATTRIBUTE_NONE && \
findAttributeField( certInfoPtr->attributes, \
certInfoPtr->currentGeneralName, \
CRYPT_ATTRIBUTE_NONE ) == NULL )
certInfoPtr->currentGeneralName = CRYPT_ATTRIBUTE_NONE;
return( NULL ); /* Return value is ignored */
}
/* If it's a straight GeneralName selection, remember the setting and
select as the new default DN the DirectoryName within the
GeneralName */
if( isGeneralNameSelectionComponent( certInfoType ) )
{
if( certInfoPtr->currentGeneralName != certInfoType )
{
certInfoPtr->currentGeneralName = certInfoType;
selectDN( certInfoPtr, CRYPT_CERTINFO_DIRECTORYNAME,
SELECT_SELECT );
}
attributeListPtr = findAttributeField( certInfoPtr->attributes,
certInfoPtr->currentGeneralName, CRYPT_ATTRIBUTE_NONE );
}
else
{
/* If there's no General Name selected, default to the subject
altName */
if( certInfoPtr->currentGeneralName == CRYPT_ATTRIBUTE_NONE )
certInfoPtr->currentGeneralName = CRYPT_CERTINFO_SUBJECTALTNAME;
/* If we're selecting a DirectoryName, pass the call down to the DN
selection to set things up at that level */
if( certInfoType == CRYPT_CERTINFO_DIRECTORYNAME )
{
if( cryptStatusError( selectDN( certInfoPtr,
CRYPT_CERTINFO_DIRECTORYNAME, option ) ) )
return( NULL );
}
attributeListPtr = findAttributeField( certInfoPtr->attributes,
certInfoPtr->currentGeneralName, certInfoType );
}
/* Since a GeneralName is always part of an attribute, we also need to
update the attribute cursor unless it's already set to this field,
in which case updating it might overwrite the current component
position in a multivalued field */
if( attributeListPtr != NULL )
{
if( certInfoPtr->attributeCursor == NULL || \
attributeListPtr->attributeID != \
certInfoPtr->attributeCursor->attributeID || \
attributeListPtr->fieldID != \
certInfoPtr->attributeCursor->fieldID )
certInfoPtr->attributeCursor = attributeListPtr;
else
/* Return the currently-selected component of a potentially
multivalued field. Since this is set by the attribute
cursor management subsystem, we don't try and change it
here */
attributeListPtr = certInfoPtr->attributeCursor;
}
return( attributeListPtr );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -