cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
http_parse.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib HTTP Parsing Routines *
4 * Copyright Peter Gutmann 1998-2007 *
5 * *
6 ****************************************************************************/
7 
8 #include <ctype.h>
9 #include <stdio.h>
10 #if defined( INC_ALL )
11  #include "crypt.h"
12  #include "misc_rw.h"
13  #include "http.h"
14 #else
15  #include "crypt.h"
16  #include "enc_dec/misc_rw.h"
17  #include "io/http.h"
18 #endif /* Compiler-specific includes */
19 
20 #ifdef USE_HTTP
21 
22 /* The various HTTP header types that we can process */
23 
24 typedef enum { HTTP_HEADER_NONE, HTTP_HEADER_HOST, HTTP_HEADER_CONTENT_LENGTH,
25  HTTP_HEADER_CONTENT_TYPE, HTTP_HEADER_TRANSFER_ENCODING,
26  HTTP_HEADER_CONTENT_ENCODING,
27  HTTP_HEADER_CONTENT_TRANSFER_ENCODING, HTTP_HEADER_TRAILER,
28  HTTP_HEADER_CONNECTION, HTTP_HEADER_WARNING,
29  HTTP_HEADER_LOCATION, HTTP_HEADER_EXPECT, HTTP_HEADER_LAST
30  } HTTP_HEADER_TYPE;
31 
32 /* HTTP header parsing information. Note that the first letter of the
33  header string must be uppercase for the case-insensitive quick match */
34 
35 #define HTTP_STATUSSTRING_LENGTH 3
36 
37 typedef struct {
38  BUFFER_FIXED( headerStringLen ) \
39  const char FAR_BSS *headerString;/* Header string */
40  const int headerStringLen; /* Length of header string */
41  const HTTP_HEADER_TYPE headerType; /* Type corresponding to header string */
42  } HTTP_HEADER_PARSE_INFO;
43 
44 static const HTTP_HEADER_PARSE_INFO FAR_BSS httpHeaderParseInfo[] = {
45  { "Host:", 5, HTTP_HEADER_HOST },
46  { "Content-Length:", 15, HTTP_HEADER_CONTENT_LENGTH },
47  { "Content-Type:", 13, HTTP_HEADER_CONTENT_TYPE },
48  { "Transfer-Encoding:", 18, HTTP_HEADER_TRANSFER_ENCODING },
49  { "Content-Encoding:", 17, HTTP_HEADER_CONTENT_ENCODING },
50  { "Content-Transfer-Encoding:", 26, HTTP_HEADER_CONTENT_TRANSFER_ENCODING },
51  { "Trailer:", 8, HTTP_HEADER_TRAILER },
52  { "Connection:", 11, HTTP_HEADER_CONNECTION },
53  { "NnCoection:", 11, HTTP_HEADER_CONNECTION },
54  { "Cneonction:", 11, HTTP_HEADER_CONNECTION },
55  /* The bizarre spellings are for NetApp NetCache servers, which
56  unfortunately are widespread enough that we need to provide
57  special-case handling for them. For the second mis-spelling we
58  have to capitalise the first letter for our use since we compare
59  the uppercase form for a quick match.
60 
61  The reason why NetApp devices do this is because they think that
62  they can manage connections better than the application that's
63  creating them, so they rewrite "Connection: close" into something
64  that won't be recognised in order to avoid the connection
65  actually being closed. The reason for the 16-bit swap is because
66  the Fletcher checksum used in TCP/IP doesn't detect 16-bit word
67  swaps, so this allows the connection-control to be invalidated
68  without requiring a recalculation of the TCP checksum */
69  { "Warning:", 8, HTTP_HEADER_WARNING },
70  { "Location:", 9, HTTP_HEADER_LOCATION },
71  { "Expect:", 7, HTTP_HEADER_EXPECT },
72  { NULL, 0, HTTP_HEADER_NONE }, { NULL, 0, HTTP_HEADER_NONE }
73  };
74 
75 /* HTTP error/warning messages. The mapped status for 30x redirects is
76  somewhat special-case, see the comment in readResponseHeader() for
77  details. This table also contains known non-HTTP codes in the
78  expectation that, when used as a general-purpose substrate, it'll be
79  pressed into use in all sorts of situations */
80 
81 typedef struct {
82  const int httpStatus; /* Numeric status value */
83  BUFFER_FIXED( HTTP_STATUSSTRING_LENGTH ) \
84  const char FAR_BSS *httpStatusString; /* String status value */
85  BUFFER_FIXED( httpErrorStringLength ) \
86  const char FAR_BSS *httpErrorString; /* Text description of status */
87  const int httpErrorStringLength;
88  const int status; /* Equivalent cryptlib status */
89  } HTTP_STATUS_INFO;
90 
91 static const HTTP_STATUS_INFO FAR_BSS httpStatusInfo[] = {
92  { 100, "100", "Continue", 8, OK_SPECIAL },
93  { 101, "101", "Switching Protocols", 19, CRYPT_ERROR_READ },
94  { 110, "110", "Warning: Response is stale", 26, CRYPT_OK },
95  { 111, "111", "Warning: Revalidation failed", 28, CRYPT_OK },
96  { 112, "112", "Warning: Disconnected operation", 31, CRYPT_OK },
97  { 113, "113", "Warning: Heuristic expiration", 29, CRYPT_OK },
98  { 199, "199", "Warning: Miscellaneous warning", 30, CRYPT_OK },
99  { 200, "200", "OK", 2, CRYPT_OK },
100  { 201, "201", "Created", 7, CRYPT_ERROR_READ },
101  { 202, "202", "Accepted", 8, CRYPT_ERROR_READ },
102  { 203, "203", "Non-Authoritative Information", 29, CRYPT_OK },
103  { 204, "204", "No Content", 10, CRYPT_ERROR_READ },
104  { 205, "205", "Reset Content", 13, CRYPT_ERROR_READ },
105  { 206, "206", "Partial Content", 15, CRYPT_ERROR_READ },
106  { 214, "214", "Warning: Transformation applied", 31, CRYPT_OK },
107  { 250, "250", "RTSP: Low on Storage Space", 26, CRYPT_OK },
108  { 299, "299", "Warning: Miscellaneous persistent warning", 41, CRYPT_OK },
109  { 300, "300", "Multiple Choices", 16, CRYPT_ERROR_READ },
110  { 301, "301", "Moved Permanently", 17, OK_SPECIAL },
111  { 302, "302", "Moved Temporarily/Found", 23, OK_SPECIAL },
112  { 303, "303", "See Other", 9, CRYPT_ERROR_READ },
113  { 304, "304", "Not Modified", 12, CRYPT_ERROR_READ },
114  { 305, "305", "Use Proxy", 9, CRYPT_ERROR_READ },
115  { 306, "306", "Unused/obsolete", 15, CRYPT_ERROR_READ },
116  { 307, "307", "Temporary Redirect", 18, OK_SPECIAL },
117  { 400, "400", "Bad Request", 11, CRYPT_ERROR_READ },
118  { 401, "401", "Unauthorized", 12, CRYPT_ERROR_PERMISSION },
119  { 402, "402", "Payment Required", 16, CRYPT_ERROR_READ },
120  { 403, "403", "Forbidden", 9, CRYPT_ERROR_PERMISSION },
121  { 404, "404", "Not Found", 9, CRYPT_ERROR_NOTFOUND },
122  { 405, "405", "Method Not Allowed", 18, CRYPT_ERROR_NOTAVAIL },
123  { 406, "406", "Not Acceptable", 14, CRYPT_ERROR_PERMISSION },
124  { 407, "407", "Proxy Authentication Required", 29, CRYPT_ERROR_PERMISSION },
125  { 408, "408", "Request Time-out", 16, CRYPT_ERROR_READ },
126  { 409, "409", "Conflict", 8, CRYPT_ERROR_READ },
127  { 410, "410", "Gone", 4, CRYPT_ERROR_NOTFOUND },
128  { 411, "411", "Length Required", 15, CRYPT_ERROR_READ },
129  { 412, "412", "Precondition Failed", 19, CRYPT_ERROR_READ },
130  { 413, "413", "Request Entity too Large", 24, CRYPT_ERROR_OVERFLOW },
131  { 414, "414", "Request-URI too Large", 21, CRYPT_ERROR_OVERFLOW },
132  { 415, "415", "Unsupported Media Type", 22, CRYPT_ERROR_READ },
133  { 416, "416", "Requested range not satisfiable", 31, CRYPT_ERROR_READ },
134  { 417, "417", "Expectation Failed", 18, CRYPT_ERROR_READ },
135  { 426, "426", "Upgrade Required", 16, CRYPT_ERROR_READ },
136  { 451, "451", "RTSP: Parameter not Understood", 30, CRYPT_ERROR_BADDATA },
137  { 452, "452", "RTSP: Conference not Found", 26, CRYPT_ERROR_NOTFOUND },
138  { 453, "453", "RTSP: Not enough Bandwidth", 26, CRYPT_ERROR_NOTAVAIL },
139  { 454, "454", "RTSP: Session not Found", 23, CRYPT_ERROR_NOTFOUND },
140  { 455, "455", "RTSP: Method not Valid in this State", 36, CRYPT_ERROR_NOTAVAIL },
141  { 456, "456", "RTSP: Header Field not Valid for Resource", 41, CRYPT_ERROR_NOTAVAIL },
142  { 457, "457", "RTSP: Invalid Range", 19, CRYPT_ERROR_READ },
143  { 458, "458", "RTSP: Parameter is Read-Only", 28, CRYPT_ERROR_PERMISSION },
144  { 459, "459", "RTSP: Aggregate Operation not Allowed", 37, CRYPT_ERROR_PERMISSION },
145  { 460, "460", "RTSP: Only Aggregate Operation Allowed", 38, CRYPT_ERROR_PERMISSION },
146  { 461, "461", "RTSP: Unsupported Transport", 27, CRYPT_ERROR_NOTAVAIL },
147  { 462, "462", "RTSP: Destination Unreachable", 29, CRYPT_ERROR_OPEN },
148  { 500, "500", "Internal Server Error", 21, CRYPT_ERROR_READ },
149  { 501, "501", "Not Implemented", 15, CRYPT_ERROR_NOTAVAIL },
150  { 502, "502", "Bad Gateway", 11, CRYPT_ERROR_READ },
151  { 503, "503", "Service Unavailable", 19, CRYPT_ERROR_NOTAVAIL },
152  { 504, "504", "Gateway Time-out", 16, CRYPT_ERROR_TIMEOUT },
153  { 505, "505", "HTTP Version not supported", 26, CRYPT_ERROR_READ },
154  { 510, "510", "HTTP-Ext: Not Extended", 22, CRYPT_ERROR_READ },
155  { 551, "551", "RTSP: Option not supported", 26, CRYPT_ERROR_READ },
156  { 0, NULL, "Unrecognised HTTP status condition", 34, CRYPT_ERROR_READ },
157  { 0, NULL, "Unrecognised HTTP status condition", 34, CRYPT_ERROR_READ }
158  };
159 
160 /****************************************************************************
161 * *
162 * Utility Functions *
163 * *
164 ****************************************************************************/
165 
166 /* Callback function used by readTextLine() to read characters from a
167  stream. When reading text data over a network we don't know how much
168  more data is to come so we have to read a byte at a time looking for an
169  EOL. In addition we can't use the simple optimisation of reading two
170  bytes at a time because some servers only send a LF even though the spec
171  requires a CRLF. This is horribly inefficient but is pretty much
172  eliminated through the use of opportunistic read-ahead buffering */
173 
175 static int readCharFunction( INOUT TYPECAST( STREAM * ) void *streamPtr )
176  {
178  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
179  BYTE ch;
180  int length, status;
181 
182  assert( isWritePtr( streamPtr, sizeof( STREAM ) ) );
183 
184  status = netStream->bufferedTransportReadFunction( stream, &ch, 1,
185  &length,
187  return( cryptStatusError( status ) ? status : ch );
188  }
189 
190 /* Decode a hex nibble */
191 
192 CHECK_RETVAL \
193 static int getNibble( IN_CHAR const char srcCh )
194  {
195  int ch;
196 
197  ch = toLower( srcCh );
198  if( !isXDigit( ch ) )
199  return( CRYPT_ERROR_BADDATA );
200  return( ( ch <= '9' ) ? ch - '0' : ch - ( 'a' - 10 ) );
201  }
202 
203 /* Decode an escaped character */
204 
206 static int getEncodedChar( IN_BUFFER( bufSize ) const char *buffer,
207  IN_LENGTH_SHORT const int bufSize )
208  {
209  int chLo, chHi, ch;
210 
211  assert( isReadPtr( buffer, bufSize ) );
212 
213  REQUIRES( bufSize > 0 && bufSize < MAX_INTLENGTH_SHORT );
214 
215  /* Make sure that there's enough data left to decode the character */
216  if( bufSize < 2 )
217  return( CRYPT_ERROR_BADDATA );
218 
219  /* Recreate the original from the hex value */
220  chHi = getNibble( buffer[ 0 ] );
221  chLo = getNibble( buffer[ 1 ] );
222  if( cryptStatusError( chHi ) || cryptStatusError( chLo ) )
223  return( CRYPT_ERROR_BADDATA );
224  ch = ( chHi << 4 ) | chLo;
225 
226  /* It's a special-case/control character of some kind, report it as an
227  error. This gets rid of things like nulls (treated as string
228  terminators by some functions) and CR/LF line terminators, which can
229  be embedded into strings to turn a single line of supplied text into
230  multi-line responses containing user-controlled type : value pairs
231  (in other words they allow user data to be injected into the control
232  channel) */
233  if( ch <= 0 || ch > 0x7F || !isPrint( ch ) )
234  return( CRYPT_ERROR_BADDATA );
235 
236  return( ch );
237  }
238 
239 /* Decode a string as per RFC 1866 */
240 
242 static int decodeRFC1866( IN_BUFFER( bufSize ) char *buffer,
243  IN_LENGTH_SHORT const int bufSize )
244  {
245  int srcIndex = 0, destIndex = 0;
246 
247  assert( isWritePtr( buffer, bufSize ) );
248 
249  REQUIRES( bufSize > 0 && bufSize < MAX_INTLENGTH_SHORT );
250 
251  while( srcIndex < bufSize )
252  {
253  int ch = byteToInt( buffer[ srcIndex++ ] );
254 
255  /* If it's an escaped character, decode it. If it's not escaped we
256  can copy it straight over, the input has already been sanitised
257  when it was read so there's no need to perform another check
258  here */
259  if( ch == '%' )
260  {
261  ch = getEncodedChar( buffer + srcIndex, bufSize - srcIndex );
262  if( cryptStatusError( ch ) )
263  return( ch );
264  srcIndex += 2;
265  }
266  buffer[ destIndex++ ] = intToByte( ch );
267  }
268 
269  /* If we've processed an escape sequence (causing the data to change
270  size), tell the caller the new length, otherwise tell them that
271  nothing's changed */
272  return( ( destIndex < srcIndex ) ? destIndex : OK_SPECIAL );
273  }
274 
275 /* Convert a hex ASCII string used with chunked encoding into a numeric
276  value */
277 
279 static int getChunkLength( IN_BUFFER( dataLength ) const char *data,
280  IN_LENGTH_SHORT const int dataLength )
281  {
282  int i, chunkLength = 0, length = dataLength;
283 
284  assert( isReadPtr( data, dataLength ) );
285 
286  REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT );
287 
288  /* Chunk size information can have extensions tacked onto it following a
289  ';', strip these before we start */
290  for( i = 0; i < length; i++ )
291  {
292  if( data[ i ] == ';' )
293  {
294  /* Move back to the end of the string that precedes the ';' */
295  while( i > 0 && data[ i - 1 ] == ' ' )
296  i--;
297  length = i; /* Adjust length and force loop exit */
298  }
299  }
300 
301  /* The other side shouldn't be sending us more than 64K of data, given
302  that what we're expecting is a short PKI message */
303  if( length < 1 || length > 4 )
304  return( CRYPT_ERROR_BADDATA );
305 
306  /* Walk down the string converting hex characters into their numeric
307  values. Unfortunately since it's hex we can't use strGetNumeric()
308  to read the value but have to hand-assemble it ourselves */
309  for( i = 0; i < length; i++ )
310  {
311  const int ch = getNibble( data[ i ] );
312 
313  if( cryptStatusError( ch ) )
314  return( CRYPT_ERROR_BADDATA );
315  chunkLength = ( chunkLength << 4 ) | ch;
316  }
317  if( chunkLength < 0 || chunkLength >= MAX_INTLENGTH )
318  return( CRYPT_ERROR_BADDATA );
319 
320  return( chunkLength );
321  }
322 
323 /****************************************************************************
324 * *
325 * Error Handling Functions *
326 * *
327 ****************************************************************************/
328 
329 /* Exit with extended error information after a readTextLine() call */
330 
331 STDC_NONNULL_ARG( ( 1, 4 ) ) \
332 int retTextLineError( INOUT STREAM *stream, IN_ERROR const int status,
333  const BOOLEAN isTextLineError,
334  FORMAT_STRING const char *format,
335  const int value )
336  {
337 #ifdef USE_ERRMSGS
338  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
339 #endif /* USE_ERRMSGS */
340 
341  assert( isWritePtr( stream, sizeof( STREAM ) ) );
342  assert( cryptStatusError( status ) );
343  assert( isReadPtr( format, 4 ) );
344 
345  /* If the extended error information came up from a lower level than
346  readCharFunction(), pass it on up to the caller */
347  if( !isTextLineError )
348  return( status );
349 
350  /* Extend the readTextLine()-level error information with higher-level
351  detail. This allows us to provide a more useful error report
352  ("Problem with line x") than just the rather low-level view provided
353  by readTextLine() ("Invalid character 0x8F at position 12"). The
354  argument handling is:
355 
356  printf( format, value );
357  printf( stream->errorInfo->errorString );
358 
359  so that 'format' has the form 'High-level error %d: ", to which the
360  low-level string is then appended */
361  retExtErr( status,
363  format, value ) );
364  }
365 
366 /* Exit with extended error information relating to header-line parsing */
367 
368 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
369 static int retHeaderError( INOUT STREAM *stream,
370  FORMAT_STRING const char *format,
371  IN_BUFFER( strArgLen ) char *strArg,
372  IN_LENGTH_SHORT const int strArgLen,
373  const int lineNo )
374  {
375 #ifdef USE_ERRMSGS
376  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
377 #endif /* USE_ERRMSGS */
378 
379  assert( isWritePtr( stream, sizeof( STREAM ) ) );
380  assert( isReadPtr( format, 4 ) );
381  assert( isWritePtr( strArg, strArgLen ) );
382 
383  REQUIRES( strArgLen > 0 && strArgLen < MAX_INTLENGTH_SHORT );
384 
385  /* Format the error information. We add two to the line number since
386  it's zero-based and the header counts as an extra line */
389  sanitiseString( strArg, strArgLen,
390  min( strArgLen, CRYPT_MAX_TEXTSIZE ) ),
391  lineNo + 2 ) );
392  }
393 
394 /* Send an HTTP error message. This function is somewhat unusually placed
395  with the general HTTP parsing functions because it's used by both read
396  and write code but needs access to the HTTP status decoding table, which
397  is part of the parsing code */
398 
399 STDC_NONNULL_ARG( ( 1, 2 ) ) \
400 int sendHTTPError( INOUT STREAM *stream,
401  IN_BUFFER( headerBufMaxLen ) char *headerBuffer,
402  IN_LENGTH_SHORT_MIN( 256 ) const int headerBufMaxLen,
403  IN_INT const int httpStatus )
404  {
405  STREAM headerStream;
406  const char *statusString = "400";
407  const char *errorString = "Bad Request";
408  int errorStringLength = 11, length = DUMMY_INIT, i, status;
409 
410  assert( isWritePtr( stream, sizeof( STREAM ) ) );
411  assert( isWritePtr( headerBuffer, headerBufMaxLen ) );
412 
413  REQUIRES( headerBufMaxLen >= 256 && \
414  headerBufMaxLen < MAX_INTLENGTH_SHORT );
415 
416  /* Find the HTTP error string that corresponds to the HTTP status
417  value */
418  for( i = 0; httpStatusInfo[ i ].httpStatus > 0 && \
419  httpStatusInfo[ i ].httpStatus != httpStatus && \
420  i < FAILSAFE_ARRAYSIZE( httpStatusInfo, HTTP_STATUS_INFO );
421  i++ );
422  ENSURES( i < FAILSAFE_ARRAYSIZE( httpStatusInfo, HTTP_STATUS_INFO ) );
423  if( httpStatusInfo[ i ].httpStatus > 0 )
424  {
425  statusString = httpStatusInfo[ i ].httpStatusString;
426  errorString = httpStatusInfo[ i ].httpErrorString;
427  errorStringLength = httpStatusInfo[ i ].httpErrorStringLength;
428  }
429 
430  /* Send the error message to the peer */
431  sMemOpen( &headerStream, headerBuffer, headerBufMaxLen );
432  swrite( &headerStream, isHTTP10( stream ) ? "HTTP/1.0 " : \
433  "HTTP/1.1 ", 9 );
434  swrite( &headerStream, statusString, HTTP_STATUSSTRING_LENGTH );
435  sputc( &headerStream, ' ' );
436  swrite( &headerStream, errorString, errorStringLength );
437  swrite( &headerStream, "\r\n", 2 );
438  if( httpStatus == 501 )
439  {
440  /* Since the assumption on the web is that anything listening for
441  HTTP requests is a conventional web server, we provide a bit more
442  information to (probable) browsers that connect and send a GET
443  request. This is also useful for some browsers which hang around
444  forever waiting for content if they don't see anything following
445  the HTTP error status */
446  swrite( &headerStream, "Content-Length: 139\r\n\r\n", 23 );
447  swrite( &headerStream,
448  "<html><head><title>Invalid PKI Server Request</title></head>"
449  "<body>This is a PKI messaging service, not a standard web "
450  "server.</body></html>", 139 );
451  }
452  status = swrite( &headerStream, "\r\n", 2 );
453  if( cryptStatusOK( status ) )
454  length = stell( &headerStream );
455  sMemDisconnect( &headerStream );
456  ENSURES( cryptStatusOK( status ) );
457  return( sendHTTPData( stream, headerBuffer, length,
459  }
460 
461 /****************************************************************************
462 * *
463 * URI Parsing Functions *
464 * *
465 ****************************************************************************/
466 
467 /* Information needed to parse a URI sub-segment: The character that ends a
468  segment and an optional alternative segment-end character, and the
469  minimum and maximum permitted segment size. The alternative segment-end
470  character is used for strings like:
471 
472  type-info [; more-info]
473 
474  where optional additional information may follow the value that we're
475  interested in, separated by a delimiter. The formatting of the parse
476  info is one of:
477 
478  endChar altEndChar Matches
479  ------- ---------- ------------------
480  x \0 ....x.... (Case 1)
481  x y ....x....
482  ....y....
483  \0 y .... (Case 2)
484  ....y.... */
485 
486 typedef struct {
487  const char segmentEndChar, altSegmentEndChar;
488  const int segmentMinLength, segmentMaxLength;
489  } URI_PARSE_INFO;
490 
491 /* Get the length of a sub-segment of a URI */
492 
493 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
494 static int getUriSegmentLength( IN_BUFFER( dataMaxLength ) const char *data,
495  IN_LENGTH_SHORT const int dataMaxLength,
496  OUT_LENGTH_SHORT_Z int *dataLength,
497  const URI_PARSE_INFO *uriParseInfo,
498  BOOLEAN *altDelimiterFound )
499  {
500  const int maxLength = min( dataMaxLength, uriParseInfo->segmentMaxLength );
501  int i;
502 
503  assert( isReadPtr( data, dataMaxLength ) );
504  assert( isWritePtr( dataLength, sizeof( int ) ) );
505  assert( isReadPtr( uriParseInfo, sizeof( URI_PARSE_INFO ) ) );
506  assert( ( uriParseInfo->altSegmentEndChar == '\0' && \
507  altDelimiterFound == NULL ) || \
508  ( uriParseInfo->altSegmentEndChar > '\0' && \
509  isWritePtr( altDelimiterFound, sizeof( BOOLEAN ) ) ) );
510 
511  REQUIRES( maxLength > 0 && maxLength < MAX_INTLENGTH_SHORT );
512  REQUIRES( uriParseInfo->segmentMinLength >= 0 && \
513  uriParseInfo->segmentMinLength < \
514  uriParseInfo->segmentMaxLength && \
515  uriParseInfo->segmentMaxLength <= 1024 );
516  REQUIRES( ( uriParseInfo->altSegmentEndChar == '\0' && \
517  altDelimiterFound == NULL ) || \
518  ( uriParseInfo->altSegmentEndChar > '\0' && \
519  altDelimiterFound != NULL ) );
520 
521  /* Clear return value */
522  *dataLength = 0;
523  if( altDelimiterFound != NULL )
524  *altDelimiterFound = FALSE;
525 
526  /* Parse the current query sub-segment */
527  for( i = 0; i < maxLength; i++ )
528  {
529  if( data[ i ] == uriParseInfo->segmentEndChar )
530  break;
531  if( uriParseInfo->altSegmentEndChar > '\0' && \
532  data[ i ] == uriParseInfo->altSegmentEndChar )
533  {
534  *altDelimiterFound = TRUE;
535  break;
536  }
537  }
538 
539  /* If there's an end-char specified (Case 1) and we didn't find it (or
540  the alternative end-char if there is one), it's an error. If there's
541  no end-char specified (Case 2), the end of the sub-segment is at the
542  end of the data */
543  if( uriParseInfo->segmentEndChar != '\0' && i >= dataMaxLength )
544  return( CRYPT_ERROR_BADDATA );
545 
546  /* Make sure that we both got enough data and that we didn't run out of
547  data */
548  if( i < uriParseInfo->segmentMinLength || \
549  i >= uriParseInfo->segmentMaxLength )
550  return( CRYPT_ERROR_BADDATA );
551 
552  *dataLength = i;
553  return( CRYPT_OK );
554  }
555 
556 /* Parse a URI of the form "* '?' attribute '=' value [ '&' ... ] ' ' ",
557  returning the parsed form to the caller (there's always a space at the
558  end because it's followed by the HTTP ID string). This function needs to
559  return two length values since it decodes the URI string according to RFC
560  1866, which means that its length can change. So as its standard return
561  value it returns the number of chars consumed, but it also returns the
562  new length of the input as a by-reference parameter */
563 
564 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
565 int parseUriInfo( INOUT_BUFFER( dataInLength, *dataOutLength ) char *data,
566  IN_LENGTH_SHORT const int dataInLength,
567  OUT_LENGTH_SHORT_Z int *dataOutLength,
568  INOUT HTTP_URI_INFO *uriInfo )
569  {
570  static const URI_PARSE_INFO locationParseInfo = \
571  { '?', '\0', 1, CRYPT_MAX_TEXTSIZE };
572  static const URI_PARSE_INFO attributeParseInfo = \
573  { '=', '\0', 3, CRYPT_MAX_TEXTSIZE };
574  static const URI_PARSE_INFO valueParseInfo = \
575  { ' ', '&', 3, CRYPT_MAX_TEXTSIZE };
576  static const URI_PARSE_INFO extraParseInfo = \
577  { ' ', '\0', 1, CRYPT_MAX_TEXTSIZE };
578  BOOLEAN altDelimiterFound;
579  const char *bufPtr = data;
580  int length = dataInLength, segmentLength, parsedLength, i, status;
581 
582  assert( isWritePtr( data, dataInLength ) );
583  assert( isWritePtr( dataOutLength, sizeof( int ) ) );
584  assert( isWritePtr( uriInfo, sizeof( HTTP_URI_INFO ) ) );
585 
586  REQUIRES( dataInLength > 0 && dataInLength < MAX_INTLENGTH_SHORT );
587 
588  /* Clear return values */
589  memset( uriInfo, 0, sizeof( HTTP_URI_INFO ) );
590  *dataOutLength = 0;
591 
592  /* Decode the URI text. Since there can be multiple nested levels of
593  encoding, we keep iteratively decoding in-place until either
594  decodeRFC1866() cries Uncle or we hit the sanity-check limit */
595  for( i = 0; i < FAILSAFE_ITERATIONS_SMALL; i++ )
596  {
597  status = decodeRFC1866( data, length );
598  if( cryptStatusError( status ) )
599  {
600  if( status == OK_SPECIAL )
601  /* There's been no further change in the data, exit */
602  break;
603  return( CRYPT_ERROR_BADDATA );
604  }
605  length = status; /* Record the new length of the decoded data */
606  }
607  if( i >= FAILSAFE_ITERATIONS_SMALL )
608  {
609  /* Sanity-check limit exceeded. This could be either a data error
610  or an internal error, since we can't automatically tell which it
611  is we report it as a data error */
612  return( CRYPT_ERROR_BADDATA );
613  }
614  *dataOutLength = length;
615 
616  /* We need to get at least 'x?xxx=xxx' */
617  if( length < 9 )
618  return( CRYPT_ERROR_BADDATA );
619 
620  /* Parse a URI of the form "* '?' attribute '=' value [ '&' ... ] ' ' ".
621  The URI is followed by the HTTP ID, so we know that it always has to
622  end on a space; running out of input is an error */
623  status = getUriSegmentLength( bufPtr, length, &segmentLength,
624  &locationParseInfo, NULL );
625  if( cryptStatusError( status ) )
626  return( status );
627  memcpy( uriInfo->location, bufPtr, segmentLength );
628  uriInfo->locationLen = segmentLength;
629  bufPtr += segmentLength + 1; /* Skip delimiter */
630  length -= segmentLength + 1;
631  parsedLength = segmentLength + 1;
632  status = getUriSegmentLength( bufPtr, length, &segmentLength,
633  &attributeParseInfo, NULL );
634  if( cryptStatusError( status ) )
635  return( status );
636  memcpy( uriInfo->attribute, bufPtr, segmentLength );
637  uriInfo->attributeLen = segmentLength;
638  bufPtr += segmentLength + 1; /* Skip delimiter */
639  length -= segmentLength + 1;
640  parsedLength += segmentLength + 1;
641  status = getUriSegmentLength( bufPtr, length, &segmentLength,
642  &valueParseInfo, &altDelimiterFound );
643  if( cryptStatusError( status ) )
644  return( status );
645  memcpy( uriInfo->value, bufPtr, segmentLength );
646  uriInfo->valueLen = segmentLength;
647  bufPtr += segmentLength + 1; /* Skip delimiter */
648  length -= segmentLength + 1;
649  parsedLength += segmentLength + 1;
650  if( altDelimiterFound )
651  {
652  status = getUriSegmentLength( bufPtr, length, &segmentLength,
653  &extraParseInfo, NULL );
654  if( cryptStatusError( status ) )
655  return( status );
656  memcpy( uriInfo->extraData, bufPtr, segmentLength );
657  uriInfo->extraDataLen = segmentLength;
658  parsedLength += segmentLength + 1;
659  }
660 
661  return( parsedLength );
662  }
663 
664 /* Check an "HTTP 1.x" ID string. No PKI client should be sending us a 0.9
665  ID, so we only allow 1.x */
666 
667 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
668 int checkHTTPID( IN_BUFFER( dataLength ) const char *data,
669  IN_LENGTH_SHORT const int dataLength,
670  INOUT STREAM *stream )
671  {
672  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
673 
674  assert( isReadPtr( data, dataLength ) );
675  assert( isWritePtr( stream, sizeof( STREAM ) ) );
676 
677  REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT );
678 
679  if( dataLength < 8 || strCompare( data, "HTTP/1.", 7 ) )
680  return( CRYPT_ERROR_BADDATA );
681  if( data[ 7 ] == '0' )
682  netStream->nFlags |= STREAM_NFLAG_HTTP10;
683  else
684  {
685  if( data[ 7 ] != '1' )
686  return( CRYPT_ERROR_BADDATA );
687  }
688 
689  return( 8 );
690  }
691 
692 /****************************************************************************
693 * *
694 * HTTP Header Processing *
695 * *
696 ****************************************************************************/
697 
698 /* Read an HTTP status code. Some status values are warnings only and
699  don't return an error status */
700 
701 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
702 static int readHTTPStatus( IN_BUFFER( dataLength ) const char *data,
703  IN_LENGTH_SHORT const int dataLength,
704  OUT_OPT_RANGE( 0, 999 ) int *httpStatus,
706  {
707  const HTTP_STATUS_INFO *httpStatusPtr;
708  const BOOLEAN isResponseStatus = ( httpStatus != NULL ) ? TRUE : FALSE;
709  char thirdChar;
710  int value, remainderLength, offset, i, status;
711 
712  assert( isReadPtr( data, dataLength ) );
713  assert( httpStatus == NULL || \
714  isWritePtr( httpStatus, sizeof( int ) ) );
715 
716  REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT );
717  REQUIRES( errorInfo != NULL );
718 
719  /* Clear return value */
720  if( httpStatus != NULL )
721  *httpStatus = 999;
722 
723  /* Check that the numeric value is in order, being exactly three
724  characters followed by a space */
725  if( dataLength < 3 || strSkipNonWhitespace( data, dataLength ) != 3 )
726  {
728  ( CRYPT_ERROR_BADDATA, errorInfo,
729  "Invalid/missing HTTP %sstatus code",
730  isResponseStatus ? "response " : "" ) );
731  }
732 
733  /* Process the three-digit numeric status code */
734  status = strGetNumeric( data, 3, &value, 1, 999 );
735  if( cryptStatusError( status ) )
736  {
738  ( CRYPT_ERROR_BADDATA, errorInfo,
739  "Invalid HTTP %sstatus code",
740  isResponseStatus ? "response " : "" ) );
741  }
742  if( httpStatus != NULL )
743  *httpStatus = value;
744 
745  /* Try and translate the HTTP status code into a cryptlib equivalent.
746  Most of the HTTP codes don't have any meaning in a cryptlib context
747  so they're mapped to a generic CRYPT_ERROR_READ by the HTTP status
748  decoding table */
749  thirdChar = data[ 2 ];
750  for( i = 0; httpStatusInfo[ i ].httpStatus != 0 && \
751  i < FAILSAFE_ARRAYSIZE( httpStatusInfo, HTTP_STATUS_INFO );
752  i++ )
753  {
754  /* We check the third digit (the one most likely to be different)
755  for a mismatch to avoid a large number of calls to the string-
756  compare function */
757  if( httpStatusInfo[ i ].httpStatusString[ 2 ] == thirdChar && \
758  !strCompare( data, httpStatusInfo[ i ].httpStatusString, 3 ) )
759  break;
760  }
761  ENSURES( i < FAILSAFE_ARRAYSIZE( httpStatusInfo, HTTP_STATUS_INFO ) );
762  httpStatusPtr = &httpStatusInfo[ i ];
763 
764  /* If we're doing a status read from something in a header line rather
765  than an HTTP response (for example a Warning line, which only
766  requires a status code but no status message), we're done */
767  if( !isResponseStatus )
768  return( CRYPT_OK );
769 
770  /* We're doing a status read from an HTTP response, make sure that
771  there's status text present alongside the status code */
772  remainderLength = dataLength - 3;
773  if( remainderLength < 2 || \
774  ( offset = strSkipWhitespace( data + 3, remainderLength ) ) < 0 || \
775  dataLength - offset < 1 )
776  {
778  ( CRYPT_ERROR_BADDATA, errorInfo,
779  "Missing HTTP response status text" ) );
780  }
781 
782  /* If it's a special-case condition such as a redirect, tell the caller
783  to handle it specially */
784  if( httpStatusPtr->status == OK_SPECIAL )
785  return( OK_SPECIAL );
786 
787  /* If it's an error condition, return extended error info (from the
788  information we have, not from any externally-supplied message) */
789  if( httpStatusPtr->status != CRYPT_OK )
790  {
791  assert( httpStatusPtr->httpStatusString != NULL );
792  /* Catch oddball errors in debug version */
793  retExt( httpStatusPtr->status,
794  ( httpStatusPtr->status, errorInfo,
795  "HTTP response status: %s",
796  httpStatusPtr->httpErrorString ) );
797  }
798 
799  return( CRYPT_OK );
800  }
801 
802 /* Process an HTTP header line looking for anything that we can handle */
803 
804 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
805 static int processHeaderLine( IN_BUFFER( dataLength ) const char *data,
806  IN_LENGTH_SHORT const int dataLength,
807  OUT_ENUM_OPT( HTTP_HEADER_TYPE ) \
808  HTTP_HEADER_TYPE *headerType,
809  INOUT ERROR_INFO *errorInfo,
810  IN_RANGE( 1, 999 ) const int errorLineNo )
811  {
812  const HTTP_HEADER_PARSE_INFO *headerParseInfoPtr = NULL;
813  const int firstChar = toUpper( *data );
814  int processedLength, dataLeft, i;
815 
816  assert( isReadPtr( data, dataLength ) );
817  assert( isWritePtr( headerType, sizeof( HTTP_HEADER_TYPE ) ) );
818 
819  REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT );
820  REQUIRES( errorLineNo > 0 && errorLineNo < 1000 );
821  REQUIRES( errorInfo != NULL );
822 
823  /* Clear return value */
824  *headerType = HTTP_HEADER_NONE;
825 
826  /* Look for a header line that we recognise */
827  for( i = 0;
828  httpHeaderParseInfo[ i ].headerString != NULL && \
829  i < FAILSAFE_ARRAYSIZE( httpHeaderParseInfo, \
830  HTTP_HEADER_PARSE_INFO );
831  i++ )
832  {
833  if( httpHeaderParseInfo[ i ].headerString[ 0 ] == firstChar && \
834  dataLength >= httpHeaderParseInfo[ i ].headerStringLen && \
835  !strCompare( data, httpHeaderParseInfo[ i ].headerString, \
836  httpHeaderParseInfo[ i ].headerStringLen ) )
837  {
838  headerParseInfoPtr = &httpHeaderParseInfo[ i ];
839  break;
840  }
841  }
842  ENSURES( i < FAILSAFE_ARRAYSIZE( httpHeaderParseInfo, \
843  HTTP_HEADER_PARSE_INFO ) );
844  if( headerParseInfoPtr == NULL )
845  {
846  /* It's nothing that we can handle, exit */
847  return( 0 );
848  }
849  processedLength = headerParseInfoPtr->headerStringLen;
850  dataLeft = dataLength - processedLength;
851 
852  /* Make sure that there's an attribute value present */
853  if( dataLeft > 0 )
854  {
855  const int extraLength = \
856  strSkipWhitespace( data + processedLength, dataLeft );
857  if( cryptStatusError( extraLength ) )
858  {
859  /* There was a problem, make sure that we fail the following
860  check */
861  dataLeft = CRYPT_ERROR;
862  }
863  else
864  {
865  if( extraLength > 0 )
866  {
867  /* We skipped some whitespace before the attribute value,
868  adjust the consumed/remaining byte counts */
869  dataLeft -= extraLength;
870  processedLength += extraLength;
871  }
872  }
873  }
874  if( dataLeft < 1 )
875  {
877  ( CRYPT_ERROR_BADDATA, errorInfo,
878  "Missing HTTP header value for '%s' token, line %d",
879  headerParseInfoPtr->headerString, errorLineNo ) );
880  }
881 
882  /* Tell the caller what we found */
883  *headerType = headerParseInfoPtr->headerType;
884  return( processedLength );
885  }
886 
887 /* Read the first line in an HTTP response header */
888 
889 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
890 int readFirstHeaderLine( INOUT STREAM *stream,
891  OUT_BUFFER_FIXED( dataMaxLength ) char *dataBuffer,
892  IN_LENGTH_SHORT const int dataMaxLength,
893  OUT_RANGE( 0, 999 ) int *httpStatus )
894  {
895  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
896  BOOLEAN textDataError;
897  int length, processedLength, dataLeft, status;
898 
899  assert( isWritePtr( stream, sizeof( STREAM ) ) );
900  assert( isWritePtr( dataBuffer, dataMaxLength ) );
901  assert( isWritePtr( httpStatus, sizeof( int ) ) );
902 
903  REQUIRES( dataMaxLength > 0 && dataMaxLength < MAX_INTLENGTH_SHORT );
904 
905  /* Clear return value */
906  *httpStatus = 999;
907 
908  /* Read the header and check for an HTTP ID */
909  status = readTextLine( readCharFunction, stream, dataBuffer,
910  dataMaxLength, &length, &textDataError );
911  if( cryptStatusError( status ) )
912  return( retTextLineError( stream, status, textDataError,
913  "Invalid HTTP header line 1: ", 0 ) );
914  if( length < 8 )
915  {
918  "Invalid HTTP header line length %d ", length ) );
919  }
920  processedLength = checkHTTPID( dataBuffer, length, stream );
921  if( cryptStatusError( processedLength ) )
922  {
923  retExt( cryptStatusError( processedLength ) ? \
924  processedLength : CRYPT_ERROR_BADDATA,
925  ( cryptStatusError( processedLength ) ? \
926  processedLength : CRYPT_ERROR_BADDATA,
927  NETSTREAM_ERRINFO, "Invalid HTTP ID/version" ) );
928  }
929  dataLeft = length - processedLength;
930 
931  /* Skip the whitespace between the HTTP ID and status info */
932  if( dataLeft > 0 )
933  {
934  const int extraLength = \
935  strSkipWhitespace( dataBuffer + processedLength, dataLeft );
936  if( cryptStatusError( extraLength ) )
937  {
938  /* There was a problem, make sure that we fail the following
939  check */
940  dataLeft = CRYPT_ERROR;
941  }
942  else
943  {
944  if( extraLength > 0 )
945  {
946  /* We skipped some whitespace before the HTTP status info,
947  adjust the consumed/remaining byte counts */
948  dataLeft -= extraLength;
949  processedLength += extraLength;
950  }
951  }
952  }
953  if( dataLeft < 1 )
954  {
955  retExt( CRYPT_ERROR_BADDATA,
956  ( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
957  "Missing HTTP status code, line 1" ) );
958  }
959 
960  /* Read the HTTP status info */
961  return( readHTTPStatus( dataBuffer + processedLength, dataLeft,
962  httpStatus, NETSTREAM_ERRINFO ) );
963  }
964 
965 /* Read the remaining HTTP header lines after the first one */
966 
967 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
968 int readHeaderLines( INOUT STREAM *stream,
969  OUT_BUFFER_FIXED( lineBufMaxLen ) char *lineBuffer,
970  IN_LENGTH_SHORT_MIN( 256 ) const int lineBufMaxLen,
971  INOUT HTTP_HEADER_INFO *headerInfo )
972  {
973  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
974  BOOLEAN seenHost = FALSE, seenLength = FALSE;
975  int contentLength = 0, lineCount, status;
976 
977  assert( isWritePtr( stream, sizeof( STREAM ) ) );
978  assert( isWritePtr( lineBuffer, lineBufMaxLen ) );
979  assert( isWritePtr( headerInfo, sizeof( HTTP_HEADER_INFO ) ) );
980 
981  REQUIRES( lineBufMaxLen >= 256 && lineBufMaxLen < MAX_INTLENGTH_SHORT );
982 
983  /* Read each line in the header checking for any fields that we need to
984  handle. We check for a couple of basic problems with the header to
985  avoid malformed-header attacks, for example an attacker could send a
986  request with two 'Content-Length:' headers, one of which covers the
987  entire message body and the other which indicates that there's a
988  second request that begins halfway through the message body. Some
989  proxies/caches will take the first length, some the second, if the
990  proxy is expected to check/rewrite the request as it passes through
991  then the single/dual-message issue can be used to bypass the checking
992  on the tunnelled second message. Because of this we only allow a
993  single Host: and Content-Length: header, and disallow a chunked
994  encoding in combination with a content-length (Apache does some
995  really strange things with chunked encodings). We can't be too
996  finicky with the checking though or we'll end up rejecting non-
997  malicious requests from some of the broken HTTP implementations out
998  there */
999  for( lineCount = 0; lineCount < FAILSAFE_ITERATIONS_MED; lineCount++ )
1000  {
1001  HTTP_HEADER_TYPE headerType;
1002  BOOLEAN textDataError;
1003  char *lineBufPtr;
1004  int lineLength;
1005 
1006  status = readTextLine( readCharFunction, stream, lineBuffer,
1007  lineBufMaxLen, &lineLength, &textDataError );
1008  if( cryptStatusError( status ) )
1009  {
1010  return( retTextLineError( stream, status, textDataError,
1011  "Invalid HTTP header line %d: ",
1012  lineCount + 2 ) );
1013  }
1014  if( lineLength <= 0 )
1015  {
1016  /* End of input, exit */
1017  break;
1018  }
1019  status = processHeaderLine( lineBuffer, lineLength, &headerType,
1020  NETSTREAM_ERRINFO, lineCount + 2 );
1021  if( cryptStatusError( status ) )
1022  return( status );
1023  lineBufPtr = lineBuffer + status;
1024  lineLength -= status;
1025  ENSURES( lineLength > 0 ); /* Guaranteed by processHeaderLine() */
1026  switch( headerType )
1027  {
1028  case HTTP_HEADER_HOST:
1029  /* Make sure that it's a non-duplicate, and remember that
1030  we've seen a Host: line, to meet the HTTP 1.1
1031  requirements */
1032  if( seenHost )
1033  {
1034  retExt( CRYPT_ERROR_BADDATA,
1035  ( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
1036  "Duplicate HTTP 'Host:' header, line %d",
1037  lineCount + 2 ) );
1038  }
1039  seenHost = TRUE;
1040  break;
1041 
1042  case HTTP_HEADER_CONTENT_LENGTH:
1043  /* Make sure that it's a non-duplicate and get the content
1044  length. At this point all that we do is perform a
1045  general sanity check that the length looks OK, a specific
1046  check against the caller-supplied minimum/maximum
1047  allowable length is performed later since the content
1048  length may also be provided as a chunked encoding length,
1049  which we can't check until we've processed all of the
1050  header lines */
1051  if( seenLength )
1052  {
1053  retExt( CRYPT_ERROR_BADDATA,
1054  ( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
1055  "Duplicate HTTP 'Content-Length:' header, "
1056  "line %d", lineCount + 2 ) );
1057  }
1058  status = strGetNumeric( lineBufPtr, lineLength,
1059  &contentLength, 1, MAX_INTLENGTH );
1060  if( cryptStatusError( status ) )
1061  {
1062  retExt( CRYPT_ERROR_BADDATA,
1063  ( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
1064  "Invalid HTTP content length, line %d",
1065  lineCount + 2 ) );
1066  }
1067  seenLength = TRUE;
1068  break;
1069 
1070  case HTTP_HEADER_CONTENT_TYPE:
1071  {
1072  static const URI_PARSE_INFO typeParseInfo = \
1073  { '/', '\0', 2, CRYPT_MAX_TEXTSIZE };
1074  static const URI_PARSE_INFO subtypeParseInfo = \
1075  { '\0', ';', 2, CRYPT_MAX_TEXTSIZE };
1076  BOOLEAN dummy;
1077  char *contentType;
1078  int contentTypeLen, subTypeLen;
1079 
1080  /* Sometimes if there's an error it'll be returned as content
1081  at the HTTP level rather than at the tunnelled-over-HTTP
1082  protocol level. The easiest way to check for this would
1083  be to make sure that the content-type matches the
1084  expected type and report anything else as an error.
1085  Unfortunately due to the hit-and-miss handling of content-
1086  types by PKI software using HTTP as a substrate it's not
1087  safe to do this, so we have to default to allow-all
1088  rather than deny-all, treating only straight text as a
1089  problem type.
1090 
1091  To compound the problem there are also apps out there
1092  that send their PKI messages marked as plain text, so
1093  this isn't 100% foolproof. This is particularly
1094  problematic for web browsers, where so many servers were
1095  misconfigured to return pretty much anything as
1096  text/plain that Microsoft added content-type guessing
1097  code to MSIE to make web pages served from misconfigured
1098  servers work (you can see this by serving a JPEG file as
1099  text/plain, MSIE will display it as a JPEG while Mozilla/
1100  Firefox/Opera/etc will display it as text or prompt for a
1101  helper app to handle it). Since this content-type
1102  guessing is a potential security hole, MS finally made it
1103  configurable in Windows XP SP2, but it's still enabled
1104  by default even there.
1105 
1106  In practice however errors-via-HTTP is more common than
1107  certs-via-text. We try and detect the cert-as-plain-text
1108  special-case at a later point when we've got the message
1109  body available.
1110 
1111  Since we're now looking at the content-type line (even if
1112  we don't really process it in any way), we perform at
1113  least a minimal validity check for * '/' * [ ';*' ] */
1114  status = getUriSegmentLength( lineBufPtr, lineLength,
1115  &contentTypeLen,
1116  &typeParseInfo, NULL );
1117  if( cryptStatusError( status ) )
1118  {
1119  /* We need to have at least "xx/"* present (length is
1120  guaranteed by getUriSegmentLength()) */
1121  return( retHeaderError( stream,
1122  "Invalid HTTP content type '%s', line %d",
1123  lineBufPtr, lineLength, lineCount ) );
1124  }
1125  contentType = lineBufPtr;
1126  lineBufPtr += contentTypeLen + 1; /* Skip delimiter */
1127  lineLength -= contentTypeLen + 1;
1128  status = getUriSegmentLength( lineBufPtr, lineLength,
1129  &subTypeLen,
1130  &subtypeParseInfo, &dummy );
1131  if( cryptStatusError( status ) )
1132  {
1133  /* We need to have at least 'xx/yy' present (length is
1134  guaranteed by getUriSegmentLength()) */
1135  return( retHeaderError( stream,
1136  "Invalid HTTP content subtype '%s', line %d",
1137  lineBufPtr, lineLength, lineCount ) );
1138  }
1139  if( contentTypeLen == 4 && \
1140  !strCompare( contentType, "text", 4 ) )
1141  headerInfo->flags |= HTTP_FLAG_TEXTMSG;
1142  break;
1143  }
1144 
1145  case HTTP_HEADER_TRANSFER_ENCODING:
1146  if( lineLength < 7 || \
1147  strCompare( lineBufPtr, "Chunked", 7 ) )
1148  {
1149  return( retHeaderError( stream,
1150  "Invalid HTTP transfer encoding method "
1151  "'%s', expected 'Chunked', line %d",
1152  lineBufPtr, lineLength, lineCount ) );
1153  }
1154 
1155  /* If it's a chunked encoding, the length is part of the
1156  data and must be read later */
1157  if( seenLength )
1158  {
1159  retExt( CRYPT_ERROR_BADDATA,
1160  ( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
1161  "Duplicate HTTP 'Content-Length:' header, "
1162  "line %d", lineCount + 2 ) );
1163  }
1164  headerInfo->flags |= HTTP_FLAG_CHUNKED;
1165  seenLength = TRUE;
1166  break;
1167 
1168  case HTTP_HEADER_CONTENT_ENCODING:
1169  /* We can't handle any type of content encoding (e.g. gzip,
1170  compress, deflate, mpeg4, interpretive dance) except the
1171  no-op identity encoding */
1172  if( lineLength < 8 || \
1173  strCompare( lineBufPtr, "Identity", 8 ) )
1174  {
1175  headerInfo->httpStatus = 415; /* Unsupp.media type */
1176  return( retHeaderError( stream,
1177  "Invalid HTTP content encoding method "
1178  "'%s', expected 'Identity', line %d",
1179  lineBufPtr, lineLength, lineCount ) );
1180  }
1181  break;
1182 
1183  case HTTP_HEADER_CONTENT_TRANSFER_ENCODING:
1184  /* HTTP uses Transfer-Encoding, not the MIME Content-
1185  Transfer-Encoding types such as base64 or quoted-
1186  printable. If any implementations erroneously use a
1187  C-T-E, we make sure that it's something that we can
1188  handle */
1189  if( !( lineLength >= 6 && \
1190  !strCompare( lineBufPtr, "Binary", 6 ) ) && \
1191  !( lineLength >= 8 && \
1192  !strCompare( lineBufPtr, "Identity", 8 ) ) )
1193  {
1194  headerInfo->httpStatus = 415; /* Unsupp.media type */
1195  return( retHeaderError( stream,
1196  "Invalid HTTP content transfer encoding "
1197  "method '%s', expected 'Identity' or "
1198  "'Binary', line %d", lineBufPtr, lineLength,
1199  lineCount ) );
1200  }
1201  break;
1202 
1203  case HTTP_HEADER_TRAILER:
1204  /* The body is followed by trailer lines, used with chunked
1205  encodings where some header lines can't be produced until
1206  the entire body has been generated. This wasn't added
1207  until RFC 2616, since many implementations are based on
1208  RFC 2068 and don't produce this header we don't do
1209  anything with it. The trailer can be auto-detected
1210  anyway, it's only present to tell the receiver to perform
1211  certain actions such as creating an MD5 hash of the data
1212  as it arrives */
1213  headerInfo->flags |= HTTP_FLAG_TRAILER;
1214  break;
1215 
1216  case HTTP_HEADER_CONNECTION:
1217  /* If the other side has indicated that it's going to close
1218  the connection, remember that the stream is now no longer
1219  usable */
1220  if( lineLength >= 5 && \
1221  !strCompare( lineBufPtr, "Close", 5 ) )
1222  sioctlSet( stream, STREAM_IOCTL_CONNSTATE, FALSE );
1223  break;
1224 
1225  case HTTP_HEADER_WARNING:
1226  /* Read the HTTP status info from the warning.
1227  readHTTPStatus() will process the error status in the
1228  warning line, but since we're passing in a NULL pointer
1229  for the status info it'll only report an error in the
1230  warning content itself, it won't return the processed
1231  warning status as an error */
1232  status = readHTTPStatus( lineBufPtr, lineLength, NULL,
1234  if( cryptStatusError( status ) )
1235  {
1236  return( retHeaderError( stream,
1237  "Invalid HTTP warning information '%s', "
1238  "line %d", lineBufPtr, lineLength,
1239  lineCount ) );
1240  }
1241  break;
1242 
1243  case HTTP_HEADER_LOCATION:
1244  {
1245 #if defined( __WIN32__ ) && !defined( NDEBUG ) && 1
1246  URL_INFO urlInfo;
1247 #endif /* Win32 debug build only */
1248 
1249  /* Make sure that we've been given an HTTP URL as the
1250  redirect location. We need to do this because
1251  sNetParseURL() will accept a wide range of URL types
1252  while we only allow "http://"* */
1253  if( lineLength < 10 || \
1254  strCompare( lineBufPtr, "http://", 7 ) )
1255  {
1256  return( retHeaderError( stream,
1257  "Invalid HTTP redirect location '%s', line %d",
1258  lineBufPtr, lineLength, lineCount ) );
1259  }
1260 
1261  /* Process the redirect location */
1262 #if defined( __WIN32__ ) && !defined( NDEBUG ) && 1
1263  /* We don't try and parse the URL other than in the Win32
1264  debug build because we don't do redirects yet so there's
1265  no need to expose ourselves to possibly maliciously-
1266  created URLs from external sources */
1267  status = sNetParseURL( &urlInfo, lineBufPtr, lineLength,
1268  URL_TYPE_HTTP );
1269  if( cryptStatusError( status ) )
1270  {
1271  return( retHeaderError( stream,
1272  "Invalid HTTP redirect location '%s', line %d",
1273  lineBufPtr, lineLength, lineCount ) );
1274  }
1275 #endif /* Win32 debug build only */
1276  break;
1277  }
1278 
1279  case HTTP_HEADER_EXPECT:
1280  /* If the other side wants the go-ahead to continue, give it
1281  to them. We do this automatically because we're merely
1282  using HTTP as a substrate, the real decision will be made
1283  at the higher-level protocol layer */
1284  if( lineLength >= 12 && \
1285  !strCompare( lineBufPtr, "100-Continue", 12 ) )
1286  sendHTTPError( stream, lineBufPtr, lineBufMaxLen, 100 );
1287  break;
1288 
1289  case HTTP_HEADER_NONE:
1290  /* It's something that we don't know/care about, skip it */
1291  break;
1292 
1293  default:
1294  retIntError();
1295  }
1296  }
1297  if( lineCount >= FAILSAFE_ITERATIONS_MED )
1298  {
1301  "Too many HTTP header lines" ) );
1302  }
1303 
1304  /* If this is a tunnel being opened via an HTTP proxy, we're done */
1305  if( !( netStream->nFlags & STREAM_NFLAG_ISSERVER ) && \
1306  ( netStream->nFlags & STREAM_NFLAG_HTTPTUNNEL ) )
1307  return( CRYPT_OK );
1308 
1309  /* If it's a chunked encoding for which the length is kludged on before
1310  the data as a hex string, decode the length value */
1311  if( headerInfo->flags & HTTP_FLAG_CHUNKED )
1312  {
1313  BOOLEAN textDataError;
1314  int lineLength;
1315 
1316  status = readTextLine( readCharFunction, stream, lineBuffer,
1317  lineBufMaxLen, &lineLength, &textDataError );
1318  if( cryptStatusError( status ) )
1319  {
1320  return( retTextLineError( stream, status, textDataError,
1321  "Invalid HTTP chunked encoding "
1322  "header line %d: ", lineCount + 2 ) );
1323  }
1324  if( lineLength <= 0 )
1325  {
1326  retExt( CRYPT_ERROR_BADDATA,
1327  ( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
1328  "Missing HTTP chunk length, line %d", lineCount + 2 ) );
1329  }
1330  status = contentLength = getChunkLength( lineBuffer, lineLength );
1331  if( cryptStatusError( status ) )
1332  {
1333  retExt( CRYPT_ERROR_BADDATA,
1334  ( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
1335  "Invalid length for HTTP chunked encoding, line %d",
1336  lineCount + 2 ) );
1337  }
1338  }
1339 
1340  /* If this is a no-op read (for example lines following an error or 100
1341  Continue response), all that we're interested in is draining the
1342  input, so we don't check any further */
1343  if( headerInfo->flags & HTTP_FLAG_NOOP )
1344  return( CRYPT_OK );
1345 
1346  /* If we're a server talking HTTP 1.1 and we haven't seen a "Host:"
1347  header from the client, return an error */
1348  if( ( netStream->nFlags & STREAM_NFLAG_ISSERVER ) && \
1349  !isHTTP10( stream ) && !seenHost )
1350  {
1351  headerInfo->httpStatus = 400; /* Bad request */
1352  retExt( CRYPT_ERROR_BADDATA,
1353  ( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
1354  "Missing HTTP 1.1 'Host:' header" ) );
1355  }
1356 
1357  /* If it's a GET request there's no length so we can exit now */
1358  if( headerInfo->flags & HTTP_FLAG_GET )
1359  {
1360  if( seenLength )
1361  {
1362  retExt( CRYPT_ERROR_BADDATA,
1363  ( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
1364  "Unexpected %d bytes HTTP body content received in "
1365  "idempotent read", contentLength ) );
1366  }
1367  return( CRYPT_OK );
1368  }
1369 
1370  /* Make sure that we've been given a length. In theory a server could
1371  indicate the length implicitly by closing the connection once it's
1372  sent the last byte, but this isn't allowed for PKI messages. The
1373  client can't use this option either since that would make it
1374  impossible for us to send back the response */
1375  if( !seenLength )
1376  {
1377  headerInfo->httpStatus = 411; /* Length required */
1378  retExt( CRYPT_ERROR_BADDATA,
1379  ( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
1380  "Missing HTTP length" ) );
1381  }
1382 
1383  /* Make sure that the length is sensible */
1384  if( contentLength < headerInfo->minContentLength || \
1385  contentLength > headerInfo->maxContentLength )
1386  {
1387  retExt( ( contentLength < headerInfo->minContentLength ) ? \
1389  ( ( contentLength < headerInfo->minContentLength ) ? \
1390  CRYPT_ERROR_UNDERFLOW : CRYPT_ERROR_OVERFLOW,
1392  "Invalid HTTP content length %d bytes, expected "
1393  "%d...%d bytes", contentLength,
1394  headerInfo->minContentLength,
1395  headerInfo->maxContentLength ) );
1396  }
1397  headerInfo->contentLength = contentLength;
1398 
1399  return( CRYPT_OK );
1400  }
1401 
1402 /* Read the HTTP trailer lines that follow chunked data:
1403 
1404  CRLF
1405  "0" CRLF
1406  trailer-lines*
1407  CRLF */
1408 
1409 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
1410 int readTrailerLines( INOUT STREAM *stream,
1411  OUT_BUFFER_FIXED( lineBufMaxLen ) char *lineBuffer,
1412  IN_LENGTH_SHORT_MIN( 256 ) const int lineBufMaxLen )
1413  {
1414 #ifdef USE_ERRMSGS
1415  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
1416 #endif /* USE_ERRMSGS */
1417  HTTP_HEADER_INFO headerInfo;
1418  BOOLEAN textDataError;
1419  int readLength = DUMMY_INIT, dummy, status;
1420 
1421  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1422  assert( isReadPtr( lineBuffer, lineBufMaxLen ) );
1423 
1424  REQUIRES( lineBufMaxLen >= 256 && lineBufMaxLen < MAX_INTLENGTH_SHORT );
1425 
1426  /* Read the blank line and chunk length */
1427  status = readTextLine( readCharFunction, stream, lineBuffer,
1428  lineBufMaxLen, &dummy, &textDataError );
1429  if( cryptStatusOK( status ) )
1430  status = readTextLine( readCharFunction, stream, lineBuffer,
1431  lineBufMaxLen, &readLength, &textDataError );
1432  if( cryptStatusError( status ) )
1433  return( retTextLineError( stream, status, textDataError,
1434  "Invalid HTTP chunked trailer line: ",
1435  0 ) );
1436 
1437  /* Make sure that there are no more chunks to follow */
1438  status = getChunkLength( lineBuffer, readLength );
1439  if( status != 0 )
1440  {
1441  retExt( CRYPT_ERROR_BADDATA,
1442  ( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
1443  "Unexpected additional data following HTTP chunked "
1444  "data" ) );
1445  }
1446 
1447  /* Read any remaining trailer lines */
1448  initHeaderInfo( &headerInfo, 0, 0, HTTP_FLAG_NOOP );
1449  return( readHeaderLines( stream, lineBuffer, lineBufMaxLen,
1450  &headerInfo ) );
1451  }
1452 #endif /* USE_HTTP */