Esempio n. 1
0
static void sendErrorResponse( INOUT SESSION_INFO *sessionInfoPtr,
							   INOUT SCEP_PROTOCOL_INFO *protocolInfo,
							   IN_ERROR const int scepStatus )
	{
	CRYPT_CERTIFICATE iCmsAttributes;
	int status;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) );
	
	REQUIRES_V( cryptStatusError( scepStatus ) );

	/* Sign the error response using the CA key and SCEP attributes */
	status = createScepAttributes( sessionInfoPtr, protocolInfo,  
								   &iCmsAttributes, MESSAGETYPE_CERTREP, 
								   scepStatus );
	if( cryptStatusOK( status ) )
		{
		ERROR_INFO errorInfo;

		/* Since this message is being sent in response to an existing 
		   error, we don't care about the possible error information 
		   returned from the function that sends the error response,
		   so the errorInfo result is ignored */
		status = envelopeSign( NULL, 0, sessionInfoPtr->receiveBuffer, 
							   sessionInfoPtr->receiveBufSize, 
							   &sessionInfoPtr->receiveBufEnd, 
							   CRYPT_CONTENT_NONE, sessionInfoPtr->privateKey, 
							   iCmsAttributes, &errorInfo );
		krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
		}
	if( cryptStatusError( status ) )
		{
		HTTP_DATA_INFO httpDataInfo;

		/* If we encounter an error processing the initial request then 
		   there won't be enough information available to create an error 
		   response.  Similarly if we run into problems generating the
		   response then 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_FILE( "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 */
	sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_LASTMESSAGE, TRUE );
	( void ) writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE, 
							   SCEP_CONTENT_TYPE_LEN );
	}
static int serverTransact( INOUT SESSION_INFO *sessionInfoPtr )
	{
	SCEP_PROTOCOL_INFO protocolInfo;
	HTTP_DATA_INFO httpDataInfo;
	HTTP_URI_INFO httpReqInfo;
	BOOLEAN requestDataOK;
	int requestCount, length = DUMMY_INIT, status;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );

	/* SCEP is a weird protocol that started out as a basic IPsec 
	   certificate-provisioning mechanism for routers but then had a pile of 
	   additional functionality bolted onto it via HTTP mechanisms rather 
	   than having the protocol itself handle the extra functionality.  
	   Because of this we have to handle not only the standard HTTP-as-a-
	   substrate mechanism used by the other protocols but also HTTP GET 
	   requests for additional information that the original protocol didn't 
	   accomodate */
	sessionInfoPtr->receiveBufEnd = 0;
	sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HTTPREQTYPES, 
			   STREAM_HTTPREQTYPE_ANY );
	for( requestCount = 0; requestCount < 5; requestCount++ )
		{
		initHttpDataInfoEx( &httpDataInfo, sessionInfoPtr->receiveBuffer,
							sessionInfoPtr->receiveBufSize, &httpReqInfo );
		status = sread( &sessionInfoPtr->stream, &httpDataInfo,
						sizeof( HTTP_DATA_INFO ) );
		if( cryptStatusError( status ) )
			{
			sNetGetErrorInfo( &sessionInfoPtr->stream, 
							  &sessionInfoPtr->errorInfo );
			return( status );
			}

		/* If it's a proper SCEP protocol message, switch back to handling 
		   the main protocol */
		if( httpDataInfo.reqType != STREAM_HTTPREQTYPE_GET )
			{
			sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HTTPREQTYPES, 
					   STREAM_HTTPREQTYPE_POST );
			length = httpDataInfo.bytesAvail;
			break;
			}

		/* It's one of the bolted-on additions to the basic SCEP protocol,
		   handle it specially */
		status = processAdditionalScepRequest( sessionInfoPtr, 
											   &httpReqInfo );
		if( cryptStatusError( status ) )
			return( status );
		}
	if( requestCount >= 5 )
		{
		/* The exact type of error response to send at this point is a bit
		   tricky, the least inappropriate one is probably 
		   CRYPT_ERROR_DUPLICATE to indicate that too many duplicate 
		   requests were sent, since to get here the client would have had 
		   to send repeated identical bolt-on requests */
		sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_DUPLICATE );
		return( CRYPT_ERROR_OVERFLOW );
		}

	/* Unfortunately we can't use readPkiDatagram() because of the weird 
	   dual-purpose HTTP transport used in SCEP so we have to duplicate 
	   portions of readPkiDatagram() here.  See the readPkiDatagram() 
	   function for code comments explaining the following operations */
	if( length < 4 || length >= MAX_INTLENGTH )
		{
		sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_BADDATA );
		retExt( CRYPT_ERROR_UNDERFLOW,
				( CRYPT_ERROR_UNDERFLOW, SESSION_ERRINFO, 
				  "Invalid PKI message length %d", length ) );
		}
	status = length = \
				checkObjectEncoding( sessionInfoPtr->receiveBuffer, length );
	if( cryptStatusError( status ) )
		{
		sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_BADDATA );
		retExt( status, 
				( status, SESSION_ERRINFO, "Invalid PKI message encoding" ) );
		}
	sessionInfoPtr->receiveBufEnd = length;

	/* Process the initial message from the client */
	initSCEPprotocolInfo( &protocolInfo );
	status = checkScepRequest( sessionInfoPtr, &protocolInfo, 
							   &requestDataOK );
	if( cryptStatusError( status ) )
		{
		/* If we got far enough into the request data to be able to send a 
		   SCEP-level response, send that, otherwise just send an HTTP-level
		   response */
		if( requestDataOK )
			sendErrorResponse( sessionInfoPtr, &protocolInfo, status );
		else
			sendCertErrorResponse( sessionInfoPtr, status );
		return( status );
		}

	/* Issue a certificate from the request */
	status = issueCertFromRequest( sessionInfoPtr, &protocolInfo );
	if( cryptStatusError( status ) )
		{
		sendErrorResponse( sessionInfoPtr, &protocolInfo, status );
		destroySCEPprotocolInfo( &protocolInfo );
		return( status );
		}

	/* Return the certificate to the client */
	status = createScepResponse( sessionInfoPtr, &protocolInfo );
	if( cryptStatusOK( status ) )
		status = writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE,
								   SCEP_CONTENT_TYPE_LEN );
	destroySCEPprotocolInfo( &protocolInfo );
	return( status );
	}
static int processAdditionalScepRequest( INOUT SESSION_INFO *sessionInfoPtr,
										 const HTTP_URI_INFO *httpReqInfo )
	{
	HTTP_URI_INFO rewrittenHttpReqInfo;
	MESSAGE_DATA msgData;
	int operationType, status;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( isReadPtr( httpReqInfo, sizeof( HTTP_URI_INFO ) ) );

	/* If the client has fed us an HTTP GET request, find out what they  
	   want.  SCEP's handling of HTTP requests is a bit different from the 
	   "attribute '=' value" lookup that's normally used for HTTP data 
	   retrieval.  Instead, it uses the format 
	   "'operation =' value '&' extraData", with the search key buried in 
	   the 'extraData' value.  In addition the content of the 'extraData' 
	   value isn't defined outside of "any string which is understood by the 
	   CA".  However since 'value' defines what we want, we can determine 
	   what to return based on this and ignore the 'extraData' portion.

	   In order to fix up the query information into a format that works 
	   with standard HTTP queries, we rewrite the query data from the 
	   "'operation =' value '&' extraData" form into "attribute '=' value" 
	   before we process the query */
	memset( &rewrittenHttpReqInfo, 0, sizeof( HTTP_URI_INFO ) );
	memcpy( rewrittenHttpReqInfo.attribute, httpReqInfo->value, 
			httpReqInfo->valueLen );
	rewrittenHttpReqInfo.attributeLen = httpReqInfo->valueLen;
	if( httpReqInfo->extraDataLen > 0 )
		{
		memcpy( rewrittenHttpReqInfo.value, httpReqInfo->extraData, 
				httpReqInfo->extraDataLen );
		rewrittenHttpReqInfo.valueLen = httpReqInfo->extraDataLen;
		}
	status = processCertQuery( sessionInfoPtr, &rewrittenHttpReqInfo,
							   certstoreReadInfo, 
							   FAILSAFE_ARRAYSIZE( certstoreReadInfo, \
												   CERTSTORE_READ_INFO ),
							   &operationType, NULL, 0, NULL );
	if( cryptStatusError( status ) )
		{
		sendCertErrorResponse( sessionInfoPtr, status );
		return( status );
		}
	ENSURES( operationType == SCEP_OPERATION_GETCACAPS || \
			 operationType == SCEP_OPERATION_GETCACERT || \
			 operationType == SCEP_OPERATION_GETCACERTCHAIN );

	/* If it's a CA capabilities query, return the information as raw text
	   over HTTP */
	if( operationType == SCEP_OPERATION_GETCACAPS )
		{
		STREAM stream;

		sMemOpen( &stream, sessionInfoPtr->receiveBuffer, 1024 );
		status = swrite( &stream, "POSTPKIOperation\n", 17 );
		if( algoAvailable( CRYPT_ALGO_SHA1 ) )
			status = swrite( &stream, "SHA-1\n", 6 );
		if( algoAvailable( CRYPT_ALGO_SHA2 ) )
			status = swrite( &stream, "SHA-256\n", 8 );
		if( algoAvailable( CRYPT_ALGO_SHAng ) )
			status = swrite( &stream, "SHAng\n", 6 );
		if( algoAvailable( CRYPT_ALGO_3DES ) )
			status = swrite( &stream, "DES3\n", 5 );
		if( algoAvailable( CRYPT_ALGO_AES ) )
			status = swrite( &stream, "AES\n", 4 );
		if( cryptStatusOK( status ) )
			sessionInfoPtr->receiveBufEnd = stell( &stream );
		sMemDisconnect( &stream );
		ENSURES( cryptStatusOK( status ) );
		return( writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE,
								  SCEP_CONTENT_TYPE_LEN ) );
		}

	/* Export the CA certificate and send it to the client */
	setMessageData( &msgData, sessionInfoPtr->receiveBuffer,
					sessionInfoPtr->receiveBufSize );
	status = krnlSendMessage( sessionInfoPtr->privateKey,
							  IMESSAGE_CRT_EXPORT, &msgData,
							  ( operationType == SCEP_OPERATION_GETCACERT ) ? \
								CRYPT_CERTFORMAT_CERTIFICATE : \
								CRYPT_CERTFORMAT_CERTCHAIN );
	if( cryptStatusError( status ) )
		{
		retExt( status,
				( status, SESSION_ERRINFO, 
				  "Couldn't export CA certificate%s for '%s' request", 
				  ( operationType == SCEP_OPERATION_GETCACERT ) ? \
					"" : " chain",
				  ( operationType == SCEP_OPERATION_GETCACERT ) ? \
					"GetCACert" : "GetCACertChain" ) );
		}
	sessionInfoPtr->receiveBufEnd = msgData.length;
	if( operationType == SCEP_OPERATION_GETCACERT )
		{
		return( writePkiDatagram( sessionInfoPtr, 
								  SCEP_CONTENT_TYPE_GETCACERT,
								  SCEP_CONTENT_TYPE_GETCACERT_LEN ) );
		}
	return( writePkiDatagram( sessionInfoPtr, 
							  SCEP_CONTENT_TYPE_GETCACERTCHAIN,
							  SCEP_CONTENT_TYPE_GETCACERTCHAIN_LEN ) );
	}
