📄 ca_issue.c
字号:
/****************************************************************************
* *
* cryptlib DBMS CA Certificate Issue Interface *
* Copyright Peter Gutmann 1996-2007 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "crypt.h"
#include "keyset.h"
#include "dbms.h"
#else
#include "crypt.h"
#include "keyset/keyset.h"
#include "keyset/dbms.h"
#endif /* Compiler-specific includes */
#ifdef USE_DBMS
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Get the issue type (new request, renewal, etc) for a particular
certificate request or certificate */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int getCertIssueType( INOUT DBMS_INFO *dbmsInfo,
IN_HANDLE const CRYPT_CERTIFICATE iCertificate,
OUT_ENUM_OPT( CERTADD ) CERTADD_TYPE *issueType,
const BOOLEAN isCert )
{
BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
BYTE certData[ MAX_QUERY_RESULT_SIZE + 8 ];
char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
int certIDlength, length, status;
assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
assert( isWritePtr( issueType, sizeof( CERTADD_TYPE ) ) );
REQUIRES( isHandleRangeValid( iCertificate ) );
/* Clear return value */
*issueType = CERTADD_NONE;
/* Get the certID of the request that resulted in the certificate
creation */
status = getKeyID( certID, ENCODED_DBXKEYID_SIZE, &certIDlength,
iCertificate, CRYPT_CERTINFO_FINGERPRINT_SHA );
if( cryptStatusOK( status ) && isCert )
{
/* If it's a certificate we have to apply an extra level of
indirection to get the request that resulted in its creation */
initBoundData( boundDataPtr );
setBoundData( boundDataPtr, 0, certID, certIDlength );
status = dbmsQuery(
"SELECT reqCertID FROM certLog WHERE certID = ?",
certData, MAX_QUERY_RESULT_SIZE, &length,
boundDataPtr, DBMS_CACHEDQUERY_NONE,
DBMS_QUERY_NORMAL );
if( cryptStatusOK( status ) )
{
if( length > ENCODED_DBXKEYID_SIZE )
length = ENCODED_DBXKEYID_SIZE;
memcpy( certID, certData, length );
certIDlength = length;
}
}
if( cryptStatusError( status ) )
return( status );
/* Find out whether this was a certificate update by checking whether it
was added as a standard or renewal request, then set the update type
appropriately. The comparison for the action type is a bit odd since
some back-ends will return the action as text and some as a binary
numeric value, rather than relying on the back-end glue code to
perform the appropriate conversion we just check for either value
type */
initBoundData( boundDataPtr );
setBoundData( boundDataPtr, 0, certID, certIDlength );
status = dbmsQuery(
"SELECT action FROM certLog WHERE certID = ?",
certData, MAX_QUERY_RESULT_SIZE, &length,
boundDataPtr, DBMS_CACHEDQUERY_NONE,
DBMS_QUERY_NORMAL );
if( cryptStatusError( status ) )
return( status );
switch( certData[ 0 ] )
{
case CRYPT_CERTACTION_REQUEST_CERT:
case TEXTCH_CERTACTION_REQUEST_CERT:
*issueType = CERTADD_PARTIAL;
return( CRYPT_OK );
case CRYPT_CERTACTION_REQUEST_RENEWAL:
case TEXTCH_CERTACTION_REQUEST_RENEWAL:
*issueType = CERTADD_PARTIAL_RENEWAL;
return( CRYPT_OK );
default:
assert( DEBUG_WARN );
}
return( CRYPT_ERROR_NOTFOUND );
}
/* Sanitise a new certificate of potentially dangerous attributes by
creating a template of the disallowed values and setting them as blocked
attributes. For our use we clear all CA and CA-equivalent attributes to
prevent users from submitting requests that turn them into CAs */
CHECK_RETVAL \
static int sanitiseCertAttributes( IN_HANDLE const CRYPT_CERTIFICATE iCertificate )
{
CRYPT_CERTIFICATE iTemplateCertificate;
MESSAGE_CREATEOBJECT_INFO createInfo;
int value, status;
REQUIRES( isHandleRangeValid( iCertificate ) );
setMessageCreateObjectInfo( &createInfo, CRYPT_CERTTYPE_CERTIFICATE );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
&createInfo, OBJECT_TYPE_CERTIFICATE );
if( cryptStatusError( status ) )
return( status );
iTemplateCertificate = createInfo.cryptHandle;
/* Add as disallowed values the CA flag, CA-equivalent values (in this
case the old Netscape usage flags, which (incredibly) are still used
today by some CAs in place of the X.509 keyUsage extension), and the
CA keyUsages */
status = krnlSendMessage( iTemplateCertificate, IMESSAGE_SETATTRIBUTE,
MESSAGE_VALUE_TRUE, CRYPT_CERTINFO_CA );
if( cryptStatusOK( status ) )
{
value = CRYPT_NS_CERTTYPE_SSLCA | CRYPT_NS_CERTTYPE_SMIMECA | \
CRYPT_NS_CERTTYPE_OBJECTSIGNINGCA;
status = krnlSendMessage( iTemplateCertificate, IMESSAGE_SETATTRIBUTE,
&value, CRYPT_CERTINFO_NS_CERTTYPE );
}
if( cryptStatusOK( status ) )
{
value = CRYPT_KEYUSAGE_KEYCERTSIGN | CRYPT_KEYUSAGE_CRLSIGN;
status = krnlSendMessage( iTemplateCertificate, IMESSAGE_SETATTRIBUTE,
&value, CRYPT_CERTINFO_KEYUSAGE );
}
if( cryptStatusOK( status ) )
status = krnlSendMessage( iCertificate, IMESSAGE_SETATTRIBUTE,
( void * ) &iTemplateCertificate,
CRYPT_IATTRIBUTE_BLOCKEDATTRS );
if( status == CRYPT_ERROR_INVALID )
{
/* If the request would have resulted in the creation of an invalid
certificate, report it as an error with the request */
status = CAMGMT_ARGERROR_REQUEST;
}
krnlSendNotifier( iTemplateCertificate, IMESSAGE_DECREFCOUNT );
return( status );
}
/* Make sure that an about-to-be-issued certificate hasn't been added to the
certificate store yet. In theory we wouldn't need to do this since the
keyID uniqueness constraint will catch duplicates, however duplicates are
allowed for updates and won't automatically be caught for partial adds
because the keyID has to be added in a special form to enable the
completion of the partial add to work. What we therefore need to check
for is that a partial add (which will add the keyID in special form)
won't in the future clash with a keyID in standard form. The checking
for a keyID clash in special form happens automagically through the
uniqueness constraint.
There are two special cases in which the issue can fail during the
completion rather than initial add phase, one is during an update (which
can't be avoided, since clashes are legal for this and we can't resolve
things until the completion phase) and the other is through a race
condition caused by the following sequence of updates:
1: check keyID -> OK
2: check keyID -> OK
1: add as ESC1+keyID
1: issue as keyID
2: add as ESC1+keyID
2: issue -> fails
This condition will be fairly rare. Note that in neither case are the
integrity constraints of the certificate issuing process violated, the
only thing that happens is that a failure due to duplicates is detected
at a later stage than it normally would be */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int checkDuplicateAdd( INOUT DBMS_INFO *dbmsInfo,
IN_HANDLE const CRYPT_CERTIFICATE iLocalCertificate,
IN_ENUM( CERTADD ) const CERTADD_TYPE issueType )
{
BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
char keyID[ ENCODED_DBXKEYID_SIZE + 8 ];
int keyIDlength, status;
assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
REQUIRES( isHandleRangeValid( iLocalCertificate ) );
REQUIRES( issueType > CERTADD_NONE && issueType < CERTADD_LAST );
/* If it's a normal certificate issue then there's no problem with the
potential presence of pseudo-duplicates */
if( issueType != CERTADD_PARTIAL )
return( CRYPT_OK );
/* Check whether a certificate with this keyID is already present in the
store */
status = getCertKeyID( keyID, ENCODED_DBXKEYID_SIZE, &keyIDlength,
iLocalCertificate );
if( cryptStatusError( status ) )
return( status );
initBoundData( boundDataPtr );
setBoundData( boundDataPtr, 0, keyID, keyIDlength );
status = dbmsQuery( \
"SELECT certData FROM certificates WHERE keyID = ?",
NULL, 0, NULL, boundDataPtr,
DBMS_CACHEDQUERY_NONE, DBMS_QUERY_CHECK );
resetErrorInfo( dbmsInfo );
return( cryptStatusOK( status ) ? CRYPT_ERROR_DUPLICATE : CRYPT_OK );
}
/* Replace one certificate (usually a partially-issued one) with another
(usually its completed form). The types of operations and their
corresponding add-type values are:
ESC1 -> std CERTADD_PARTIAL Completion of partial
ESC1 -> ESC2 CERTADD_PARTIAL_RENEWAL First half of renewal
ESC2 -> std CERTADD_RENEWAL_COMPLETE Second half of renewal */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int completeCert( INOUT DBMS_INFO *dbmsInfo,
IN_HANDLE const CRYPT_CERTIFICATE iCertificate,
IN_ENUM( CERTADD ) const CERTADD_TYPE addType,
INOUT ERROR_INFO *errorInfo )
{
BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
int certIDlength, status;
assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
REQUIRES( isHandleRangeValid( iCertificate ) );
REQUIRES( addType == CERTADD_PARTIAL || \
addType == CERTADD_PARTIAL_RENEWAL || \
addType == CERTADD_RENEWAL_COMPLETE );
REQUIRES( errorInfo != NULL );
status = getKeyID( certID, ENCODED_DBXKEYID_SIZE, &certIDlength,
iCertificate, CRYPT_CERTINFO_FINGERPRINT_SHA );
if( cryptStatusError( status ) )
return( status );
status = addCert( dbmsInfo, iCertificate, CRYPT_CERTTYPE_CERTIFICATE,
( addType == CERTADD_PARTIAL_RENEWAL ) ? \
CERTADD_PARTIAL_RENEWAL : CERTADD_NORMAL,
DBMS_UPDATE_BEGIN, errorInfo );
if( cryptStatusOK( status ) )
{
char specialCertID[ ENCODED_DBXKEYID_SIZE + 8 ];
/* Turn the general certID into the form required for special-case
certificate data */
memcpy( specialCertID, certID, certIDlength );
memcpy( specialCertID,
( addType == CERTADD_RENEWAL_COMPLETE ) ? \
KEYID_ESC2 : KEYID_ESC1, KEYID_ESC_SIZE );
initBoundData( boundDataPtr );
setBoundData( boundDataPtr, 0, specialCertID, certIDlength );
status = dbmsUpdate(
"DELETE FROM certificates WHERE certID = ?",
boundDataPtr,
( addType == CERTADD_PARTIAL_RENEWAL ) ? \
DBMS_UPDATE_COMMIT : DBMS_UPDATE_CONTINUE );
}
if( cryptStatusOK( status ) )
{
if( addType != CERTADD_PARTIAL_RENEWAL )
{
status = updateCertLog( dbmsInfo,
CRYPT_CERTACTION_CERT_CREATION_COMPLETE,
NULL, 0, NULL, 0, certID, certIDlength,
NULL, 0, DBMS_UPDATE_COMMIT );
}
}
else
{
/* Something went wrong, abort the transaction */
dbmsUpdate( NULL, NULL, DBMS_UPDATE_ABORT );
}
/* If the operation failed, record the details */
if( cryptStatusError( status ) )
{
updateCertErrorLog( dbmsInfo, status,
"Certificate creation - completion operation "
"failed", NULL, 0, NULL, 0,
certID, certIDlength, NULL, 0 );
retExtErr( status,
( status, errorInfo, getDbmsErrorInfo( dbmsInfo ),
"Certificate creation - completion operation "
"failed: " ) );
}
return( CRYPT_OK );
}
/****************************************************************************
* *
* Certificate Issue Functions *
* *
****************************************************************************/
/* Complete a certificate renewal operation by revoking the certificate to
be replaced and replacing it with the newly-issued certificate */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
int completeCertRenewal( INOUT DBMS_INFO *dbmsInfo,
IN_HANDLE const CRYPT_CERTIFICATE iReplaceCertificate,
INOUT ERROR_INFO *errorInfo )
{
CRYPT_CERTIFICATE iOrigCertificate = DUMMY_INIT;
char keyID[ ENCODED_DBXKEYID_SIZE + 8 ];
int keyIDlength, dummy, status;
assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
REQUIRES( isHandleRangeValid( iReplaceCertificate ) );
REQUIRES( errorInfo != NULL );
/* Extract the key ID from the new certificate and use it to fetch the
existing certificate issued for the same key */
status = getCertKeyID( keyID, ENCODED_DBXKEYID_SIZE, &keyIDlength,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -