/**************************************************************************** * * * cryptlib DBMS CA Certificate Misc Interface * * Copyright Peter Gutmann 1996-2007 * * * ****************************************************************************/ #include /* For snprintf() */ #if defined( INC_ALL ) #include "crypt.h" #include "asn1.h" #include "dbms.h" #include "keyset.h" #else #include "crypt.h" #include "enc_dec/asn1.h" #include "keyset/dbms.h" #include "keyset/keyset.h" #endif /* Compiler-specific includes */ #ifdef USE_DBMS /**************************************************************************** * * * Utility Functions * * * ****************************************************************************/ #if 0 /* Get the ultimate successor certificate for one that's been superseded */ CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \ static int getSuccessorCert( INOUT DBMS_INFO *dbmsInfo, OUT_HANDLE_OPT CRYPT_CERTIFICATE *iCertificate, IN_BUFFER( initialCertIDlength ) \ const char *initialCertID, IN_LENGTH_SHORT const int initialCertIDlength ) { char certID[ ENCODED_DBXKEYID_SIZE + 8 ]; int chainingLevel, status; assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) ); assert( isWritePtr( *iCertificate, sizeof( CRYPT_CERTIFICATE ) ) ); assert( isReadPtr( initialCertID, initialCertIDlength ) ); /* Walk through the chain of renewals in the certificate store log until we find the ultimate successor certificate to the current one */ memcpy( certID, initialCertID, initialCertIDlength ); for( chainingLevel = 0, status = CRYPT_ERROR_NOTFOUND; status == CRYPT_ERROR_NOTFOUND && \ chainingLevel < FAILSAFE_ITERATIONS_MED; chainingLevel++ ) { BYTE keyCertID[ DBXKEYID_SIZE + 8 ]; char certData[ MAX_QUERY_RESULT_SIZE + 8 ]; int certDataLength, length, dummy; /* Find the request to renew this certificate */ status = dbmsQuery( "SELECT certID FROM certLog WHERE subjCertID = ? " "AND action = " TEXT_CERTACTION_REQUEST_RENEWAL, certData, &certDataLength, certID, strlen( certID ), 0, DBMS_CACHEDQUERY_NONE, DBMS_QUERY_NORMAL ); if( cryptStatusError( status ) ) return( status ); /* Find the resulting certificate */ memcpy( certID, certData, min( certDataLength, ENCODED_DBXKEYID_SIZE + 1 ) ); certID[ MAX_ENCODED_DBXKEYID_SIZE ] = '\0'; status = dbmsQuery( "SELECT certID FROM certLog WHERE reqCertID = ? " "AND action = " TEXT_CERTACTION_CERT_CREATION, certData, &certDataLength, certID, strlen( certID ), 0, DBMS_CACHEDQUERY_NONE, DBMS_QUERY_NORMAL ); if( cryptStatusOK( status ) ) { status = length = \ base64decode( keyCertID, certData, min( certDataLength, ENCODED_DBXKEYID_SIZE ), CRYPT_CERTFORMAT_NONE ); assert( !cryptStatusError( status ) ); } if( cryptStatusError( status ) ) return( status ); /* Try and get the replacement certificate */ status = getItemData( dbmsInfo, iCertificate, &dummy, getKeyName( CRYPT_IKEYID_CERTID ), keyCertID, length, KEYMGMT_ITEM_PUBLICKEY, KEYMGMT_FLAG_NONE, errorInfo ); } if( chainingLevel >= FAILSAFE_ITERATIONS_MED ) { /* We've chained through too many entries, bail out */ return( CRYPT_ERROR_OVERFLOW ); } return( status ); } #endif /* 0 */ /**************************************************************************** * * * Logging Functions * * * ****************************************************************************/ /* Add an entry to the CA log */ RETVAL STDC_NONNULL_ARG( ( 1 ) ) \ int updateCertLog( INOUT DBMS_INFO *dbmsInfo, IN_ENUM( CRYPT_CERTACTION ) const CRYPT_CERTACTION_TYPE action, IN_BUFFER_OPT( certIDlength ) const char *certID, IN_LENGTH_SHORT_Z const int certIDlength, IN_BUFFER_OPT( reqCertIDlength ) const char *reqCertID, IN_LENGTH_SHORT_Z const int reqCertIDlength, IN_BUFFER_OPT( subjCertIDlength ) const char *subjCertID, IN_LENGTH_SHORT_Z const int subjCertIDlength, IN_BUFFER_OPT( dataLength ) const void *data, IN_LENGTH_SHORT_Z const int dataLength, IN_ENUM( DBMS_UPDATE ) const DBMS_UPDATE_TYPE updateType ) { BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData; char sqlBuffer[ MAX_SQL_QUERY_SIZE + 8 ]; char certIDbuffer[ ENCODED_DBXKEYID_SIZE + 8 ]; char encodedCertData[ MAX_ENCODED_CERT_SIZE + 8 ]; const time_t boundDate = getApproxTime(); int localCertIDlength = certIDlength, sqlOffset, sqlLength, boundDataIndex; assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) ); assert( ( certID == NULL && certIDlength == 0 ) || \ isReadPtr( certID, certIDlength ) ); assert( ( reqCertID == NULL && reqCertIDlength == 0 ) || \ isReadPtr( reqCertID, reqCertIDlength ) ); assert( ( subjCertID == NULL && subjCertIDlength == 0 ) || \ isReadPtr( subjCertID, subjCertIDlength ) ); assert( ( data == NULL && dataLength == 0 ) || \ isReadPtr( data, dataLength ) ); REQUIRES( action > CRYPT_CERTACTION_NONE && \ action < CRYPT_CERTACTION_LAST ); REQUIRES( ( certID == NULL && certIDlength == 0 ) || \ ( certID != NULL && \ certIDlength > 0 && \ certIDlength < MAX_INTLENGTH_SHORT ) ); REQUIRES( ( reqCertID == NULL && reqCertIDlength == 0 ) || \ ( reqCertID != NULL && \ reqCertIDlength > 0 && \ reqCertIDlength < MAX_INTLENGTH_SHORT ) ); REQUIRES( ( subjCertID == NULL && subjCertIDlength == 0 ) || \ ( subjCertID != NULL && \ subjCertIDlength > 0 && \ subjCertIDlength < MAX_INTLENGTH_SHORT ) ); REQUIRES( ( data == NULL && dataLength == 0 ) || \ ( data != NULL && \ dataLength > 0 && \ dataLength < MAX_INTLENGTH_SHORT ) ); REQUIRES( updateType > DBMS_UPDATE_NONE && \ updateType < DBMS_UPDATE_LAST ); /* Build up the necessary SQL format string required to insert the log entry. This is complicated somewhat by the fact that some of the values may be NULL so we have to insert them by naming the columns (some databases allow the use of the DEFAULT keyword but this isn't standardised enough to be safe) */ strlcpy_s( sqlBuffer, MAX_SQL_QUERY_SIZE, "INSERT INTO certLog (action, actionTime, certID" ); if( reqCertID != NULL ) strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ", reqCertID" ); if( subjCertID != NULL ) strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ", subjCertID" ); if( data != NULL ) strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ", certData" ); strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ") VALUES (" ); sqlOffset = strlen( sqlBuffer ); sqlLength = MAX_SQL_QUERY_SIZE - sqlOffset; sprintf_s( sqlBuffer + sqlOffset, sqlLength, "%d, ?, ?", action ); if( reqCertID != NULL ) strlcat_s( sqlBuffer + sqlOffset, sqlLength, ", ?" ); if( subjCertID != NULL ) strlcat_s( sqlBuffer + sqlOffset, sqlLength, ", ?" ); if( data != NULL ) strlcat_s( sqlBuffer + sqlOffset, sqlLength, ", ?" ); strlcat_s( sqlBuffer + sqlOffset, sqlLength, ")" ); /* If we're not worried about the certID we just insert a nonce value which is used to meet the constraints for a unique entry. In order to ensure that it doesn't clash with a real certID we set the first four characters to an out-of-band value */ if( certID == NULL ) { MESSAGE_DATA msgData; BYTE nonce[ KEYID_SIZE + 8 ]; int status; setMessageData( &msgData, nonce, KEYID_SIZE ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE ); if( cryptStatusOK( status ) ) status = base64encode( certIDbuffer, ENCODED_DBXKEYID_SIZE, &localCertIDlength, nonce, DBXKEYID_SIZE, CRYPT_CERTTYPE_NONE ); if( cryptStatusError( status ) ) { /* Normally this is a should-never-occur error, however if cryptlib has been shut down from another thread the kernel will fail all non shutdown-related calls with a permission error. To avoid false alarms, we mask out failures due to permission errors */ assert( ( status == CRYPT_ERROR_PERMISSION ) || DEBUG_WARN ); return( status ); } memset( certIDbuffer, '-', 4 ); certID = certIDbuffer; } /* Set up the parameter information and update the certificate store log */ initBoundData( boundDataPtr ); setBoundDataDate( boundDataPtr, 0, &boundDate ); setBoundData( boundDataPtr, 1, certID, localCertIDlength ); boundDataIndex = 2; if( reqCertID != NULL ) setBoundData( boundDataPtr, boundDataIndex++, reqCertID, reqCertIDlength ); if( subjCertID != NULL ) setBoundData( boundDataPtr, boundDataIndex++, subjCertID, subjCertIDlength ); if( data != NULL ) { if( hasBinaryBlobs( dbmsInfo ) ) { setBoundDataBlob( boundDataPtr, boundDataIndex, data, dataLength ); } else { int encodedDataLength, status; status = base64encode( encodedCertData, MAX_ENCODED_CERT_SIZE, &encodedDataLength, data, dataLength, CRYPT_CERTTYPE_NONE ); if( cryptStatusError( status ) ) { DEBUG_DIAG(( "Couldn't base64-encode data" )); assert( DEBUG_WARN ); return( status ); } setBoundData( boundDataPtr, boundDataIndex, encodedCertData, encodedDataLength ); } } return( dbmsUpdate( sqlBuffer, boundDataPtr, updateType ) ); } RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \ int updateCertErrorLog( INOUT DBMS_INFO *dbmsInfo, IN_ERROR const int errorStatus, IN_STRING const char *errorString, IN_BUFFER_OPT( certIDlength ) const char *certID, IN_LENGTH_SHORT_Z const int certIDlength, IN_BUFFER_OPT( reqCertIDlength ) const char *reqCertID, IN_LENGTH_SHORT_Z const int reqCertIDlength, IN_BUFFER_OPT( subjCertIDlength ) const char *subjCertID, IN_LENGTH_SHORT_Z const int subjCertIDlength, IN_BUFFER_OPT( dataLength ) const void *data, IN_LENGTH_SHORT_Z const int dataLength ) { STREAM stream; BYTE errorData[ 64 + MAX_CERT_SIZE + 8 ]; const int errorStringLength = strlen( errorString ); int errorDataLength DUMMY_INIT, status; assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) ); assert( ( certID == NULL && certIDlength == 0 ) || \ isReadPtr( certID, certIDlength ) ); assert( ( reqCertID == NULL && reqCertIDlength == 0 ) || \ isReadPtr( reqCertID, reqCertIDlength ) ); assert( ( subjCertID == NULL && subjCertIDlength == 0 ) || \ isReadPtr( subjCertID, subjCertIDlength ) ); assert( ( data == NULL && dataLength == 0 ) || \ isReadPtr( data, dataLength ) ); ANALYSER_HINT_STRING( errorString ); REQUIRES( cryptStatusError( errorStatus ) ); REQUIRES( errorString != NULL ); REQUIRES( ( certID == NULL && certIDlength == 0 ) || \ ( certID != NULL && \ certIDlength > 0 && \ certIDlength < MAX_INTLENGTH_SHORT ) ); REQUIRES( ( reqCertID == NULL && reqCertIDlength == 0 ) || \ ( reqCertID != NULL && \ reqCertIDlength > 0 && \ reqCertIDlength < MAX_INTLENGTH_SHORT ) ); REQUIRES( ( subjCertID == NULL && subjCertIDlength == 0 ) || \ ( subjCertID != NULL && \ subjCertIDlength > 0 && \ subjCertIDlength < MAX_INTLENGTH_SHORT ) ); REQUIRES( ( data == NULL && dataLength == 0 ) || \ ( data != NULL && \ dataLength > 0 && \ dataLength < MAX_INTLENGTH_SHORT ) ); /* Encode the error information: SEQUENCE { errorStatus INTEGER, errorString UTF8String, certData ANY OPTIONAL } Note that the buffer we use is slightly larger than MAX_CERT_SIZE in order to accomodate the error status information alongside the largest possible certificate, in theory this means that if the database back-end doesn't support binary blobs there won't be enough room in the logging code to text-encode this worst-case scenario, but the use of non-binary-blob capable database should be fairly rare so it's easier to just rely on the logging code to catch this unlikely scenario than to try and special-case around it */ sMemOpen( &stream, errorData, 64 + MAX_CERT_SIZE ); writeSequence( &stream, sizeofShortInteger( -errorStatus ) + \ ( int ) sizeofObject( errorStringLength ) + \ dataLength ); writeShortInteger( &stream, -errorStatus, DEFAULT_TAG ); status = writeCharacterString( &stream, errorString, errorStringLength, BER_STRING_UTF8 ); if( dataLength > 0 ) status = swrite( &stream, data, dataLength ); if( cryptStatusOK( status ) ) errorDataLength = stell( &stream ); sMemDisconnect( &stream ); if( cryptStatusError( status ) ) { sMemOpen( &stream, errorData, MAX_CERT_SIZE ); writeSequence( &stream, sizeofObject( 1 ) + sizeofObject( 31 ) ); writeShortInteger( &stream, -( CRYPT_ERROR_FAILED ), DEFAULT_TAG ); status = writeCharacterString( &stream, "Error writing error information", 31, BER_STRING_UTF8 ); if( cryptStatusOK( status ) ) errorDataLength = stell( &stream ); sMemDisconnect( &stream ); } ENSURES( cryptStatusOK( status ) ); /* Update the certificate store log with the error information as the data value */ return( updateCertLog( dbmsInfo, CRYPT_CERTACTION_ERROR, certID, certIDlength, reqCertID, reqCertIDlength, subjCertID, subjCertIDlength, errorData, errorDataLength, DBMS_UPDATE_NORMAL ) ); } RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \ int updateCertErrorLogMsg( INOUT DBMS_INFO *dbmsInfo, IN_ERROR const int errorStatus, IN_STRING const char *errorString ) { assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) ); return( updateCertErrorLog( dbmsInfo, errorStatus, errorString, NULL, 0, NULL, 0, NULL, 0, NULL, 0 ) ); } /**************************************************************************** * * * Miscellaneous CA Functions * * * ****************************************************************************/ /* Get the PKI user that originally authorised the issuance of a certificate. This can involve chaining back through multiple generations of certificates, for example to check authorisation on a revocation request we might have to go through: rev_req: get reqCertID = update_req update_req: get reqCertID = cert_req cert_req: get reqCertID = init_req init_req: get reqCertID = pki_user */ CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 5 ) ) \ int caGetIssuingUser( INOUT DBMS_INFO *dbmsInfo, OUT_HANDLE_OPT CRYPT_CERTIFICATE *iPkiUser, IN_BUFFER( initialCertIDlength ) const char *initialCertID, IN_LENGTH_FIXED( ENCODED_DBXKEYID_SIZE ) \ const int initialCertIDlength, INOUT ERROR_INFO *errorInfo ) { char certID[ ENCODED_DBXKEYID_SIZE + 8 ]; int certIDlength, chainingLevel, dummy, status; assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) ); assert( isWritePtr( iPkiUser, sizeof( CRYPT_CERTIFICATE ) ) ); assert( isReadPtr( initialCertID, ENCODED_DBXKEYID_SIZE ) ); REQUIRES( initialCertIDlength == ENCODED_DBXKEYID_SIZE ); REQUIRES( errorInfo != NULL ); /* Clear return value */ *iPkiUser = CRYPT_ERROR; /* Walk through the chain of updates in the certificate store log until we find the PKI user that authorised the first certificate issue */ memcpy( certID, initialCertID, initialCertIDlength ); certIDlength = initialCertIDlength; for( chainingLevel = 0; chainingLevel < FAILSAFE_ITERATIONS_MED; chainingLevel++ ) { BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData; BYTE certData[ MAX_QUERY_RESULT_SIZE + 8 ]; int certDataLength; /* Find out whether this is a PKI user. 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, &certDataLength, boundDataPtr, DBMS_CACHEDQUERY_NONE, DBMS_QUERY_NORMAL ); if( cryptStatusError( status ) ) return( status ); if( certData[ 0 ] == CRYPT_CERTACTION_ADDUSER || \ certData[ 0 ] == TEXTCH_CERTACTION_ADDUSER ) { /* We've found the PKI user, we're done */ break; } /* Find the certificate that was issued, recorded either as a CERTACTION_CERT_CREATION for a multi-phase CMP-based certificate creation or a CERTACTION_ISSUE_CERT for a one-step creation */ status = dbmsQuery( "SELECT reqCertID FROM certLog WHERE certID = ?", certData, MAX_QUERY_RESULT_SIZE, &certDataLength, boundDataPtr, DBMS_CACHEDQUERY_NONE, DBMS_QUERY_NORMAL ); if( cryptStatusError( status ) ) return( status ); certIDlength = min( certDataLength, ENCODED_DBXKEYID_SIZE ); memcpy( certID, certData, certIDlength ); /* Find the request to issue this certificate. For a CMP-based issue this will have an authorising object (found in the next iteration through the loop), for a one-step issue it won't */ initBoundData( boundDataPtr ); setBoundData( boundDataPtr, 0, certID, certIDlength ); status = dbmsQuery( "SELECT reqCertID FROM certLog WHERE certID = ?", certData, MAX_QUERY_RESULT_SIZE, &certDataLength, boundDataPtr, DBMS_CACHEDQUERY_NONE, DBMS_QUERY_NORMAL ); if( cryptStatusError( status ) ) return( status ); certIDlength = min( certDataLength, ENCODED_DBXKEYID_SIZE ); memcpy( certID, certData, certIDlength ); } if( chainingLevel >= FAILSAFE_ITERATIONS_MED ) { /* We've chained through too many entries, bail out */ return( CRYPT_ERROR_OVERFLOW ); } /* We've found the original PKI user, get the user information */ return( getItemData( dbmsInfo, iPkiUser, &dummy, KEYMGMT_ITEM_PKIUSER, CRYPT_IKEYID_CERTID, certID, certIDlength, KEYMGMT_FLAG_NONE, errorInfo ) ); } /**************************************************************************** * * * CA Certificate Management Interface * * * ****************************************************************************/ /* Perform a certificate management operation */ CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \ static int certMgmtFunction( INOUT KEYSET_INFO *keysetInfoPtr, OUT_OPT_HANDLE_OPT CRYPT_CERTIFICATE *iCertificate, IN_HANDLE_OPT const CRYPT_CERTIFICATE caKey, IN_HANDLE_OPT const CRYPT_CERTIFICATE request, IN_ENUM( CRYPT_CERTACTION ) \ const CRYPT_CERTACTION_TYPE action ) { BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData; DBMS_INFO *dbmsInfo = keysetInfoPtr->keysetDBMS; char reqCertID[ ENCODED_DBXKEYID_SIZE + 8 ]; int reqCertIDlength, status; assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) ); assert( ( iCertificate == NULL ) || \ isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) ); REQUIRES( keysetInfoPtr->type == KEYSET_DBMS ); REQUIRES( ( action != CRYPT_CERTACTION_ISSUE_CRL && action != CRYPT_CERTACTION_CERT_CREATION ) || \ ( ( action == CRYPT_CERTACTION_ISSUE_CRL || \ action == CRYPT_CERTACTION_CERT_CREATION ) && \ iCertificate != NULL ) ); /* An ISSUE_CERT can have iCertificate == NULL, which leaves the issued certificate in the store without returning it to the caller */ REQUIRES( ( caKey == CRYPT_UNUSED ) || isHandleRangeValid( caKey ) ); REQUIRES( ( request == CRYPT_UNUSED ) || isHandleRangeValid( request ) ); REQUIRES( action > CRYPT_CERTACTION_NONE && \ action < CRYPT_CERTACTION_LAST ); /* In order for various SQL query strings to use the correct values the type values have to match their text equivalents defined at the start of this file. Since we can't check this at compile time we have to do it here via an assertion */ static_assert_opt( TEXT_CERTTYPE_REQUEST_CERT[ 0 ] - '0' == \ CRYPT_CERTTYPE_REQUEST_CERT, \ "SQL data type" ); static_assert_opt( TEXT_CERTTYPE_REQUEST_REVOCATION[ 0 ] - '0' == \ CRYPT_CERTTYPE_REQUEST_REVOCATION, \ "SQL data type" ); static_assert_opt( TEXT_CERTACTION_CREATE[ 0 ] - '0' == \ CRYPT_CERTACTION_CREATE, \ "SQL data type" ); static_assert( TEXTCH_CERTACTION_ADDUSER - '0' == \ CRYPT_CERTACTION_ADDUSER, \ "SQL data type" ); static_assert_opt( TEXT_CERTACTION_REQUEST_CERT[ 0 ] - '0' == \ CRYPT_CERTACTION_REQUEST_CERT, \ "SQL data type" ); static_assert( TEXTCH_CERTACTION_REQUEST_CERT - '0' == \ CRYPT_CERTACTION_REQUEST_CERT, \ "SQL data type" ); static_assert_opt( TEXT_CERTACTION_REQUEST_RENEWAL[ 0 ] - '0' == \ CRYPT_CERTACTION_REQUEST_RENEWAL, \ "SQL data type" ); static_assert( TEXTCH_CERTACTION_REQUEST_RENEWAL - '0' == \ CRYPT_CERTACTION_REQUEST_RENEWAL, \ "SQL data type" ); static_assert_opt( TEXT_CERTACTION_CERT_CREATION[ 0 ] - '0' == \ CRYPT_CERTACTION_CERT_CREATION / 10, \ "SQL data type" ); static_assert_opt( TEXT_CERTACTION_CERT_CREATION[ 1 ] - '0' == \ CRYPT_CERTACTION_CERT_CREATION % 10, \ "SQL data type" ); /* Clear return value */ if( iCertificate != NULL ) *iCertificate = CRYPT_ERROR; /* If it's a simple certificate expire or cleanup, there are no parameters to check so we can perform the action immediately */ if( action == CRYPT_CERTACTION_EXPIRE_CERT || \ action == CRYPT_CERTACTION_CLEANUP ) { REQUIRES( caKey == CRYPT_UNUSED ); REQUIRES( request == CRYPT_UNUSED ); return( caCleanup( dbmsInfo, action, KEYSET_ERRINFO ) ); } /* If it's the completion of a certificate creation, process it */ if( action == CRYPT_CERTACTION_CERT_CREATION_COMPLETE || \ action == CRYPT_CERTACTION_CERT_CREATION_DROP || \ action == CRYPT_CERTACTION_CERT_CREATION_REVERSE ) { REQUIRES( caKey == CRYPT_UNUSED ); REQUIRES( isHandleRangeValid( request ) ); return( caIssueCertComplete( dbmsInfo, request, action, KEYSET_ERRINFO ) ); } /* Check that the CA key that we've been passed is in order. These checks are performed automatically during the issue process by the kernel when we try and convert the request into a certificate, however we perform them explicitly here so that we can return a more meaningful error message to the caller */ if( action == CRYPT_CERTACTION_ISSUE_CRL ) { int value; REQUIRES( isHandleRangeValid( caKey ) ); /* If we're issuing a CRL, the key must be capable of CRL signing */ status = krnlSendMessage( caKey, IMESSAGE_GETATTRIBUTE, &value, CRYPT_CERTINFO_KEYUSAGE ); if( cryptStatusError( status ) || \ !( value & CRYPT_KEYUSAGE_CRLSIGN ) ) { retExtArg( CAMGMT_ARGERROR_CAKEY, ( CAMGMT_ARGERROR_CAKEY, KEYSET_ERRINFO, "CA certificate isn't valid for CRL signing" ) ); } } else { /* For anything other than a revocation action (which just updates the certificate store without doing anything else) the key must be a CA key */ if( action != CRYPT_CERTACTION_REVOKE_CERT ) { REQUIRES( isHandleRangeValid( caKey ) ); status = krnlSendMessage( caKey, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_CA ); if( cryptStatusError( status ) ) { retExtArg( CAMGMT_ARGERROR_CAKEY, ( CAMGMT_ARGERROR_CAKEY, KEYSET_ERRINFO, "CA certificate isn't valid for certificate " "signing" ) ); } } } /* If it's a CRL issue it's a read-only operation on the CRL store for which we only need the CA certificate (there's no request involved) */ if( action == CRYPT_CERTACTION_ISSUE_CRL ) { REQUIRES( isHandleRangeValid( caKey ) ); REQUIRES( request == CRYPT_UNUSED ); return( caIssueCRL( dbmsInfo, iCertificate, caKey, KEYSET_ERRINFO ) ); } /* Beyond this point we need to be working with a valid request */ REQUIRES( isHandleRangeValid( request ) ); /* We're processing an action that requires an explicit certificate request, perform further checks on the request */ if( !checkRequest( request, action ) ) { retExtArg( CAMGMT_ARGERROR_REQUEST, ( CAMGMT_ARGERROR_REQUEST, KEYSET_ERRINFO, "Certificate request information " "inconsistent/invalid" ) ); } /* Make sure that the request is present in the request table in order to issue a certificate for it. Again, this will be checked later but we can return a more meaningful error here */ status = getKeyID( reqCertID, ENCODED_DBXKEYID_SIZE, &reqCertIDlength, request, CRYPT_CERTINFO_FINGERPRINT_SHA1 ); if( cryptStatusError( status ) ) return( CAMGMT_ARGERROR_REQUEST ); initBoundData( boundDataPtr ); setBoundData( boundDataPtr, 0, reqCertID, reqCertIDlength ); status = dbmsQuery( "SELECT certData FROM certRequests WHERE certID = ?", NULL, 0, NULL, boundDataPtr, DBMS_CACHEDQUERY_NONE, DBMS_QUERY_CHECK ); if( cryptStatusError( status ) ) { retExt( CRYPT_ERROR_NOTFOUND, ( CRYPT_ERROR_NOTFOUND, KEYSET_ERRINFO, "Certificate request doesn't correspond to any existing " "request in the certificate store" ) ); } /* If it's a revocation request, process it */ if( action == CRYPT_CERTACTION_REVOKE_CERT ) { REQUIRES( caKey == CRYPT_UNUSED ); return( caRevokeCert( dbmsInfo, request, CRYPT_UNUSED, CRYPT_CERTACTION_REVOKE_CERT, KEYSET_ERRINFO ) ); } /* It's a certificate issue request, issue the certificate */ REQUIRES( action == CRYPT_CERTACTION_ISSUE_CERT || \ action == CRYPT_CERTACTION_CERT_CREATION ); REQUIRES( isHandleRangeValid( caKey ) ); return( caIssueCert( dbmsInfo, iCertificate, caKey, request, action, KEYSET_ERRINFO ) ); } /* Set up the function pointers to the keyset methods */ CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \ int initDBMSCA( INOUT KEYSET_INFO *keysetInfoPtr ) { assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) ); REQUIRES( keysetInfoPtr->type == KEYSET_DBMS ); keysetInfoPtr->keysetDBMS->certMgmtFunction = certMgmtFunction; return( CRYPT_OK ); } #endif /* USE_DBMS */