Esempio n. 4
0
static int processAdditionalScepRequest( INOUT SESSION_INFO *sessionInfoPtr,
										 const HTTP_URI_INFO *httpReqInfo )
	{
	HTTP_URI_INFO rewrittenHttpReqInfo;
	MESSAGE_DATA msgData;
	int operationType, status;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( isReadPtr( httpReqInfo, sizeof( HTTP_URI_INFO ) ) );

	/* If the client has fed us an HTTP GET request, find out what they  
	   want.  SCEP's handling of HTTP requests is a bit different from the 
	   "attribute '=' value" lookup that's normally used for HTTP data 
	   retrieval.  Instead, it uses the format 
	   "'operation =' value '&' extraData", with the search key buried in 
	   the 'extraData' value.  In addition the content of the 'extraData' 
	   value isn't defined outside of "any string which is understood by the 
	   CA".  However since 'value' defines what we want, we can determine 
	   what to return based on this and ignore the 'extraData' portion.

	   In order to fix up the query information into a format that works 
	   with standard HTTP queries, we rewrite the query data from the 
	   "'operation =' value '&' extraData" form into "attribute '=' value" 
	   before we process the query */
	memset( &rewrittenHttpReqInfo, 0, sizeof( HTTP_URI_INFO ) );
	memcpy( rewrittenHttpReqInfo.attribute, httpReqInfo->value, 
			httpReqInfo->valueLen );
	rewrittenHttpReqInfo.attributeLen = httpReqInfo->valueLen;
	if( httpReqInfo->extraDataLen > 0 )
		{
		memcpy( rewrittenHttpReqInfo.value, httpReqInfo->extraData, 
				httpReqInfo->extraDataLen );
		rewrittenHttpReqInfo.valueLen = httpReqInfo->extraDataLen;
		}
	status = processCertQuery( sessionInfoPtr, &rewrittenHttpReqInfo,
							   certstoreReadInfo, 
							   FAILSAFE_ARRAYSIZE( certstoreReadInfo, \
												   CERTSTORE_READ_INFO ),
							   &operationType, NULL, 0, NULL );
	if( cryptStatusError( status ) )
		{
		sendCertErrorResponse( sessionInfoPtr, status );
		return( status );
		}
	ENSURES( operationType == SCEP_OPERATION_GETCACAPS || \
			 operationType == SCEP_OPERATION_GETCACERT || \
			 operationType == SCEP_OPERATION_GETCACERTCHAIN );

	/* If it's a CA capabilities query, return the information as raw text
	   over HTTP */
	if( operationType == SCEP_OPERATION_GETCACAPS )
		{
		STREAM stream;

		sMemOpen( &stream, sessionInfoPtr->receiveBuffer, 1024 );
		swrite( &stream, "POSTPKIOperation\n", 17 );
#if 0	/* 14/6/14 Too risky to implement given its current state in the 
				   spec, see the discussion on the JSCEP mailing list for
				   details */
		status = swrite( &stream, "Renewal\n", 8 );
#endif /* 0 */
		if( algoAvailable( CRYPT_ALGO_SHA1 ) )
			status = swrite( &stream, "SHA-1\n", 6 );
		if( algoAvailable( CRYPT_ALGO_SHA2 ) )
			status = swrite( &stream, "SHA-256\n", 8 );
		if( algoAvailable( CRYPT_ALGO_SHAng ) )
			status = swrite( &stream, "SHAng\n", 6 );
		if( algoAvailable( CRYPT_ALGO_3DES ) )
			status = swrite( &stream, "DES3\n", 5 );
		if( algoAvailable( CRYPT_ALGO_AES ) )
			status = swrite( &stream, "AES\n", 4 );
		if( cryptStatusOK( status ) )
			sessionInfoPtr->receiveBufEnd = stell( &stream );
		sMemDisconnect( &stream );
		ENSURES( cryptStatusOK( status ) );
		return( writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE, 
								  SCEP_CONTENT_TYPE_LEN ) );
		}