📄 cmp.c
字号:
assert( sessionInfoPtr->cmpRequestType != CRYPT_REQUESTTYPE_NONE );
assert( sessionInfoPtr->cmpRequestType == CRYPT_REQUESTTYPE_PKIBOOT || \
sessionInfoPtr->iCertRequest != CRYPT_ERROR );
assert( sessionInfoPtr->cmpRequestType == CRYPT_REQUESTTYPE_PKIBOOT || \
sessionInfoPtr->iAuthInContext != CRYPT_ERROR );
/* Initialise the client-side protocol state info */
initProtocolInfo( &protocolInfo,
sessionInfoPtr->flags & SESSION_ISCRYPTLIB );
status = initClientInfo( sessionInfoPtr, &protocolInfo );
if( cryptStatusError( status ) )
{
destroyProtocolInfo( &protocolInfo );
return( status );
}
/* Write the message into the session buffer and send it to the server */
status = writePkiMessage( sessionInfoPtr, &protocolInfo,
( sessionInfoPtr->cmpRequestType == \
CRYPT_REQUESTTYPE_PKIBOOT ) ? \
CMPBODY_GENMSG : CMPBODY_NORMAL );
if( cryptStatusOK( status ) )
{
DEBUG_DUMP_CMP( protocolInfo.operation, 1, sessionInfoPtr );
if( ( protocolInfo.operation == CTAG_PB_GENM || \
protocolInfo.operation == CTAG_PB_RR ) && \
!( sessionInfoPtr->protocolFlags & CMP_PFLAG_RETAINCONNECTION ) )
/* There's no confirmation handshake for PKIBoot or a revocation
request so we mark this as the last message if required */
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_LASTMESSAGE, NULL,
TRUE );
status = writePkiDatagram( sessionInfoPtr );
}
if( cryptStatusError( status ) )
{
destroyProtocolInfo( &protocolInfo );
return( status );
}
/* Read the server response */
status = readPkiDatagram( sessionInfoPtr );
if( cryptStatusOK( status ) )
{
DEBUG_DUMP_CMP( protocolInfo.operation, 2, sessionInfoPtr );
status = readPkiMessage( sessionInfoPtr, &protocolInfo,
reqToResp( protocolInfo.operation ) );
}
if( cryptStatusOK( status ) && protocolInfo.operation == CTAG_PB_GENM )
{
/* It's a PKIBoot, add the trusted certs. If the user wants the
setting made permanent, they need to flush the config to disk
after the session has completed */
status = krnlSendMessage( sessionInfoPtr->ownerHandle,
IMESSAGE_SETATTRIBUTE,
&sessionInfoPtr->iCertResponse,
CRYPT_IATTRIBUTE_CTL );
if( status == CRYPT_ERROR_INITED )
/* If the certs are already present, trying to add them again
isn't an error */
status = CRYPT_OK;
}
if( cryptStatusError( status ) )
{
destroyProtocolInfo( &protocolInfo );
return( status );
}
/* If it's a transaction type that doesn't need a confirmation, we're
done */
if( protocolInfo.operation == CTAG_PB_GENM || \
protocolInfo.operation == CTAG_PB_RR )
{
if( protocolInfo.iMacContext != CRYPT_ERROR )
{
/* Remember the authentication context in case we can reuse it
for another transaction */
sessionInfoPtr->cmpSavedMacContext = protocolInfo.iMacContext;
protocolInfo.iMacContext = CRYPT_ERROR;
}
destroyProtocolInfo( &protocolInfo );
return( CRYPT_OK );
}
/* Exchange confirmation data with the server */
if( !( sessionInfoPtr->protocolFlags & CMP_PFLAG_RETAINCONNECTION ) )
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_LASTMESSAGE, NULL,
TRUE );
status = writePkiMessage( sessionInfoPtr, &protocolInfo,
CMPBODY_CONFIRMATION );
if( cryptStatusOK( status ) )
{
DEBUG_DUMP_CMP( protocolInfo.operation, 3, sessionInfoPtr );
status = writePkiDatagram( sessionInfoPtr );
}
if( cryptStatusOK( status ) )
status = readPkiDatagram( sessionInfoPtr );
if( cryptStatusOK( status ) )
{
DEBUG_DUMP_CMP( protocolInfo.operation, 4, sessionInfoPtr );
status = readPkiMessage( sessionInfoPtr, &protocolInfo, CTAG_PB_PKICONF );
}
if( cryptStatusOK( status ) && protocolInfo.iMacContext != CRYPT_ERROR )
{
/* Remember the authentication context in case we can reuse it for
another transaction */
sessionInfoPtr->cmpSavedMacContext = protocolInfo.iMacContext;
protocolInfo.iMacContext = CRYPT_ERROR;
}
destroyProtocolInfo( &protocolInfo );
return( status );
}
static int clientTransactWrapper( SESSION_INFO *sessionInfoPtr )
{
if( sessionInfoPtr->flags & SESSION_ISPNPPKI )
{
int status;
/* If we're doing plug-and-play PKI, point the transaction function
at the client-transact function to execute the PnP steps, then
reset it back to the PnP wrapper after we're done */
sessionInfoPtr->transactFunction = clientTransact;
status = pnpPkiSession( sessionInfoPtr );
sessionInfoPtr->transactFunction = clientTransactWrapper;
return( status );
}
return( clientTransact( sessionInfoPtr ) );
}
static int serverTransact( SESSION_INFO *sessionInfoPtr )
{
MESSAGE_CERTMGMT_INFO certMgmtInfo;
CMP_PROTOCOL_INFO protocolInfo;
int status;
/* Initialise the server-side protocol state info. Since the server
doesn't have a user ID (it uses what the client sends it), we set the
userID-sent flag to indicate that it's been implicitly exchanged */
initProtocolInfo( &protocolInfo,
sessionInfoPtr->flags & SESSION_ISCRYPTLIB );
protocolInfo.authContext = sessionInfoPtr->privateKey;
sessionInfoPtr->protocolFlags |= CMP_PFLAG_USERIDSENT;
if( sessionInfoPtr->userNameLength > 0 )
{
/* There's already user info present from a previous transaction,
try and re-use the info from it (this can be overridden by the
client sending us new user info) */
if( sessionInfoPtr->flags & SESSION_ISENCODEDUSERID )
/* It's a cryptlib-style encoded user ID, decode it into its
binary value */
protocolInfo.userIDsize = \
decodePKIUserValue( protocolInfo.userID,
sessionInfoPtr->userName,
sessionInfoPtr->userNameLength );
else
{
/* It's a standard user ID, use it as is */
memcpy( protocolInfo.userID, sessionInfoPtr->userName,
sessionInfoPtr->userNameLength );
protocolInfo.userIDsize = sessionInfoPtr->userNameLength;
}
protocolInfo.iMacContext = sessionInfoPtr->cmpSavedMacContext;
sessionInfoPtr->cmpSavedMacContext = CRYPT_ERROR;
}
/* Read the initial message from the client. We don't write an error
response at the initial read stage to prevent scanning/DOS attacks
(vir sapit qui pauca loquitur) */
status = readPkiDatagram( sessionInfoPtr );
if( cryptStatusError( status ) )
{
destroyProtocolInfo( &protocolInfo );
return( status );
}
status = readPkiMessage( sessionInfoPtr, &protocolInfo,
CRYPT_UNUSED );
if( cryptStatusError( status ) )
{
sendErrorResponse( sessionInfoPtr, &protocolInfo, status );
destroyProtocolInfo( &protocolInfo );
return( status );
}
DEBUG_DUMP_CMP( protocolInfo.operation, 1, sessionInfoPtr );
sessionInfoPtr->cmpRequestType = reqToClibReq( protocolInfo.operation );
/* If it's a PKIBoot request, send the PKIBoot response and retry the
read unless the client closes the stream. This assumes that the
client will generally send a PKIBoot request in conjunction with a
cert management request (i.e. as part of a PnP PKI transaction),
which allows us to reuse the user authentication info to process the
request that follows the PKIBoot */
if( sessionInfoPtr->cmpRequestType == CRYPT_REQUESTTYPE_PKIBOOT )
{
int streamState;
/* Handle the PKIBoot request */
status = writePkiMessage( sessionInfoPtr, &protocolInfo,
CMPBODY_GENMSG );
if( cryptStatusOK( status ) )
{
DEBUG_DUMP_CMP( CTAG_PB_GENM, 2, sessionInfoPtr );
status = writePkiDatagram( sessionInfoPtr );
}
if( cryptStatusError( status ) )
{
sendErrorResponse( sessionInfoPtr, &protocolInfo, status );
destroyProtocolInfo( &protocolInfo );
return( status );
}
/* Check whether the client left the stream open. If they haven't,
it was a standalone PKIBoot request and we're done */
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_CONNSTATE,
&streamState, 0 );
if( !streamState )
{
destroyProtocolInfo( &protocolInfo );
return( CRYPT_OK );
}
/* Process the request that follows the PKIBoot. If the client
was only performing a standardlone PKIBoot but left the
connection open in case further transactions were necesary
later, but then shut down the connection without performing
any further transactions, we'll get a read error at this point,
which we convert into a OK status */
status = readPkiDatagram( sessionInfoPtr );
if( cryptStatusOK( status ) )
status = readPkiMessage( sessionInfoPtr, &protocolInfo,
CRYPT_UNUSED );
if( cryptStatusError( status ) )
{
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_CONNSTATE,
&streamState, 0 );
if( streamState )
/* Only send an error response if the stream is still open */
sendErrorResponse( sessionInfoPtr, &protocolInfo, status );
destroyProtocolInfo( &protocolInfo );
return( streamState ? status : CRYPT_OK );
}
}
/* Make sure that the signature on the request data is OK (unless it's a
non-signed revocation request or a request for an encryption-only
key) and add it to the cert store */
if( protocolInfo.operation != CTAG_PB_RR && !protocolInfo.cryptOnlyKey )
status = krnlSendMessage( sessionInfoPtr->iCertRequest,
IMESSAGE_CRT_SIGCHECK, NULL, CRYPT_UNUSED );
if( cryptStatusError( status ) )
strcpy( sessionInfoPtr->errorMessage,
"Request signature check failed" );
else
{
MESSAGE_KEYMGMT_INFO setkeyInfo;
setMessageKeymgmtInfo( &setkeyInfo, CRYPT_KEYID_NONE, NULL, 0, NULL, 0,
( protocolInfo.operation == CTAG_PB_KUR ) ? \
KEYMGMT_FLAG_UPDATE : KEYMGMT_FLAG_NONE );
setkeyInfo.cryptHandle = sessionInfoPtr->iCertRequest;
status = krnlSendMessage( sessionInfoPtr->cryptKeyset,
IMESSAGE_KEY_SETKEY, &setkeyInfo,
KEYMGMT_ITEM_REQUEST );
if( cryptStatusError( status ) )
strcpy( sessionInfoPtr->errorMessage,
"Request couldn't be added to cert store" );
}
if( cryptStatusError( status ) )
{
/* If the cert store reports that there's a problem with the request,
convert it to an invalid request error */
if( status == CRYPT_ARGERROR_NUM1 )
status = CRYPT_ERROR_INVALID;
sendErrorResponse( sessionInfoPtr, &protocolInfo, status );
destroyProtocolInfo( &protocolInfo );
return( status );
}
/* Create or revoke a cert from the request */
if( protocolInfo.operation != CTAG_PB_RR )
{
setMessageCertMgmtInfo( &certMgmtInfo, sessionInfoPtr->privateKey,
sessionInfoPtr->iCertRequest );
status = krnlSendMessage( sessionInfoPtr->cryptKeyset,
IMESSAGE_KEY_CERTMGMT, &certMgmtInfo,
CRYPT_CERTACTION_CERT_CREATION );
if( cryptStatusOK( status ) )
sessionInfoPtr->iCertResponse = certMgmtInfo.cryptCert;
}
else
{
setMessageCertMgmtInfo( &certMgmtInfo, CRYPT_UNUSED,
sessionInfoPtr->iCertRequest );
status = krnlSendMessage( sessionInfoPtr->cryptKeyset,
IMESSAGE_KEY_CERTMGMT, &certMgmtInfo,
CRYPT_CERTACTION_REVOKE_CERT );
}
if( cryptStatusError( status ) )
{
/* If the cert store reports that there's a problem with the request,
convert it to an invalid request error */
if( status == CRYPT_ARGERROR_NUM1 )
status = CRYPT_ERROR_INVALID;
sendErrorResponse( sessionInfoPtr, &protocolInfo, status );
destroyProtocolInfo( &protocolInfo );
retExt( sessionInfoPtr, status, "%s was denied by cert store",
( protocolInfo.operation != CTAG_PB_RR ) ? \
"Cert issue" : "Revocation" );
}
/* Send the response to the client */
status = writePkiMessage( sessionInfoPtr, &protocolInfo, CMPBODY_NORMAL );
if( cryptStatusOK( status ) )
{
DEBUG_DUMP_CMP( protocolInfo.operation, 2, sessionInfoPtr );
status = writePkiDatagram( sessionInfoPtr );
}
if( cryptStatusError( status ) )
{
sendErrorResponse( sessionInfoPtr, &protocolInfo, status );
if( protocolInfo.operation != CTAG_PB_RR )
{
/* If there was a problem, drop the partially-issued cert. We
don't have to go all the way and do a full reversal because
it hasn't really been issued yet since we couldn't get it to
the client. In addition we don't do anything with the return
status since we want to return the status that caused the
problem, not the result of the drop operation */
setMessageCertMgmtInfo( &certMgmtInfo, CRYPT_UNUSED,
sessionInfoPtr->iCertResponse );
krnlSendMessage( sessionInfoPtr->cryptKeyset,
IMESSAGE_KEY_CERTMGMT, &certMgmtInfo,
CRYPT_CERTACTION_CERT_CREATION_DROP );
}
destroyProtocolInfo( &protocolInfo );
return( status );
}
/* If it's a transaction type that doesn't need a confirmation, we're
done */
if( protocolInfo.operation == CTAG_PB_RR )
{
/* Remember the authentication context in case we can reuse it for
another transaction */
sessionInfoPtr->cmpSavedMacContext = protocolInfo.iMacContext;
protocolInfo.iMacContext = CRYPT_ERROR;
destroyProtocolInfo( &protocolInfo );
return( CRYPT_OK );
}
/* Read back the confirmation from the client */
status = readPkiDatagram( sessionInfoPtr );
if( cryptStatusOK( status ) )
status = readPkiMessage( sessionInfoPtr, &protocolInfo,
CTAG_PB_CERTCONF );
if( cryptStatusError( status ) || \
protocolInfo.status == CRYPT_ERROR )
{
int localStatus;
/* If the client rejected the cert this isn't a protocol error so we
send back a standard ack, otherwise we send back an error response */
if( protocolInfo.status == CRYPT_ERROR )
{
writePkiMessage( sessionInfoPtr, &protocolInfo, CMPBODY_ACK );
writePkiDatagram( sessionInfoPtr );
}
else
sendErrorResponse( sessionInfoPtr, &protocolInfo, status );
destroyProtocolInfo( &protocolInfo );
/* Reverse the cert issue operation by revoking the incompletely-
issued cert. We only return the status from this operation if
we're performing the reversal at the request of the user (i.e. if
the earlier operations succeeded), if not we return the status
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -