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 ) ); }
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 ); 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 ) ); }