📄 certcget.c
字号:
/****************************************************************************
* *
* Get/Delete Certificate Components *
* Copyright Peter Gutmann 1997-2003 *
* *
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL ) || defined( INC_CHILD )
#include "cert.h"
#include "certattr.h"
#include "../misc/asn1_rw.h"
#include "../misc/asn1s_rw.h"
#else
#include "cert/cert.h"
#include "cert/certattr.h"
#include "misc/asn1_rw.h"
#include "misc/asn1s_rw.h"
#endif /* Compiler-specific includes */
/****************************************************************************
* *
* 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
that 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 that 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 );
}
/* Copy string data from a cert */
static int copyCertInfo( void *certInfo, int *certInfoLength,
const void *data, const int dataLength )
{
const int maxLength = *certInfoLength;
if( dataLength <= 0 )
return( CRYPT_ERROR_NOTFOUND );
*certInfoLength = dataLength;
if( certInfo == NULL )
return( CRYPT_OK );
if( dataLength > maxLength )
return( CRYPT_ERROR_OVERFLOW );
memcpy( certInfo, data, dataLength );
return( CRYPT_OK );
}
/****************************************************************************
* *
* DN/GeneralName Routines *
* *
****************************************************************************/
/* GeneralNames and DNs are handled via indirect selection. There are four
classes of field type that cover these names:
GNSelection = EXCLUDEDSUBTREES | ...
GNValue = OTHERNAME | ... | DIRECTORYNAME
DNSelection = SUBJECTNAME | ISSUERNAME | DIRECTORYNAME
DNValue = 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 implicit selections, in the worst case being something like
subjectAltName -> directoryName -> DN field. DN and GeneralName
components may be absent (if we're selecting it in order to create it),
or present (if we're about to read it), or can be created when accessed
(if we're about to write to it). The handling is selected by the
SELECTION_OPTION type, if a cert is in the high state then MAY/CREATE
options are implicitly converted to MUST_BE_PRESENT during the selection
process.
The selection is performed as follows:
set attribute:
selectionComponent:
selectDN subject | issuer | MAY_BE_ABSENT
selectGN attributeID | MAY_BE_ABSENT
- Select prior to use
valueComponent:
selectDN - | CREATE_IF_ABSENT
selectGN - | CREATE_IF_ABSENT
- To create DN/GeneralName before adding DN/GN
component/setting DN string
get attribute:
selectionComponent:
check subject | issuer | other | Presence check only
check attributeID
- Return T/F if present
valueComponent:
selectDN none | MUST_BE_PRESENT
selectGN none | MUST_BE_PRESENT
- To get DN/GeneralName component
delete attribute:
selectDN subject | issuers | MUST_BE_PRESENT
selectGN attributeID | MUST_BE_PRESENT
- To delete DN/GeneralName component
This code is cursed */
/* Check whether the currently selected extension is a GeneralName. We do
this both for simplicity and because isGeneralNameSelectionComponent() is
a complex macro that we want to avoid expanding as much as possible */
static BOOLEAN isGeneralNameSelected( const CERT_INFO *certInfoPtr )
{
return( certInfoPtr->attributeCursor != NULL && \
isGeneralNameSelectionComponent( certInfoPtr->attributeCursor->fieldID ) ? \
TRUE : FALSE );
}
#ifndef NDEBUG
static BOOLEAN selectionInfoConsistent( const CERT_INFO *certInfoPtr )
{
/* If the DN-in-extension flag is set, there must be a DN selected */
if( certInfoPtr->currentSelection.dnPtr == NULL && \
certInfoPtr->currentSelection.dnInExtension )
return( FALSE );
/* If there's a DN selected and it's not in in an extension, it must be
the subject or issuer DN */
if( certInfoPtr->currentSelection.dnPtr != NULL && \
!certInfoPtr->currentSelection.dnInExtension && \
certInfoPtr->currentSelection.dnPtr != &certInfoPtr->subjectName && \
certInfoPtr->currentSelection.dnPtr != &certInfoPtr->issuerName )
return( FALSE );
/* If there's a GeneralName selected, there can't also be a saved
GeneralName present */
if( isGeneralNameSelected( certInfoPtr ) && \
certInfoPtr->currentSelection.generalName != CRYPT_ATTRIBUTE_NONE )
return( FALSE );
return( TRUE );
}
#endif /* NDEBUG */
/* Check whether there's a DN in the currently-selected extension, and update
the various selection values if we find one */
static int findDnInExtension( CERT_INFO *certInfoPtr,
const BOOLEAN updateCursor )
{
const CRYPT_ATTRIBUTE_TYPE attributeID = certInfoPtr->attributeCursor->attributeID;
const CRYPT_ATTRIBUTE_TYPE fieldID = certInfoPtr->attributeCursor->fieldID;
ATTRIBUTE_LIST *attributeListPtr;
/* We're inside a GeneralName, clear any possible saved selection */
certInfoPtr->currentSelection.generalName = CRYPT_ATTRIBUTE_NONE;
assert( selectionInfoConsistent( certInfoPtr ) );
/* Search for a DN in the current GeneralName */
for( attributeListPtr = certInfoPtr->attributeCursor; \
attributeListPtr != NULL && \
attributeListPtr->attributeID == attributeID && \
attributeListPtr->fieldID == fieldID; \
attributeListPtr = attributeListPtr->next )
if( attributeListPtr->fieldType == FIELDTYPE_DN )
{
/* We found a DN, select it */
certInfoPtr->currentSelection.dnPtr = &attributeListPtr->value;
if( updateCursor )
certInfoPtr->attributeCursor = attributeListPtr;
certInfoPtr->currentSelection.dnInExtension = TRUE;
assert( selectionInfoConsistent( certInfoPtr ) );
return( CRYPT_OK );
}
return( CRYPT_ERROR_NOTFOUND );
}
/* Find a GeneralName field in a GeneralName */
#if 0
/* Currently handled with:
attributeListPtr = findAttributeField( certInfoPtr->attributeCursor,
certInfoPtr->attributeCursor->fieldID,
certInfoType ); */
static const ATTRIBUTE_LIST *findGeneralNameField( const ATTRIBUTE_LIST *attributeListPtr,
const CRYPT_ATTRIBUTE_TYPE certInfoType )
{
const CRYPT_ATTRIBUTE_TYPE attributeID = attributeListPtr->attributeID;
const CRYPT_ATTRIBUTE_TYPE fieldID = attributeListPtr->fieldID;
assert( isGeneralNameSelectionComponent( attributeListPtr->fieldID ) );
/* Search for the GeneralName component in the current GeneralName */
while( attributeListPtr != NULL && \
attributeListPtr->attributeID == attributeID && \
attributeListPtr->fieldID == fieldID )
{
if( attributeListPtr->subFieldID == certInfoType )
return( attributeListPtr );
attributeListPtr = attributeListPtr->next;
}
return( NULL );
}
#endif /* 0 */
/* Move the extension cursor to the given extension field */
int moveCursorToField( CERT_INFO *certInfoPtr,
const CRYPT_ATTRIBUTE_TYPE certInfoType )
{
const ATTRIBUTE_LIST *attributeListPtr;
assert( selectionInfoConsistent( certInfoPtr ) );
assert( certInfoType >= CRYPT_CERTINFO_FIRST_EXTENSION && \
certInfoType <= CRYPT_CERTINFO_LAST );
/* Try and locate the given field in the extension */
attributeListPtr = findAttributeField( certInfoPtr->attributes,
certInfoType,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr == NULL )
return( CRYPT_ERROR_NOTFOUND );
/* We found the given field, update the cursor and select the DN within
it if it's present */
certInfoPtr->currentSelection.updateCursor = FALSE;
certInfoPtr->attributeCursor = ( ATTRIBUTE_LIST * ) attributeListPtr;
if( isGeneralNameSelectionComponent( certInfoType ) )
/* If this is a GeneralName, select the DN within it if there's one
present */
findDnInExtension( certInfoPtr, FALSE );
assert( selectionInfoConsistent( certInfoPtr ) );
return( CRYPT_OK );
}
/* Synchronise DN/GeneralName selection information after moving the
extension cursor */
void syncSelection( CERT_INFO *certInfoPtr )
{
/* We've moved the cursor, clear any saved GeneralName selection */
certInfoPtr->currentSelection.generalName = CRYPT_ATTRIBUTE_NONE;
/* I've we've moved the cursor off the GeneralName or there's no DN in
the GeneralName, deselect the DN */
if( !isGeneralNameSelected( certInfoPtr ) || \
cryptStatusError( findDnInExtension( certInfoPtr, FALSE ) ) )
{
certInfoPtr->currentSelection.dnPtr = NULL;
certInfoPtr->currentSelection.dnInExtension = FALSE;
}
}
/* Handle selection of a GeneralName in a cert extension */
int selectGeneralName( CERT_INFO *certInfoPtr,
const CRYPT_ATTRIBUTE_TYPE certInfoType,
const SELECTION_OPTION option )
{
assert( ( option == MAY_BE_ABSENT && \
isGeneralNameSelectionComponent( certInfoType ) ) || \
( ( option == MUST_BE_PRESENT || option == CREATE_IF_ABSENT ) && \
certInfoType == CRYPT_ATTRIBUTE_NONE ) );
assert( selectionInfoConsistent( certInfoPtr ) );
certInfoPtr->currentSelection.updateCursor = FALSE;
if( option == MAY_BE_ABSENT )
{
/* If the selection is present, update the extension cursor and
exit */
if( cryptStatusOK( moveCursorToField( certInfoPtr, certInfoType ) ) )
return( CRYPT_OK );
/* If the certificate is in the high state, the MAY is treated as
a MUST, since we can't be selecting something so that we can
create it later */
if( certInfoPtr->certificate != NULL )
return( CRYPT_ERROR_NOTFOUND );
/* The selection isn't present, remember it for later, without
changing any other selection info */
certInfoPtr->currentSelection.generalName = certInfoType;
certInfoPtr->attributeCursor = NULL;
assert( selectionInfoConsistent( certInfoPtr ) );
return( CRYPT_OK );
}
assert( option == MUST_BE_PRESENT || option == CREATE_IF_ABSENT );
/* If there's no saved GeneralName selection present, the extension
cursor must be pointing to a GeneralName */
if( certInfoPtr->currentSelection.generalName == CRYPT_ATTRIBUTE_NONE )
return( isGeneralNameSelected( certInfoPtr ) ? \
CRYPT_OK : CRYPT_ERROR_NOTFOUND );
/* Try and move the cursor to the saved GeneralName selection */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -