fielding 99/02/09 08:57:26
Modified: src ApacheCore.def CHANGES src/include ap_mmn.h httpd.h src/main http_protocol.c util.c src/support httpd.exp Log: Refix the entity tag comparisons for If-Range, If-Match and If-None-Match. Comparisons need to be case sensitive and in all but one case (cache update) need to be strong comparisons, taking into account the possibility that any of the current Etag or the one(s) received from the client may be weak. Changed name of ap_find_list_item() to ap_size_list_item() to be consistent with other find routines and added new (simple) ap_find_list_item(), whose implementation will eventually be replaced with something more efficient. PR: 2065, 3657 Submitted by: Roy Fielding, Ken Coar, Dean Gaudet Revision Changes Path 1.11 +2 -2 apache-1.3/src/ApacheCore.def Index: ApacheCore.def =================================================================== RCS file: /home/cvs/apache-1.3/src/ApacheCore.def,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- ApacheCore.def 1999/02/08 15:12:15 1.10 +++ ApacheCore.def 1999/02/09 16:57:19 1.11 @@ -324,9 +324,9 @@ ap_make_etag @317 ap_array_pstrcat @318 ap_os_is_filename_valid @319 - ap_find_opaque_token @320 + ap_find_list_item @320 ap_MD5Encode @321 ap_validate_password @322 - ap_find_list_item @323 + ap_size_list_item @323 ap_get_list_item @324 1.1242 +11 -5 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.1241 retrieving revision 1.1242 diff -u -r1.1241 -r1.1242 --- CHANGES 1999/02/09 16:38:07 1.1241 +++ CHANGES 1999/02/09 16:57:20 1.1242 @@ -1,16 +1,22 @@ Changes with Apache 1.3.5 + *) Entity tag comparisons for If-Match and If-None-Match were not being + performed correctly -- weak tags might cause false positives. Also, + strong comparison wasn't properly enforced in all cases. + [Roy Fielding, Ken Coar, Dean Gaudet] PR#2065, 3657 + *) OS/2: Supply OS/2 error code instead of errno on semaphore errors. [Brian Havard] *) Work around a bug in Lynx regarding its sending "Negotiate: trans" even though it doesn't understand TCN. [Koen Holtman, Roy Fielding] - *) Added ap_find_list_item() and ap_get_list_item() to util.c for parsing - an HTTP header field value to extract the next list item, taking into - account the possible presence of nested comments, quoted-pairs, - and quoted-strings. ap_get_list_item() also removes insignificant - whitespace and lowercases non-quoted tokens. [Roy Fielding] PR#2065 + *) Added ap_size_list_item(), ap_get_list_item(), and ap_find_list_item() + to util.c for parsing an HTTP header field value to extract the next + list item, taking into account the possible presence of nested comments, + quoted-pairs, and quoted-strings. ap_get_list_item() also removes + insignificant whitespace and lowercases non-quoted tokens. + [Roy Fielding] PR#2065 *) proxy: The various calls to ap_proxyerror() can return HTTP/1.1 status code different from 500. This allows the proxy to, e.g., return 1.28 +3 -2 apache-1.3/src/include/ap_mmn.h Index: ap_mmn.h =================================================================== RCS file: /home/cvs/apache-1.3/src/include/ap_mmn.h,v retrieving revision 1.27 retrieving revision 1.28 diff -u -r1.27 -r1.28 --- ap_mmn.h 1999/02/09 12:29:52 1.27 +++ ap_mmn.h 1999/02/09 16:57:22 1.28 @@ -207,7 +207,8 @@ * 19990108.1 - add ap_MD5Encode() for MD5 password handling. * 19990108.2 - add ap_validate_password() and change ap_MD5Encode() * to use a stronger algorithm. - * 19990108.3 - add ap_find_list_item() and ap_get_list_item() + * 19990108.4 - add ap_size_list_item(), ap_get_list_item(), and + * ap_find_list_item() */ #define MODULE_MAGIC_COOKIE 0x41503133UL /* "AP13" */ @@ -215,7 +216,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 19990108 #endif -#define MODULE_MAGIC_NUMBER_MINOR 3 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 4 /* 0...n */ #define MODULE_MAGIC_NUMBER MODULE_MAGIC_NUMBER_MAJOR /* backward compat */ /* Useful for testing for features. */ 1.269 +3 -1 apache-1.3/src/include/httpd.h Index: httpd.h =================================================================== RCS file: /home/cvs/apache-1.3/src/include/httpd.h,v retrieving revision 1.268 retrieving revision 1.269 diff -u -r1.268 -r1.269 --- httpd.h 1999/02/09 12:29:53 1.268 +++ httpd.h 1999/02/09 16:57:22 1.269 @@ -932,8 +932,10 @@ API_EXPORT(char *) ap_getword_conf(pool *p, const char **line); API_EXPORT(char *) ap_getword_conf_nc(pool *p, char **line); -API_EXPORT(const char *) ap_find_list_item(const char **field, int *len); +API_EXPORT(const char *) ap_size_list_item(const char **field, int *len); API_EXPORT(char *) ap_get_list_item(pool *p, const char **field); +API_EXPORT(int) ap_find_list_item(pool *p, const char *line, const char *tok); + API_EXPORT(char *) ap_get_token(pool *p, const char **accept_line, int accept_white); API_EXPORT(int) ap_find_token(pool *p, const char *line, const char *tok); API_EXPORT(int) ap_find_last_token(pool *p, const char *line, const char *tok); 1.257 +40 -21 apache-1.3/src/main/http_protocol.c Index: http_protocol.c =================================================================== RCS file: /home/cvs/apache-1.3/src/main/http_protocol.c,v retrieving revision 1.256 retrieving revision 1.257 diff -u -r1.256 -r1.257 --- http_protocol.c 1999/02/09 12:29:57 1.256 +++ http_protocol.c 1999/02/09 16:57:24 1.257 @@ -144,16 +144,18 @@ return 0; } - /* Check the If-Range header for Etag or Date */ - + /* Check the If-Range header for Etag or Date. + * Note that this check will return false (as required) if either + * of the two etags are weak. + */ if ((if_range = ap_table_get(r->headers_in, "If-Range"))) { if (if_range[0] == '"') { if (!(match = ap_table_get(r->headers_out, "Etag")) || - (strcasecmp(if_range, match) != 0)) + (strcmp(if_range, match) != 0)) return 0; } else if (!(match = ap_table_get(r->headers_out, "Last-Modified")) || - (strcasecmp(if_range, match) != 0)) + (strcmp(if_range, match) != 0)) return 0; } @@ -398,13 +400,14 @@ mtime = (r->mtime != 0) ? r->mtime : time(NULL); /* If an If-Match request-header field was given - * AND if our ETag does not match any of the entity tags in that field - * AND the field value is not "*" (meaning match anything), then + * AND the field value is not "*" (meaning match anything) + * AND if our strong ETag does not match any entity tag in that field, * respond with a status of 412 (Precondition Failed). */ if ((if_match = ap_table_get(r->headers_in, "If-Match")) != NULL) { - if ((etag == NULL) || - ((if_match[0] != '*') && !ap_find_token(r->pool, if_match, etag))) { + if (if_match[0] != '*' && + (etag == NULL || etag[0] == 'W' || + !ap_find_list_item(r->pool, if_match, etag))) { return HTTP_PRECONDITION_FAILED; } } @@ -425,22 +428,38 @@ } /* If an If-None-Match request-header field was given - * AND if our ETag matches any of the entity tags in that field - * OR if the field value is "*" (meaning match anything), then - * if the request method was GET or HEAD, the server SHOULD - * respond with a 304 (Not Modified) response. - * For all other request methods, the server MUST - * respond with a status of 412 (Precondition Failed). + * AND the field value is "*" (meaning match anything) + * OR our ETag matches any of the entity tags in that field, fail. + * + * If the request method was GET or HEAD, failure means the server + * SHOULD respond with a 304 (Not Modified) response. + * For all other request methods, failure means the server MUST + * respond with a status of 412 (Precondition Failed). + * + * GET or HEAD allow weak etag comparison, all other methods require + * strong comparison. We can only use weak if it's not a range request. */ if_nonematch = ap_table_get(r->headers_in, "If-None-Match"); if (if_nonematch != NULL) { - int rstatus; - - if ((if_nonematch[0] == '*') - || ((etag != NULL) && ap_find_token(r->pool, if_nonematch, etag))) { - rstatus = (r->method_number == M_GET) ? HTTP_NOT_MODIFIED - : HTTP_PRECONDITION_FAILED; - return rstatus; + if (r->method_number == M_GET) { + if (if_nonematch[0] == '*') + return HTTP_NOT_MODIFIED; + if (etag != NULL) { + if (ap_table_get(r->headers_in, "Range")) { + if (etag[0] != 'W' && + ap_find_list_item(r->pool, if_nonematch, etag)) { + return HTTP_NOT_MODIFIED; + } + } + else if (strstr(if_nonematch, etag)) { + return HTTP_NOT_MODIFIED; + } + } + } + else if (if_nonematch[0] == '*' || + (etag != NULL && + ap_find_list_item(r->pool, if_nonematch, etag))) { + return HTTP_PRECONDITION_FAILED; } } /* Else if a valid If-Modified-Since request-header field was given 1.150 +36 -9 apache-1.3/src/main/util.c Index: util.c =================================================================== RCS file: /home/cvs/apache-1.3/src/main/util.c,v retrieving revision 1.149 retrieving revision 1.150 diff -u -r1.149 -r1.150 --- util.c 1999/02/09 12:29:58 1.149 +++ util.c 1999/02/09 16:57:24 1.150 @@ -978,16 +978,16 @@ } } -/* Find an HTTP header field list item, as separated by a comma. +/* Size an HTTP header field list item, as separated by a comma. * The return value is a pointer to the beginning of the non-empty list item * within the original string (or NULL if there is none) and the address * of field is shifted to the next non-comma, non-whitespace character. * len is the length of the item excluding any beginning whitespace. */ -API_EXPORT(const char *) ap_find_list_item(const char **field, int *len) +API_EXPORT(const char *) ap_size_list_item(const char **field, int *len) { - const char *ptr = *field; - const char *token; + const unsigned char *ptr = (const unsigned char *)*field; + const unsigned char *token; int in_qpair, in_qstr, in_com; /* Find first non-comma, non-whitespace byte */ @@ -1035,7 +1035,7 @@ ++ptr; *field = ptr; - return token; + return (const char *)token; } /* Retrieve an HTTP header field list item, as separated by a comma, @@ -1046,14 +1046,16 @@ */ API_EXPORT(char *) ap_get_list_item(pool *p, const char **field) { - const char *tok_start, *ptr; - char *token, *pos; + const char *tok_start; + const unsigned char *ptr; + unsigned char *pos; + char *token; int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0; /* Find the beginning and maximum length of the list item so that * we can allocate a buffer for the new string and reset the field. */ - if ((tok_start = ap_find_list_item(field, &tok_len)) == NULL) { + if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) { return NULL; } token = ap_palloc(p, tok_len + 1); @@ -1063,7 +1065,7 @@ * strip comments, and lowercase normal characters not within a * quoted-string or quoted-pair. The result may be an empty string. */ - for (ptr = tok_start, pos = token; + for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token; *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); ++ptr) { @@ -1129,6 +1131,31 @@ return token; } + +/* Find an item in canonical form (lowercase, no extra spaces) within + * an HTTP field value list. Returns 1 if found, 0 if not found. + * This would be much more efficient if we stored header fields as + * an array of list items as they are received instead of a plain string. + * We could make it more efficient by duplicating the loop/switch above + * within this function, replacing the assignments with compares. + */ +API_EXPORT(int) ap_find_list_item(pool *p, const char *line, const char *tok) +{ + const char *nxt; + char *item; + + if (!line || !tok) + return 0; + + nxt = line; + + while ((item = ap_get_list_item(p, &nxt)) != NULL) { + if (strcmp(item, tok) == 0) + return 1; + } + return 0; +} + /* Retrieve a token, spacing over it and returning a pointer to * the first non-white byte afterwards. Note that these tokens 1.15 +1 -0 apache-1.3/src/support/httpd.exp Index: httpd.exp =================================================================== RCS file: /home/cvs/apache-1.3/src/support/httpd.exp,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- httpd.exp 1999/02/08 15:12:21 1.14 +++ httpd.exp 1999/02/09 16:57:26 1.15 @@ -312,6 +312,7 @@ ap_signal ap_single_module_configure ap_single_module_init +ap_size_list_item ap_slack ap_snprintf ap_soft_timeout