http_parse.c
来自「cryptlib安全工具包」· C语言 代码 · 共 1,421 行 · 第 1/4 页
C
1,421 行
servers work (you can see this by serving a JPEG file as
text/plain, MSIE will display it as a JPEG while Mozilla/
Firefox/Opera/etc will display it as text or prompt for a
helper app to handle it). Since this content-type
guessing is a potential security hole, MS finally made it
configurable in Windows XP SP2, but it's still enabled
by default even there.
In practice however 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.
Since we're now looking at the content-type line (even if
we don't really process it in any way), we perform at
least a minimal validity check for * '/' * [ ';*' ] */
status = getUriSegmentLength( lineBufPtr, lineLength,
&contentTypeLen,
&typeParseInfo, NULL );
if( cryptStatusError( status ) )
{
/* We need to have at least "xx/"* present (length is
guaranteed by getUriSegmentLength()) */
return( retHeaderError( stream,
"Invalid HTTP content type '%s', line %d",
lineBufPtr, lineLength, lineCount ) );
}
contentType = lineBufPtr;
lineBufPtr += contentTypeLen + 1; /* Skip delimiter */
lineLength -= contentTypeLen + 1;
status = getUriSegmentLength( lineBufPtr, lineLength,
&subTypeLen,
&subtypeParseInfo, &dummy );
if( cryptStatusError( status ) )
{
/* We need to have at least 'xx/yy' present (length is
guaranteed by getUriSegmentLength()) */
return( retHeaderError( stream,
"Invalid HTTP content subtype '%s', line %d",
lineBufPtr, lineLength, lineCount ) );
}
if( contentTypeLen == 4 && \
!strCompare( contentType, "text", 4 ) )
headerInfo->flags |= HTTP_FLAG_TEXTMSG;
break;
}
case HTTP_HEADER_TRANSFER_ENCODING:
if( lineLength < 7 || \
strCompare( lineBufPtr, "Chunked", 7 ) )
{
return( retHeaderError( stream,
"Invalid HTTP transfer encoding method "
"'%s', expected 'Chunked', line %d",
lineBufPtr, lineLength, lineCount ) );
}
/* If it's a chunked encoding, the length is part of the
data and must be read later */
if( seenLength )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Duplicate HTTP 'Content-Length:' header, "
"line %d", lineCount + 2 ) );
}
headerInfo->flags |= HTTP_FLAG_CHUNKED;
seenLength = TRUE;
break;
case HTTP_HEADER_CONTENT_ENCODING:
/* We can't handle any type of content encoding (e.g. gzip,
compress, deflate, mpeg4, interpretive dance) except the
no-op identity encoding */
if( lineLength < 8 || \
strCompare( lineBufPtr, "Identity", 8 ) )
{
headerInfo->httpStatus = 415; /* Unsupp.media type */
return( retHeaderError( stream,
"Invalid HTTP content encoding method "
"'%s', expected 'Identity', line %d",
lineBufPtr, lineLength, lineCount ) );
}
break;
case HTTP_HEADER_CONTENT_TRANSFER_ENCODING:
/* HTTP uses Transfer-Encoding, not the MIME Content-
Transfer-Encoding types such as base64 or quoted-
printable. If any implementations erroneously use a
C-T-E, we make sure that it's something that we can
handle */
if( lineLength < 6 || \
( strCompare( lineBufPtr, "Identity", 8 ) && \
strCompare( lineBufPtr, "Binary", 6 ) ) )
{
headerInfo->httpStatus = 415; /* Unsupp.media type */
return( retHeaderError( stream,
"Invalid HTTP content transfer encoding "
"method '%s', expected 'Identity' or "
"'Binary', line %d", lineBufPtr, lineLength,
lineCount ) );
}
break;
case HTTP_HEADER_TRAILER:
/* The body is followed by trailer lines, used with chunked
encodings where some header lines can't be produced until
the entire body has been generated. This wasn't added
until RFC 2616, since many implementations are based on
RFC 2068 and don't produce this header we don't do
anything with it. The trailer can be auto-detected
anyway, it's only present to tell the receiver to perform
certain actions such as creating an MD5 hash of the data
as it arrives */
headerInfo->flags |= HTTP_FLAG_TRAILER;
break;
case HTTP_HEADER_CONNECTION:
/* If the other side has indicated that it's going to close
the connection, remember that the stream is now no longer
usable */
if( lineLength >= 5 && \
!strCompare( lineBufPtr, "Close", 5 ) )
sioctl( stream, STREAM_IOCTL_CONNSTATE, NULL, FALSE );
break;
case HTTP_HEADER_WARNING:
/* Read the HTTP status info from the warning.
readHTTPStatus() will process the error status in the
warning line, but since we're passing in a NULL pointer
for the status info it'll only report an error in the
warning content itself, it won't return the processed
warning status as an error */
status = readHTTPStatus( lineBufPtr, lineLength, NULL,
NETSTREAM_ERRINFO );
if( cryptStatusError( status ) )
{
return( retHeaderError( stream,
"Invalid HTTP warning information '%s', "
"line %d", lineBufPtr, lineLength,
lineCount ) );
}
break;
case HTTP_HEADER_LOCATION:
{
#if defined( __WIN32__ ) && !defined( NDEBUG ) && 1
URL_INFO urlInfo;
#endif /* Win32 debug build only */
/* Make sure that we've been given an HTTP URL as the
redirect location. We need to do this because
sNetParseURL() will accept a wide range of URL types
while we only allow "http://"* */
if( lineLength < 10 || \
strCompare( lineBufPtr, "http://", 7 ) )
{
return( retHeaderError( stream,
"Invalid HTTP redirect location '%s', line %d",
lineBufPtr, lineLength, lineCount ) );
}
/* Process the redirect location */
#if defined( __WIN32__ ) && !defined( NDEBUG ) && 1
/* We don't try and parse the URL other than in the Win32
debug build because we don't do redirects yet so there's
no need to expose ourselves to possibly maliciously-
created URLs from external sources */
status = sNetParseURL( &urlInfo, lineBufPtr, lineLength,
URL_TYPE_HTTP );
if( cryptStatusError( status ) )
{
return( retHeaderError( stream,
"Invalid HTTP redirect location '%s', line %d",
lineBufPtr, lineLength, lineCount ) );
}
#endif /* Win32 debug build only */
break;
}
case HTTP_HEADER_EXPECT:
/* If the other side wants the go-ahead to continue, give it
to them. We do this automatically because we're merely
using HTTP as a substrate, the real decision will be made
at the higher-level protocol layer */
if( lineLength >= 12 && \
!strCompare( lineBufPtr, "100-Continue", 12 ) )
sendHTTPError( stream, lineBufPtr, lineBufMaxLen, 100 );
break;
case HTTP_HEADER_NONE:
/* It's something that we don't know/care about, skip it */
break;
default:
retIntError();
}
}
if( lineCount >= FAILSAFE_ITERATIONS_MED )
{
retExt( CRYPT_ERROR_OVERFLOW,
( CRYPT_ERROR_OVERFLOW, NETSTREAM_ERRINFO,
"Too many HTTP header lines" ) );
}
/* If this is a tunnel being opened via an HTTP proxy, we're done */
if( !( netStream->nFlags & STREAM_NFLAG_ISSERVER ) && \
( netStream->nFlags & STREAM_NFLAG_HTTPTUNNEL ) )
return( CRYPT_OK );
/* If it's a chunked encoding for which the length is kludged on before
the data as a hex string, decode the length value */
if( headerInfo->flags & HTTP_FLAG_CHUNKED )
{
BOOLEAN textDataError;
int lineLength;
status = readTextLine( readCharFunction, stream, lineBuffer,
lineBufMaxLen, &lineLength, &textDataError );
if( cryptStatusError( status ) )
{
return( retTextLineError( stream, status, textDataError,
"Invalid HTTP chunked encoding "
"header line %d: ", lineCount + 2 ) );
}
if( lineLength <= 0 )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Missing HTTP chunk length, line %d", lineCount + 2 ) );
}
status = contentLength = getChunkLength( lineBuffer, lineLength );
if( cryptStatusError( status ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Invalid length for HTTP chunked encoding, line %d",
lineCount + 2 ) );
}
}
/* If this is a no-op read (for example lines following an error or 100
Continue response), all that we're interested in is draining the
input, so we don't check any further */
if( headerInfo->flags & HTTP_FLAG_NOOP )
return( CRYPT_OK );
/* If we're a server talking HTTP 1.1 and we haven't seen a "Host:"
header from the client, return an error */
if( ( netStream->nFlags & STREAM_NFLAG_ISSERVER ) && \
!isHTTP10( stream ) && !seenHost )
{
headerInfo->httpStatus = 400; /* Bad request */
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Missing HTTP 1.1 'Host:' header" ) );
}
/* If it's a GET request there's no length so we can exit now */
if( headerInfo->flags & HTTP_FLAG_GET )
{
if( seenLength )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Unexpected %d bytes HTTP body content received in "
"idempotent read", contentLength ) );
}
return( CRYPT_OK );
}
/* Make sure that we've been given a length. In theory a server could
indicate the length implicitly by closing the connection once it's
sent the last byte, but this isn't allowed for PKI messages. The
client can't use this option either since that would make it
impossible for us to send back the response */
if( !seenLength )
{
headerInfo->httpStatus = 411; /* Length required */
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Missing HTTP length" ) );
}
/* Make sure that the length is sensible */
if( contentLength < headerInfo->minContentLength || \
contentLength > headerInfo->maxContentLength )
{
retExt( ( contentLength < headerInfo->minContentLength ) ? \
CRYPT_ERROR_UNDERFLOW : CRYPT_ERROR_OVERFLOW,
( ( contentLength < headerInfo->minContentLength ) ? \
CRYPT_ERROR_UNDERFLOW : CRYPT_ERROR_OVERFLOW,
NETSTREAM_ERRINFO,
"Invalid HTTP content length %d bytes, expected "
"%d...%d bytes", contentLength,
headerInfo->minContentLength,
headerInfo->maxContentLength ) );
}
headerInfo->contentLength = contentLength;
return( CRYPT_OK );
}
/* Read the HTTP trailer lines that follow chunked data:
CRLF
"0" CRLF
trailer-lines*
CRLF */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int readTrailerLines( INOUT STREAM *stream,
OUT_BUFFER_FIXED( lineBufMaxLen ) char *lineBuffer,
IN_LENGTH_SHORT_MIN( 256 ) const int lineBufMaxLen )
{
NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
HTTP_HEADER_INFO headerInfo;
BOOLEAN textDataError;
int readLength = DUMMY_INIT, dummy, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( lineBuffer, lineBufMaxLen ) );
REQUIRES( lineBufMaxLen >= 256 && lineBufMaxLen < MAX_INTLENGTH_SHORT );
/* Read the blank line and chunk length */
status = readTextLine( readCharFunction, stream, lineBuffer,
lineBufMaxLen, &dummy, &textDataError );
if( cryptStatusOK( status ) )
status = readTextLine( readCharFunction, stream, lineBuffer,
lineBufMaxLen, &readLength, &textDataError );
if( cryptStatusError( status ) )
return( retTextLineError( stream, status, textDataError,
"Invalid HTTP chunked trailer line: ",
0 ) );
/* Make sure that there are no more chunks to follow */
status = getChunkLength( lineBuffer, readLength );
if( status != 0 )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Unexpected additional data following HTTP chunked "
"data" ) );
}
/* Read any remaining trailer lines */
initHeaderInfo( &headerInfo, 0, 0, HTTP_FLAG_NOOP );
return( readHeaderLines( stream, lineBuffer, lineBufMaxLen,
&headerInfo ) );
}
#endif /* USE_HTTP */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?