📄 scep.c
字号:
/****************************************************************************
* *
* cryptlib SCEP Session Management *
* Copyright Peter Gutmann 1999-2007 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "crypt.h"
#include "asn1.h"
#include "asn1_ext.h"
#include "session.h"
#include "certstore.h"
#include "scep.h"
#else
#include "crypt.h"
#include "misc/asn1.h"
#include "misc/asn1_ext.h"
#include "session/session.h"
#include "session/certstore.h"
#include "session/scep.h"
#endif /* Compiler-specific includes */
/* Prototypes for functions in pnppki.c */
CHECK_RETVAL \
int pnpPkiSession( INOUT SESSION_INFO *sessionInfoPtr ) \
STDC_NONNULL_ARG( ( 1 ) );
#ifdef USE_SCEP
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Initialise and clean up protocol info */
static void initProtocolInfo( SCEP_PROTOCOL_INFO *protocolInfo )
{
assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) );
memset( protocolInfo, 0, sizeof( SCEP_PROTOCOL_INFO ) );
protocolInfo->iScepCert = CRYPT_ERROR;
}
static void destroyProtocolInfo( SCEP_PROTOCOL_INFO *protocolInfo )
{
assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) );
if( protocolInfo->iScepCert != CRYPT_ERROR )
krnlSendNotifier( protocolInfo->iScepCert, IMESSAGE_DECREFCOUNT );
zeroise( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) );
}
/* Check that the CA's certificate can also sign and encrypt data. This is
normally a really bad idea for CA certs but is required by the SCEP
protocol */
BOOLEAN checkCACert( const CRYPT_CERTIFICATE iCaCert )
{
int status;
assert( isHandleRangeValid( iCaCert ) );
krnlSendMessage( iCaCert, IMESSAGE_SETATTRIBUTE,
MESSAGE_VALUE_CURSORFIRST,
CRYPT_CERTINFO_CURRENT_CERTIFICATE );
status = krnlSendMessage( iCaCert, IMESSAGE_CHECK, NULL,
MESSAGE_CHECK_PKC_ENCRYPT );
if( cryptStatusOK( status ) )
status = krnlSendMessage( iCaCert, IMESSAGE_CHECK, NULL,
MESSAGE_CHECK_PKC_SIGCHECK );
#if 0 /* RA certs aren't necessarily CA certs */
if( cryptStatusOK( status ) )
{
/* Make sure that it really is a CA cert */
status = krnlSendMessage( iCaCert, IMESSAGE_CHECK, NULL,
MESSAGE_CHECK_CA );
}
#endif /* 0 */
return( cryptStatusOK( status ) ? TRUE : FALSE );
}
/* Generate/check the server certificate fingerprint. Unfortunately there's
just enough protocol-specific handling in each of the different
fingerprint-handling routines that we can't use a single routine for all
of them */
int processKeyFingerprint( SESSION_INFO *sessionInfoPtr )
{
const ATTRIBUTE_LIST *fingerprintPtr = \
findSessionInfo( sessionInfoPtr->attributeList,
CRYPT_SESSINFO_SERVER_FINGERPRINT );
MESSAGE_DATA msgData;
int status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
/* Either compare the cert fingerprint to a supplied one or save it for
the caller to examine */
if( fingerprintPtr != NULL )
{
/* The caller has supplied a cert fingerprint, compare it to the
received cert's fingerprint to make sure that we're talking to
the right system */
setMessageData( &msgData, fingerprintPtr->value,
fingerprintPtr->valueLength );
status = krnlSendMessage( sessionInfoPtr->iAuthInContext,
IMESSAGE_COMPARE, &msgData,
MESSAGE_COMPARE_FINGERPRINT );
if( cryptStatusError( status ) )
{
retExt( CRYPT_ERROR_WRONGKEY,
( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO,
"Server certificate doesn't match key fingerprint" ) );
}
}
else
{
BYTE certFingerprint[ CRYPT_MAX_HASHSIZE + 8 ];
/* Remember the cert fingerprint in case the caller wants to check
it. We don't worry if the add fails, it's a minor thing and not
worth aborting the handshake for */
setMessageData( &msgData, certFingerprint, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( sessionInfoPtr->iAuthInContext,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CERTINFO_FINGERPRINT_SHA );
if( cryptStatusOK( status ) )
{
( void ) addSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_SERVER_FINGERPRINT,
certFingerprint, msgData.length );
}
}
return( CRYPT_OK );
}
/* Check that the information supplied in a request matches what's stored for
a PKI user */
static int checkPkiUserInfo( SESSION_INFO *sessionInfoPtr,
SCEP_PROTOCOL_INFO *protocolInfo )
{
const ATTRIBUTE_LIST *userNamePtr = \
findSessionInfo( sessionInfoPtr->attributeList,
CRYPT_SESSINFO_USERNAME );
MESSAGE_KEYMGMT_INFO getkeyInfo;
MESSAGE_DATA msgData;
BYTE keyIDbuffer[ 64 + 8 ], *keyIDptr = userNamePtr->value;
BYTE requestPassword[ CRYPT_MAX_TEXTSIZE + 8 ];
BYTE userPassword[ CRYPT_MAX_TEXTSIZE + 8 ];
int requestPasswordSize, userPasswordSize = DUMMY_INIT;
int keyIDsize = userNamePtr->valueLength, status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) );
/* Get the password from the PKCS #10 request */
setMessageData( &msgData, requestPassword, CRYPT_MAX_TEXTSIZE );
status = krnlSendMessage( sessionInfoPtr->iCertRequest,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CERTINFO_CHALLENGEPASSWORD );
if( cryptStatusError( status ) )
{
retExt( status,
( status, SESSION_ERRINFO,
"Couldn't get challenge password from PKCS #10 request" ) );
}
requestPasswordSize = msgData.length;
/* If it's a cryptlib encoded user ID, we need to decode it before we can
look up a PKI user with it */
if( userNamePtr->flags & ATTR_FLAG_ENCODEDVALUE )
{
status = decodePKIUserValue( keyIDbuffer, 64, &keyIDsize,
userNamePtr->value,
userNamePtr->valueLength );
keyIDptr = keyIDbuffer;
if( cryptStatusError( status ) )
retIntError();
}
/* Get the user info for the request from the cert store */
setMessageKeymgmtInfo( &getkeyInfo, CRYPT_IKEYID_KEYID, keyIDptr,
keyIDsize, NULL, 0, KEYMGMT_FLAG_NONE );
status = krnlSendMessage( sessionInfoPtr->cryptKeyset,
IMESSAGE_KEY_GETKEY, &getkeyInfo,
KEYMGMT_ITEM_PKIUSER );
if( cryptStatusError( status ) )
{
zeroise( requestPassword, CRYPT_MAX_TEXTSIZE );
retExt( status,
( status, SESSION_ERRINFO,
"Couldn't get PKI user information for requested user" ) );
}
/* Get the password from the PKI user object */
setMessageData( &msgData, userPassword, CRYPT_MAX_TEXTSIZE );
status = krnlSendMessage( getkeyInfo.cryptHandle,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CERTINFO_PKIUSER_ISSUEPASSWORD );
if( cryptStatusOK( status ) )
{
userPasswordSize = msgData.length;
status = updateSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_PASSWORD,
userPassword, userPasswordSize,
CRYPT_MAX_TEXTSIZE,
ATTR_FLAG_ENCODEDVALUE );
}
if( cryptStatusError( status ) )
{
zeroise( requestPassword, CRYPT_MAX_TEXTSIZE );
krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
retExt( status,
( status, SESSION_ERRINFO,
"Couldn't copy read PKI user data from PKI user object "
"into session object" ) );
}
/* Make sure that the password matches the one in the request */
if( userPasswordSize != requestPasswordSize || \
memcmp( userPassword, requestPassword, userPasswordSize ) )
{
zeroise( requestPassword, CRYPT_MAX_TEXTSIZE );
zeroise( userPassword, CRYPT_MAX_TEXTSIZE );
krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
retExt( status,
( status, SESSION_ERRINFO,
"Supplied password in PKCS #10 request doesn't match "
"stored PKI user password" ) );
}
zeroise( userPassword, CRYPT_MAX_TEXTSIZE );
/* If the subject only knows their CN, they may send a CN-only subject DN
in the hope that we can fill it in for them. In addition there may be
other constraints that the CA wants to apply, these are handled by
applying the PKI user info to the request */
status = krnlSendMessage( sessionInfoPtr->iCertRequest,
IMESSAGE_SETATTRIBUTE, &getkeyInfo.cryptHandle,
CRYPT_IATTRIBUTE_PKIUSERINFO );
krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
if( cryptStatusError( status ) )
{
retExt( CRYPT_ERROR_INVALID,
( CRYPT_ERROR_INVALID, SESSION_ERRINFO,
"User information in PKCS #10 request can't be "
"reconciled with stored information for the user" ) );
}
return( CRYPT_OK );
}
/* Deliver an Einladung betreff Kehrseite to the client. We don't bother
checking the return value since there's nothing that we can do in the case
of an error except close the connection, which we do anyway since this is
the last message, and we don't return extended error information since
this would overwrite the information for the error that caused us to
return an error response */
static void sendErrorResponse( SESSION_INFO *sessionInfoPtr,
SCEP_PROTOCOL_INFO *protocolInfo,
const int scepStatus )
{
CRYPT_CERTIFICATE iCmsAttributes;
int status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) );
assert( cryptStatusError( scepStatus ) );
/* Sign the error response using the CA key and SCEP attributes */
status = createScepAttributes( sessionInfoPtr, protocolInfo,
&iCmsAttributes, FALSE, scepStatus );
if( cryptStatusOK( status ) )
{
status = envelopeSign( sessionInfoPtr->receiveBuffer, 0,
sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBufSize,
&sessionInfoPtr->receiveBufEnd,
CRYPT_CONTENT_NONE, sessionInfoPtr->privateKey,
iCmsAttributes );
krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
}
if( cryptStatusError( status ) )
{
HTTP_DATA_INFO httpDataInfo;
/* If we encounter an error processing the initial request, there
won't be enough information available to create an error
response. Similarly, if we run into problems generating the
response, there won't be anything available to send. At this
point the best that we can do is send an error at the HTTP
level */
initHttpDataInfo( &httpDataInfo, sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBufSize );
httpDataInfo.reqStatus = scepStatus;
swrite( &sessionInfoPtr->stream, &httpDataInfo,
sizeof( HTTP_DATA_INFO ) );
return;
}
DEBUG_DUMP( "scep_srespx", sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBufEnd );
/* Return the response to the client, discarding any error indication
from the write. Since we're already in an error state there's not
much that we can do in terms of alerting the user if a further error
occurs when writing the error response, so we ignore any potential
write errors that occur at this point */
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_LASTMESSAGE, NULL, TRUE );
( void ) writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE,
SCEP_CONTENT_TYPE_LEN );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -