📄 ocsp.c
字号:
/****************************************************************************
* *
* cryptlib OCSP Session Management *
* Copyright Peter Gutmann 1999-2003 *
* *
****************************************************************************/
#include <stdio.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "asn1.h"
#include "asn1_ext.h"
#include "session.h"
#else
#include "crypt.h"
#include "misc/asn1.h"
#include "misc/asn1_ext.h"
#include "session/session.h"
#endif /* Compiler-specific includes */
#ifdef USE_OCSP
/* OCSP HTTP content types */
#define OCSP_CONTENT_TYPE_REQ "application/ocsp-request"
#define OCSP_CONTENT_TYPE_REQ_LEN 24
#define OCSP_CONTENT_TYPE_RESP "application/ocsp-response"
#define OCSP_CONTENT_TYPE_RESP_LEN 25
/* OCSP query/response types */
typedef enum {
OCSPRESPONSE_TYPE_NONE, /* No response type */
OCSPRESPONSE_TYPE_OCSP, /* OCSP standard response */
OCSPRESPONSE_TYPE_LAST /* Last valid response type */
} OCSPRESPONSE_TYPE;
/* OCSP response status values */
enum { OCSP_RESP_SUCCESSFUL, OCSP_RESP_MALFORMEDREQUEST,
OCSP_RESP_INTERNALERROR, OCSP_RESP_TRYLATER, OCSP_RESP_DUMMY,
OCSP_RESP_SIGREQUIRED, OCSP_RESP_UNAUTHORISED };
/* OCSP protocol state information. This is passed around various
subfunctions that handle individual parts of the protocol */
typedef struct {
OCSPRESPONSE_TYPE responseType; /* Response type to return */
} OCSP_PROTOCOL_INFO;
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* 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 */
static void sendErrorResponse( SESSION_INFO *sessionInfoPtr,
const void *responseData,
const int responseDataLength )
{
/* 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 */
memcpy( sessionInfoPtr->receiveBuffer, responseData,
responseDataLength );
sessionInfoPtr->receiveBufEnd = responseDataLength;
( void ) writePkiDatagram( sessionInfoPtr, OCSP_CONTENT_TYPE_RESP,
OCSP_CONTENT_TYPE_RESP_LEN );
}
/* Compare the nonce in a request with the returned nonce in the response */
static BOOLEAN checkNonce( const CRYPT_CERTIFICATE iCertResponse,
const void *requestNonce,
const int requestNonceLength )
{
MESSAGE_DATA responseMsgData;
BYTE responseNonceBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
/* Make sure that the nonce has a plausible length */
if( requestNonceLength < 4 )
return( FALSE );
/* Try and read the nonce from the response */
setMessageData( &responseMsgData, responseNonceBuffer,
CRYPT_MAX_HASHSIZE );
if( cryptStatusError( \
krnlSendMessage( iCertResponse, IMESSAGE_GETATTRIBUTE_S,
&responseMsgData, CRYPT_CERTINFO_OCSP_NONCE ) ) )
return( FALSE );
/* Make sure that the two nonces match. The comparison is somewhat
complex because for no known reason OCSP uses integers rather than
octet strings as nonces, so the nonce is encoded using the integer
analog to an OCTET STRING hole and may have a leading zero byte if
the high bit is set. Because of this we treat the two as equal if
they really are equal or if they differ only by a leading zero byte
(in practice the cert-handling code ensures that the high bit is
never set, but we leave the extra checking in there just in case) */
if( requestNonceLength == responseMsgData.length && \
!memcmp( requestNonce, responseMsgData.data, requestNonceLength ) )
/* Literal nonce comparison */
return( TRUE );
if( requestNonceLength == responseMsgData.length - 1 && \
responseNonceBuffer[ 0 ] == 0 && \
!memcmp( requestNonce, responseNonceBuffer + 1, requestNonceLength ) )
/* INTEGER-encoding nonce comparison */
return( TRUE );
return( FALSE );
}
/****************************************************************************
* *
* Client-side Functions *
* *
****************************************************************************/
/* OID information used to read responses */
static const OID_INFO FAR_BSS ocspOIDinfo[] = {
{ OID_OCSP_RESPONSE_OCSP, OCSPRESPONSE_TYPE_OCSP },
{ NULL, 0 }
};
/* Send a request to an OCSP server */
static int sendClientRequest( SESSION_INFO *sessionInfoPtr )
{
MESSAGE_DATA msgData;
int status;
/* Get the encoded request data. We store this in the send buffer, which
at its minimum size is roughly two orders of magnitude larger than the
request */
setMessageData( &msgData, sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBufSize );
status = krnlSendMessage( sessionInfoPtr->iCertRequest,
IMESSAGE_CRT_EXPORT, &msgData,
CRYPT_ICERTFORMAT_DATA );
if( cryptStatusError( status ) )
retExt( status,
( status, SESSION_ERRINFO,
"Couldn't get OCSP request data from OCSP request "
"object" ) );
sessionInfoPtr->receiveBufEnd = msgData.length;
DEBUG_DUMP( "ocsp_req", sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBufEnd );
/* Send the request to the responder */
return( writePkiDatagram( sessionInfoPtr, OCSP_CONTENT_TYPE_REQ,
OCSP_CONTENT_TYPE_REQ_LEN ) );
}
/* Read the response from the OCSP server */
static int readServerResponse( SESSION_INFO *sessionInfoPtr )
{
CRYPT_CERTIFICATE iCertResponse;
MESSAGE_DATA msgData;
STREAM stream;
BYTE nonceBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
const char *errorString = NULL;
int value, responseType, length, status;
/* Read the response from the responder */
status = readPkiDatagram( sessionInfoPtr );
if( cryptStatusError( status ) )
return( status );
DEBUG_DUMP( "ocsp_resp", sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBufEnd );
/* Try and extract an OCSP status code from the returned object */
sMemConnect( &stream, sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBufEnd );
readSequence( &stream, NULL );
status = readEnumerated( &stream, &value );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* If it's an error status, try and translate it into something a bit
more meaningful (some of the translations are a bit questionable, but
it's better than the generic no va response (which should actually be
"no marcha" in any case)) */
sessionInfoPtr->errorInfo.errorCode = value;
switch( value )
{
case OCSP_RESP_SUCCESSFUL:
status = CRYPT_OK;
break;
case OCSP_RESP_TRYLATER:
errorString = "Try again later";
status = CRYPT_ERROR_NOTAVAIL;
break;
case OCSP_RESP_SIGREQUIRED:
errorString = "Signed OCSP request required";
status = CRYPT_ERROR_SIGNATURE;
break;
case OCSP_RESP_UNAUTHORISED:
errorString = "Client isn't authorised to perform query";
status = CRYPT_ERROR_PERMISSION;
break;
default:
errorString = "Unknown error";
status = CRYPT_ERROR_INVALID;
break;
}
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
retExt( status,
( status, SESSION_ERRINFO,
"OCSP server returned status %d: %s",
value, errorString ) );
}
/* We've got a valid response, read the [0] EXPLICIT SEQUENCE { OID,
OCTET STRING { encapsulation and import the response into an OCSP
cert object */
readConstructed( &stream, NULL, 0 ); /* responseBytes */
readSequence( &stream, NULL );
readOID( &stream, ocspOIDinfo, /* responseType */
FAILSAFE_ARRAYSIZE( ocspOIDinfo, OID_INFO ), &responseType );
status = readGenericHole( &stream, &length, 16, DEFAULT_TAG );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
retExt( status,
( status, SESSION_ERRINFO,
"Invalid OCSP response header" ) );
}
status = importCertFromStream( &stream, &iCertResponse,
CRYPT_CERTTYPE_OCSP_RESPONSE, length );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
retExt( status,
( status, SESSION_ERRINFO, "Invalid OCSP response data" ) );
/* If the request went out with a nonce included (which it does by
default), make sure that it matches the nonce in the response */
setMessageData( &msgData, nonceBuffer, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( sessionInfoPtr->iCertRequest,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CERTINFO_OCSP_NONCE );
if( cryptStatusOK( status ) && \
!checkNonce( iCertResponse, msgData.data, msgData.length ) )
{
/* The response doesn't contain a nonce or it doesn't match what we
sent, we can't trust it. The best error that we can return here
is a signature error to indicate that the integrity check
failed */
krnlSendNotifier( iCertResponse, IMESSAGE_DECREFCOUNT );
retExt( CRYPT_ERROR_SIGNATURE,
( CRYPT_ERROR_SIGNATURE, SESSION_ERRINFO,
cryptStatusError( status ) ? \
"OCSP response doesn't contain a nonce" : \
"OCSP response nonce doesn't match the one in the "
"request" ) );
}
krnlSendNotifier( sessionInfoPtr->iCertRequest, IMESSAGE_DECREFCOUNT );
sessionInfoPtr->iCertRequest = CRYPT_ERROR;
sessionInfoPtr->iCertResponse = iCertResponse;
return( CRYPT_OK );
}
/****************************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -