⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 http.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 4 页
字号:
   reported as a CRYPT_ERROR_TIMEOUT by the lower-level network I/O 
   routines, however due to the multiple layers of I/O and special case 
   timeout handling when (for example) a cryptlib transport session is 
   layered over the network I/O layer and the fact that to the caller the
   write of the out-of-band HTTP header data is invisible, we have to 
   perform an explicit check to make sure that we sent everything */

static int sendHTTPData( STREAM *stream, void *buffer, const int length,
						 const int flags )
	{
	int status;

	status = stream->bufferedTransportWriteFunction( stream, buffer, length, 
													 flags );
	if( cryptStatusError( status ) )
		/* Network-level error, the lower-level layers have reported the 
		   error details */
		return( status );
	if( status < length )
		/* The write timed out, convert the incomplete HTTP header write to 
		   the appropriate timeout error */
		retExtStream( stream, CRYPT_ERROR_TIMEOUT,
					  "HTTP write timed out before all data could be "
					  "written" );
	return( CRYPT_OK );
	}

/* Send an HTTP error message */

static int sendHTTPError( STREAM *stream, char *headerBuffer, 
						  const int httpStatus )
	{
	const char *statusString = "400";
	const char *errorString = "Bad Request";
	int length, i;

	/* Find the HTTP error string that corresponds to the HTTP status 
	   value */
	for( i = 0; httpStatusInfo[ i ].httpStatus && \
				httpStatusInfo[ i ].httpStatus != httpStatus; i++ );
	if( httpStatusInfo[ i ].httpStatus )
		{
		statusString = httpStatusInfo[ i ].httpStatusString;
		errorString = httpStatusInfo[ i ].httpErrorString;
		}

	/* Send the error message to the peer.  We have to be careful with return
	   values since we could time out before all the data is sent */
	length = sPrintf( headerBuffer, "%s %s %s\r\n\r\n",
					  isHTTP10( stream ) ? "HTTP/1.0" : "HTTP/1.1",
					  statusString, errorString );
	return( sendHTTPData( stream, headerBuffer, length, 
						  TRANSPORT_FLAG_FLUSH ) );
	}

/****************************************************************************
*																			*
*							HTTP Parsing Functions							*
*																			*
****************************************************************************/

/* Parse a sub-segment of a URI, returning its length */

static int parseUriSegment( const char *buffer, const char endChar )
	{
	int length;

	/* Parse the current query sub-segment */
	for( length = 0; length <= CRYPT_MAX_TEXTSIZE && \
					 *buffer && *buffer != endChar; \
		 length++ )
		buffer++;

	/* Make sure that we didn't run out of data */
	if( length >= CRYPT_MAX_TEXTSIZE || !*buffer )
		return( CRYPT_ERROR_BADDATA );

	return( length );
	}

/* Parse a URI of the form * '?' attribute '=' value */

static int parseURI( char *outBuffer, int *outBufPos, 
					 const char *inBuffer, const int inBufLen )
	{
	const char *origOutBuffer = outBuffer, *bufPtr = inBuffer;
	const char *namePtr, *valuePtr;
	int nameLength, valueLength, bufLen = inBufLen, status;

	/* Clear return value */
	*outBufPos = 0;

	/* Decode the URI line.  Since there can be multiple nested levels of
	   encoding, we keep iteratively decoding until decodeRFC1866() cries
	   Uncle.  The first time through the loop we decode from the inBuffer to
	   the outBuffer, in successive iterations we decode in-place in the
	   outBuffer */
	do
		{
		status = decodeRFC1866( outBuffer, bufPtr, bufLen );
		if( !cryptStatusError( status ) )
			/* It's a length-change notification, record the new length */
			bufLen = status;
		else
			if( status != OK_SPECIAL )
				return( CRYPT_ERROR_BADDATA );
		bufPtr = outBuffer;
		}
	while( status != OK_SPECIAL );

	/* Open up a gap at the start of the output buffer to allow for the 
	   encoded return form */
	memmove( outBuffer + 8, outBuffer, bufLen );
	bufPtr = outBuffer + 8;

	/* Parse a URI of the form * '?' attribute '=' value */
	status = parseUriSegment( bufPtr, '?' );
	if( cryptStatusError( status ) )
		return( status );
	bufPtr += status + 1;		/* Skip '?' */
	namePtr = bufPtr;
	nameLength = parseUriSegment( bufPtr, '=' );
	if( cryptStatusError( nameLength ) )
		return( nameLength );
	bufPtr += nameLength + 1;	/* Skip '=' */
	valuePtr = bufPtr;
	valueLength = parseUriSegment( bufPtr, ' ' );
	if( cryptStatusError( valueLength ) )
		return( valueLength );

	/* Encode the location, attribute, and value for use by the caller */
	mputWord( outBuffer, 0 );
	mputWord( outBuffer, nameLength );
	memmove( outBuffer, namePtr, nameLength );
	outBuffer += nameLength;
	mputWord( outBuffer, valueLength );
	memmove( outBuffer, valuePtr, valueLength );
	outBuffer += valueLength;
	*outBufPos = outBuffer - origOutBuffer;

	return( ( valuePtr + valueLength ) - origOutBuffer );
	}

/* Check an "HTTP 1.x" ID string.  No PKI client should be sending us an 0.9
   ID, so we only allow 1.x */

static int checkHTTPID( STREAM *stream, const char *buffer, const int length )
	{
	if( length < 8 || strCompare( buffer, "HTTP/1.", 7 ) )
		return( CRYPT_ERROR_BADDATA );
	if( buffer[ 7 ] == '0' )
		stream->flags |= STREAM_NFLAG_HTTP10;
	else
		if( buffer[ 7 ] != '1' )
			return( CRYPT_ERROR_BADDATA );

	return( 8 );
	}

/* Read an HTTP status code.  Some status values are warnings only and
   don't return an error status */

static int readHTTPStatus( STREAM *stream, int *httpStatus,
						   const char *lineBuffer )
	{
	const HTTP_STATUS_INFO *httpStatusPtr;
	const char *lineBufPtr;
	char thirdChar;
	int i;

	/* Clear return value */
	if( httpStatus != NULL )
		*httpStatus = CRYPT_OK;

	/* Process the numeric HTTP status code and translate it into a cryptlib
	   equivalent.  We check the third digit (the one most likely to be 
	   different) for a mismatch to avoid a large number of calls to the 
	   string-compare function.  Most of the HTTP codes don't have any 
	   meaning in a cryptlib context, so we return a generic 
	   CRYPT_ERROR_READ */
	lineBufPtr = skipWhitespace( lineBuffer );
	if( lineBufPtr == NULL || strlen( lineBufPtr ) < 3 || \
		!isDigit( *lineBufPtr ) )
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Invalid/missing HTTP status code" );
	thirdChar = lineBufPtr[ 2 ];
	for( i = 0; \
		 httpStatusInfo[ i ].httpStatus && \
		 ( httpStatusInfo[ i ].httpStatusString[ 2 ] != thirdChar || \
		   strCompare( lineBufPtr, httpStatusInfo[ i ].httpStatusString, 3 ) ); \
		 i++ );
	httpStatusPtr = &httpStatusInfo[ i ];
	if( httpStatus != NULL )
		*httpStatus = aToI( lineBufPtr );
	if( httpStatusPtr->status == OK_SPECIAL )
		/* It's a special-case condition such as a redirect, tell the caller
		   to handle it specially */
		return( OK_SPECIAL );
	if( httpStatusPtr->status != CRYPT_OK )
		{
		/* It's an error condition, return extended error info */
		assert( httpStatusPtr->httpStatusString != NULL );
							/* Catch oddball errors in debug version */
		retExtStream( stream, httpStatusPtr->status, "HTTP status: %s",
					  httpStatusPtr->httpErrorString );
		}
	return( CRYPT_OK );
	}

/* Process an HTTP header line looking for anything that we can handle */

static int checkHeaderLine( char **lineBufPtrPtr, 
							HTTP_HEADER_TYPE *headerType, void *stream )
	{
	const HTTP_HEADER_INFO *headerInfoPtr;
	const char *lineBufPtr = *lineBufPtrPtr;
	const char firstChar = toUpper( *lineBufPtr );
	const int lineLength = strlen( lineBufPtr );
	int i;

	/* Clear return value */
	*headerType = HTTP_HEADER_NONE;

	/* Look for a header line that we recognise */
	for( i = 0; 
		 httpHeaderInfo[ i ].headerString != NULL && \
		 ( httpHeaderInfo[ i ].headerString[ 0 ] != firstChar || \
		   lineLength < httpHeaderInfo[ i ].headerStringLen || \
		   strCompare( lineBufPtr, httpHeaderInfo[ i ].headerString, \
					   httpHeaderInfo[ i ].headerStringLen ) ); 
		 i++ );
	headerInfoPtr = &httpHeaderInfo[ i ];
	if( headerInfoPtr->headerString == NULL )
		/* It's nothing that we can handle, exit */
		return( CRYPT_OK );

	/* Make sure that there's a token present */
	lineBufPtr = skipWhitespace( lineBufPtr + headerInfoPtr->headerStringLen );
	if( lineBufPtr == NULL )
		retExtStream( stream, CRYPT_ERROR_BADDATA, 
					  "Missing HTTP header token for '%s'", 
					  headerInfoPtr->headerString );

	/* Tell the caller what we found */
	*lineBufPtrPtr = ( char * ) lineBufPtr;
	*headerType = headerInfoPtr->headerType;
	return( CRYPT_OK );
	}

/* Read the first line in an HTTP response header */

static int readFirstHeaderLine( STREAM *stream, int *httpStatus,
								char *lineBuffer, const int maxLength )
	{
	int status;

	*httpStatus = CRYPT_OK;

	/* Read the header and check for an HTTP ID */
	status = readLine( stream, lineBuffer, maxLength );
	if( cryptStatusError( status ) )
		return( status );
	status = checkHTTPID( stream, lineBuffer, status );
	if( cryptStatusError( status ) )
		retExtStream( stream, status, "Invalid HTTP ID/version" );

	/* Read the HTTP status info */
	return( readHTTPStatus( stream, httpStatus, lineBuffer + status ) );
	}

/* Read the remaining HTTP header lines after the first one */

static int readHeaderLines( STREAM *stream, char *lineBuffer,
							int *contentLength, int *httpErrorStatus, 
							int *flags, const int minLength, 
							const int maxLength, const BOOLEAN expandBuffer )
	{
	BOOLEAN seenHost = FALSE, seenLength = FALSE;
	int localLength = 0, lineCount, status;

	/* Clear return value */
	if( httpErrorStatus != NULL )
		*httpErrorStatus = 0;
	if( contentLength != NULL )
		*contentLength = 0;

	/* Read each line in the header checking for any fields that we need to 
	   handle.  We check for a couple of basic problems with the header to
	   avoid malformed-header attacks, for example an attacker could send a
	   request with two 'Content-Length:' headers, one of which covers the
	   entire message body and the other which indicates that there's a 
	   second request that begins halfway through the message body.  Some
	   proxies/caches will take the first length, some the second, if the
	   proxy is expected to check/rewrite the request as it passes through
	   then the single/dual-message issue can be used to bypass the checking
	   on the tunnelled second message.  Because of this we only allow a 
	   single Host: and Content-Length: header, and disallow a chunked 
	   encoding in combination with a content-length (Apache does some 
	   really strange things with chunked encodings).  Note that we can't be
	   too finicky with the checking or we'll end up rejecting non-malicious 
	   requests from some of the broken HTTP implementations out there */
	for( lineCount = 0; lineCount < MAX_HEADER_LINES; lineCount++ )
		{
		HTTP_HEADER_TYPE headerType;
		char *lineBufPtr = lineBuffer;

		status = readLine( stream, lineBuffer, maxLength );
		if( cryptStatusError( status ) )
			return( status );
		if( status == 0 )
			/* End of input, exit */
			break;
		status = checkHeaderLine( &lineBufPtr, &headerType, stream );
		if( cryptStatusError( status ) )
			return( status );
		switch( headerType )
			{
			case HTTP_HEADER_HOST:
				/* Make sure that it's a non-duplicate, and remember that 
				   we've seen a Host: line, to meet the HTTP 1.1 
				   requirements */
				if( seenHost )
					retExtStream( stream, CRYPT_ERROR_BADDATA,
								  "Duplicate 'Host:' header line" );
				seenHost = TRUE;
				break;
			
			case HTTP_HEADER_CONTENT_LENGTH:
				/* Make sure that it's a non-duplicate and get the content 
				   length.  At this point all we do is a general sanity 
				   check that the length looks OK, a specific check against 
				   the caller-supplied minimum allowable length is performed 
				   later since the content length may also be provided as a 
				   chunked encoding length */
				if( seenLength || ( *flags & HTTP_FLAG_CHUNKED ) )
					retExtStream( stream, CRYPT_ERROR_BADDATA,
								  "Duplicate 'Content-Length:' header line" );
				localLength = aToI( lineBufPtr );
				if( localLength <= 0 || localLength > MAX_INTLENGTH )
					retExtStream( stream, CRYPT_ERROR_BADDATA,
								  "Invalid HTTP content length %d",
								  localLength );
				seenLength = TRUE;
				break;

			case HTTP_HEADER_CONTENT_TYPE:
				/* Sometimes if there's an error it'll be returned as content
				   at the HTTP level rather than at the tunnelled-over-HTTP 
				   protocol level.  The easiest way to check for this would 
				   be to make sure that the content-type matches the 
				   expected type and report anything else as an error.  
				   Unfortunately due to the hit-and-miss handling of content-
				   types by PKI software using HTTP as a substrate it's not 
				   safe to do this, so we have to default to allow-all 
				   rather than deny-all, treating only straight text as a 
				   problem type.
				   
				   Unfortunately there are also apps out there that send 
				   their PKI messages marked as plain text, so this isn't
				   100% foolproof, but in practice errors-via-HTTP is more
				   common than certs-via-text.  We try and detect the 
				   cert-as-plain-text special-case at a later point when 
				   we've got the message body available */
				if( !strCompare( lineBufPtr, "text/", 5 ) )
					*flags |= HTTP_FLAG_TEXTMSG;
				break;

			case HTTP_HEADER_TRANSFER_ENCODING:
				if( !strCompare( lineBuffer, "Chunked", 7 ) )

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -