*/
while(1) {
apr_status_t rv;
- int folded = 0;
field = NULL;
rv = ap_rgetline(&field, r->server->limit_req_fieldsize + 2,
return;
}
- if (last_field != NULL) {
- if ((len > 0) && ((*field == '\t') || *field == ' ')) {
- /* This line is a continuation of the preceding line(s),
- * so append it to the line that we've set aside.
- * Note: this uses a power-of-two allocator to avoid
- * doing O(n) allocs and using O(n^2) space for
- * continuations that span many many lines.
- */
- apr_size_t fold_len = last_len + len + 1; /* trailing null */
+ /* Process an obs-fold immediately by appending it to last_field */
+ if ((*field == '\t') || *field == ' ') {
- if (fold_len >= (apr_size_t)(r->server->limit_req_fieldsize)) {
- const char *field_escaped;
+ if (last_field == NULL) {
+ r->status = HTTP_BAD_REQUEST;
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03442)
+ "Line folding encountered before first"
+ " header line");
+ return;
+ }
- r->status = HTTP_BAD_REQUEST;
- /* report what we have accumulated so far before the
- * overflow (last_field) as the field with the problem
- */
- field_escaped = ap_escape_html(r->pool, last_field);
- apr_table_setn(r->notes, "error-notes",
- apr_psprintf(r->pool,
- "Size of a request header field "
- "after folding "
- "exceeds server limit.<br />\n"
- "<pre>\n%.*s\n</pre>\n",
- field_name_len(field_escaped),
- field_escaped));
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00562)
- "Request header exceeds LimitRequestFieldSize "
- "after folding: %.*s",
- field_name_len(last_field), last_field);
- return;
- }
+ /* This line is a continuation of the preceding line(s),
+ * so append it to the line that we've set aside.
+ * Note: this uses a power-of-two allocator to avoid
+ * doing O(n) allocs and using O(n^2) space for
+ * continuations that span many many lines.
+ */
+ apr_size_t fold_len = last_len + len + 1; /* trailing null */
+
+ if (fold_len >= (apr_size_t)(r->server->limit_req_fieldsize)) {
+ const char *field_escaped;
+ r->status = HTTP_BAD_REQUEST;
+ /* report what we have accumulated so far before the
+ * overflow (last_field) as the field with the problem
+ */
+ field_escaped = ap_escape_html(r->pool, last_field);
+ apr_table_setn(r->notes, "error-notes",
+ apr_psprintf(r->pool,
+ "Size of a request header field after folding"
+ " exceeds server limit.<br />\n"
+ "<pre>\n%.*s\n</pre>\n",
+ field_name_len(field_escaped),
+ field_escaped));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00562)
+ "Request header exceeds LimitRequestFieldSize "
+ "after folding: %.*s",
+ field_name_len(last_field), last_field);
+ return;
+ }
+
+ if (fold_len > alloc_len) {
+ char *fold_buf;
+ alloc_len += alloc_len;
if (fold_len > alloc_len) {
- char *fold_buf;
- alloc_len += alloc_len;
- if (fold_len > alloc_len) {
- alloc_len = fold_len;
- }
- fold_buf = (char *)apr_palloc(r->pool, alloc_len);
- memcpy(fold_buf, last_field, last_len);
- last_field = fold_buf;
- }
- memcpy(last_field + last_len, field, len +1); /* +1 for nul */
- /* Replace obs-fold w/ SP per RFC 7230 3.2.4 */
- if (conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT) {
- last_field[last_len] = ' ';
+ alloc_len = fold_len;
}
- last_len += len;
- folded = 1;
+ fold_buf = (char *)apr_palloc(r->pool, alloc_len);
+ memcpy(fold_buf, last_field, last_len);
+ last_field = fold_buf;
}
- else /* not a continuation line */ {
-
- if (r->server->limit_req_fields
- && (++fields_read > r->server->limit_req_fields)) {
- r->status = HTTP_BAD_REQUEST;
- apr_table_setn(r->notes, "error-notes",
- "The number of request header fields "
- "exceeds this server's limit.");
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00563)
- "Number of request headers exceeds "
- "LimitRequestFields");
- return;
- }
+ memcpy(last_field + last_len, field, len +1); /* +1 for nul */
+ /* Replace obs-fold w/ SP per RFC 7230 3.2.4 */
+ if (conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT) {
+ last_field[last_len] = ' ';
+ }
+ last_len += len;
- if (!(conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT))
- {
- /* Not Strict, using the legacy parser */
-
- if (!(value = strchr(last_field, ':'))) { /* Find ':' or */
- r->status = HTTP_BAD_REQUEST; /* abort bad request */
- apr_table_setn(r->notes, "error-notes",
- apr_psprintf(r->pool,
- "Request header field is "
- "missing ':' separator.<br />\n"
- "<pre>\n%.*s</pre>\n",
- (int)LOG_NAME_MAX_LEN,
- ap_escape_html(r->pool,
- last_field)));
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00564)
- "Request header field is missing ':' "
- "separator: %.*s", (int)LOG_NAME_MAX_LEN,
- last_field);
- return;
- }
+ /* The obs-fold continuation line is merged to the last_field
+ * so continue to the next input line
+ */
+ continue;
+ }
- tmp_field = value - 1; /* last character of field-name */
+ /* Not a continuation line, so process the last_field fully composed */
- *value++ = '\0'; /* NUL-terminate at colon */
+ if (r->server->limit_req_fields
+ && (++fields_read > r->server->limit_req_fields)) {
+ r->status = HTTP_BAD_REQUEST;
+ apr_table_setn(r->notes, "error-notes",
+ "The number of request header fields "
+ "exceeds this server's limit.");
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00563)
+ "Number of request headers exceeds "
+ "LimitRequestFields");
+ return;
+ }
- while (*value == ' ' || *value == '\t') {
- ++value; /* Skip to start of value */
- }
+ if (!(conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT))
+ {
+ /* Not Strict, using the legacy parser */
- /* Strip LWS after field-name: */
- while (tmp_field > last_field
- && (*tmp_field == ' ' || *tmp_field == '\t')) {
- *tmp_field-- = '\0';
- }
+ if (!(value = strchr(last_field, ':'))) { /* Find ':' or */
+ r->status = HTTP_BAD_REQUEST; /* abort bad request */
+ apr_table_setn(r->notes, "error-notes",
+ apr_psprintf(r->pool,
+ "Request header field is "
+ "missing ':' separator.<br />\n"
+ "<pre>\n%.*s</pre>\n", (int)LOG_NAME_MAX_LEN,
+ ap_escape_html(r->pool, last_field)));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00564)
+ "Request header field is missing ':' "
+ "separator: %.*s", (int)LOG_NAME_MAX_LEN,
+ last_field);
+ return;
+ }
- /* Strip LWS after field-value: */
- tmp_field = last_field + last_len - 1;
- while (tmp_field > value
- && (*tmp_field == ' ' || *tmp_field == '\t')) {
- *tmp_field-- = '\0';
- }
- }
- else /* Using strict RFC7230 parsing */
- {
- /* Ensure valid token chars before ':' per RFC 7230 3.2.4 */
- if (!(value = (char *)ap_scan_http_token(last_field))
- || *value != ':') {
- r->status = HTTP_BAD_REQUEST;
- apr_table_setn(r->notes, "error-notes",
- apr_psprintf(r->pool,
- "Request header field name "
- "is malformed.<br />\n"
- "<pre>\n%.*s</pre>\n",
- (int)LOG_NAME_MAX_LEN,
- ap_escape_html(r->pool, last_field)));
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02426)
- "Request header field name is malformed: "
- "%.*s", (int)LOG_NAME_MAX_LEN, last_field);
- return;
- }
+ tmp_field = value - 1; /* last character of field-name */
- *value++ = '\0'; /* NUL-terminate last_field name at ':' */
+ *value++ = '\0'; /* NUL-terminate at colon */
- while (*value == ' ' || *value == '\t') {
- ++value; /* Skip LWS of value */
- }
+ while (*value == ' ' || *value == '\t') {
+ ++value; /* Skip to start of value */
+ }
- /* Find invalid, non-HT ctrl char, or the trailing NULL */
- tmp_field = (char *)ap_scan_http_field_content(value);
-
- /* Strip LWS after field-value, if string not empty */
- if (*value && (*tmp_field == '\0')) {
- tmp_field--;
- while (*tmp_field == ' ' || *tmp_field == '\t') {
- *tmp_field-- = '\0';
- }
- ++tmp_field;
- }
+ /* Strip LWS after field-name: */
+ while (tmp_field > last_field
+ && (*tmp_field == ' ' || *tmp_field == '\t')) {
+ *tmp_field-- = '\0';
+ }
- /* Reject value for all garbage input (CTRLs excluding HT)
- * e.g. only VCHAR / SP / HT / obs-text are allowed per
- * RFC7230 3.2.6 - leave all more explicit rule enforcement
- * for specific header handler logic later in the cycle
- */
- if (*tmp_field != '\0') {
- r->status = HTTP_BAD_REQUEST;
- apr_table_setn(r->notes, "error-notes",
- apr_psprintf(r->pool,
- "Request header value "
- "is malformed.<br />\n"
- "<pre>\n%.*s</pre>\n",
- (int)LOG_NAME_MAX_LEN,
- ap_escape_html(r->pool, value)));
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02427)
- "Request header value is malformed: "
- "%.*s", (int)LOG_NAME_MAX_LEN, value);
- return;
- }
- }
+ /* Strip LWS after field-value: */
+ tmp_field = last_field + last_len - 1;
+ while (tmp_field > value
+ && (*tmp_field == ' ' || *tmp_field == '\t')) {
+ *tmp_field-- = '\0';
+ }
+ }
+ else /* Using strict RFC7230 parsing */
+ {
+ /* Ensure valid token chars before ':' per RFC 7230 3.2.4 */
+ value = (char *)ap_scan_http_token(last_field);
+ if ((value == last_field) || *value != ':') {
+ r->status = HTTP_BAD_REQUEST;
+ apr_table_setn(r->notes, "error-notes",
+ apr_psprintf(r->pool,
+ "Request header field name"
+ " is malformed.<br />\n"
+ "<pre>\n%.*s</pre>\n", (int)LOG_NAME_MAX_LEN,
+ ap_escape_html(r->pool, last_field)));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02426)
+ "Request header field name is malformed: "
+ "%.*s", (int)LOG_NAME_MAX_LEN, last_field);
+ return;
+ }
- apr_table_addn(r->headers_in, last_field, value);
+ *value++ = '\0'; /* NUL-terminate last_field name at ':' */
- /* reset the alloc_len so that we'll allocate a new
- * buffer if we have to do any more folding: we can't
- * use the previous buffer because its contents are
- * now part of r->headers_in
- */
- alloc_len = 0;
+ while (*value == ' ' || *value == '\t') {
+ ++value; /* Skip LWS of value */
+ }
+
+ /* Find invalid, non-HT ctrl char, or the trailing NULL */
+ tmp_field = (char *)ap_scan_http_field_content(value);
+
+ /* Strip LWS after field-value, if string not empty */
+ if (*value && (*tmp_field == '\0')) {
+ tmp_field--;
+ while (*tmp_field == ' ' || *tmp_field == '\t') {
+ *tmp_field-- = '\0';
+ }
+ ++tmp_field;
+ }
- } /* end if current line is not a continuation starting with tab */
+ /* Reject value for all garbage input (CTRLs excluding HT)
+ * e.g. only VCHAR / SP / HT / obs-text are allowed per
+ * RFC7230 3.2.6 - leave all more explicit rule enforcement
+ * for specific header handler logic later in the cycle
+ */
+ if (*tmp_field != '\0') {
+ r->status = HTTP_BAD_REQUEST;
+ apr_table_setn(r->notes, "error-notes",
+ apr_psprintf(r->pool,
+ "Request header value is malformed.<br />\n"
+ "<pre>\n%.*s</pre>\n", (int)LOG_NAME_MAX_LEN,
+ ap_escape_html(r->pool, value)));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02427)
+ "Request header value is malformed: "
+ "%.*s", (int)LOG_NAME_MAX_LEN, value);
+ return;
+ }
}
- /* Found a blank line, stop. */
+ apr_table_addn(r->headers_in, last_field, value);
+
+ /* After recording the last_field header line, we end our read loop
+ * here upon encountering the trailing line terminating headers
+ */
if (len == 0) {
break;
}
- /* Keep track of this line so that we can parse it on
- * the next loop iteration. (In the folded case, last_field
- * has been updated already.)
+ /* Keep track of this new header line (not an obs-fold). We will merge
+ * subsequent obs_fold lines on the next loop iterations. The zero
+ * alloc_len signals we have not allocated an obs-folding buffer yet
*/
- if (!folded) {
- last_field = field;
- last_len = len;
- }
+ alloc_len = 0;
+ last_field = field;
+ last_len = len;
}
/* Combine multiple message-header fields with the same