[PATCH v2] httpd: Pass custom HTTP headers to CGI scripts

Alexander Vickberg wickbergster at gmail.com
Sat Mar 30 12:12:09 UTC 2019


Inspired by the links below I added a limit of 8k for HTTP headers. If more
a status of 413 Entity Too Large will be sent. Don't do explicit free
anymore. Let all headers which start with HTTP_ be handled by the list
creating code. This saves some space.

https://bugs.busybox.net/show_bug.cgi?id=9611
https://stackoverflow.com/questions/686217/maximum-on-http-header-values
http://httpd.apache.org/docs/2.2/mod/core.html#limitrequestfieldsize

Bloatcheck:
function                                             old     new   delta
http_response                                        160     176     +16
http_response_type                                    20      22      +2
httpd_main                                           874     871      -3
send_file_and_exit                                   802     796      -6
send_headers                                        1004     992     -12
handle_incoming_and_exit                            2832    2737     -95
send_cgi_and_exit                                   1016     914    -102
.rodata                                           156610  156485    -125
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/6 up/down: 18/-343)          Total: -325
bytes
   text    data     bss     dec     hex filename
1012236   17687    1888 1031811   fbe83 busybox_old
1011941   17719    1888 1031548   fbd7c busybox_unstripped

diff --git a/networking/httpd.c b/networking/httpd.c
index b52526a78..c27cd3001 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -267,6 +267,7 @@
 #define DEBUG 0

 #define IOBUF_SIZE 8192
+#define MAX_HTTP_HEADER_SIZE (8*1024)
 #if PIPE_BUF >= IOBUF_SIZE
 # error "PIPE_BUF >= IOBUF_SIZE"
 #endif
@@ -305,6 +306,12 @@ typedef struct Htaccess_Proxy {
  char *url_to;
 } Htaccess_Proxy;

+typedef struct HTTP_Header {
+    struct HTTP_Header *next;
+    char *name;
+    char *value;
+} HTTP_Header;
+
 enum {
  HTTP_OK = 200,
  HTTP_PARTIAL_CONTENT = 206,
@@ -316,6 +323,7 @@ enum {
  HTTP_REQUEST_TIMEOUT = 408,
  HTTP_NOT_IMPLEMENTED = 501,   /* used for unrecognized requests */
  HTTP_INTERNAL_SERVER_ERROR = 500,
+ HTTP_ENTITY_TOO_LARGE = 413,
  HTTP_CONTINUE = 100,
 #if 0   /* future use */
  HTTP_SWITCHING_PROTOCOLS = 101,
@@ -347,6 +355,7 @@ static const uint16_t http_response_type[] ALIGN2 = {
  HTTP_BAD_REQUEST,
  HTTP_FORBIDDEN,
  HTTP_INTERNAL_SERVER_ERROR,
+ HTTP_ENTITY_TOO_LARGE,
 #if 0   /* not implemented */
  HTTP_CREATED,
  HTTP_ACCEPTED,
@@ -377,6 +386,7 @@ static const struct {
  { "Bad Request", "Unsupported method" },
  { "Forbidden", ""  },
  { "Internal Server Error", "Internal Server Error" },
+ { "Entity Too Large", "Entity Too Large" },
 #if 0   /* not implemented */
  { "Created" },
  { "Accepted" },
@@ -412,11 +422,7 @@ struct globals {

  IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
  IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
- IF_FEATURE_HTTPD_CGI(char *referer;)
- IF_FEATURE_HTTPD_CGI(char *user_agent;)
- IF_FEATURE_HTTPD_CGI(char *host;)
- IF_FEATURE_HTTPD_CGI(char *http_accept;)
- IF_FEATURE_HTTPD_CGI(char *http_accept_language;)
+ IF_FEATURE_HTTPD_CGI(HTTP_Header *hdr_list;)

  off_t file_size;        /* -1 - unknown */
 #if ENABLE_FEATURE_HTTPD_RANGES
@@ -1437,7 +1443,6 @@ static void setenv1(const char *name, const char
*value)
  * const char *url              The requested URL (with leading /).
  * const char *orig_uri         The original URI before rewriting (if any)
  * int post_len                 Length of the POST body.
- * const char *cookie           For set HTTP_COOKIE.
  * const char *content_type     For set CONTENT_TYPE.
  */
 static void send_cgi_and_exit(
@@ -1445,14 +1450,12 @@ static void send_cgi_and_exit(
  const char *orig_uri,
  const char *request,
  int post_len,
- const char *cookie,
  const char *content_type) NORETURN;
 static void send_cgi_and_exit(
  const char *url,
  const char *orig_uri,
  const char *request,
  int post_len,
- const char *cookie,
  const char *content_type)
 {
  struct fd_pair fromCgi;  /* CGI -> httpd pipe */
@@ -1531,15 +1534,8 @@ static void send_cgi_and_exit(
 #endif
  }
  }
- setenv1("HTTP_USER_AGENT", G.user_agent);
- if (G.http_accept)
- setenv1("HTTP_ACCEPT", G.http_accept);
- if (G.http_accept_language)
- setenv1("HTTP_ACCEPT_LANGUAGE", G.http_accept_language);
  if (post_len)
  putenv(xasprintf("CONTENT_LENGTH=%u", post_len));
- if (cookie)
- setenv1("HTTP_COOKIE", cookie);
  if (content_type)
  setenv1("CONTENT_TYPE", content_type);
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
@@ -1548,12 +1544,17 @@ static void send_cgi_and_exit(
  putenv((char*)"AUTH_TYPE=Basic");
  }
 #endif
- if (G.referer)
- setenv1("HTTP_REFERER", G.referer);
- setenv1("HTTP_HOST", G.host); /* set to "" if NULL */
  /* setenv1("SERVER_NAME", safe_gethostname()); - don't do this,
  * just run "env SERVER_NAME=xyz httpd ..." instead */

+ if (G.hdr_list) {
+ HTTP_Header *cur = G.hdr_list;
+ do {
+ setenv1(cur->name, cur->value);
+ cur = cur->next;
+ } while (cur != G.hdr_list);
+ }
+
  xpiped_pair(fromCgi);
  xpiped_pair(toCgi);

@@ -2077,12 +2078,13 @@ static void handle_incoming_and_exit(const
len_and_sockaddr *fromAddr)
  char *urlcopy;
  char *urlp;
  char *tptr;
+ int hdr_len = 0;
 #if ENABLE_FEATURE_HTTPD_CGI
  static const char request_HEAD[] ALIGN1 = "HEAD";
  const char *prequest;
- char *cookie = NULL;
  char *content_type = NULL;
  unsigned long length = 0;
+ HTTP_Header *last = NULL;
 #elif ENABLE_FEATURE_HTTPD_PROXY
 #define prequest request_GET
  unsigned long length = 0;
@@ -2263,8 +2265,11 @@ static void handle_incoming_and_exit(const
len_and_sockaddr *fromAddr)

  /* Read until blank line */
  while (1) {
- if (!get_line())
+ int iobuf_len = get_line();
+ if (!iobuf_len)
  break; /* EOF or error or empty line */
+ if ((hdr_len += iobuf_len) > MAX_HTTP_HEADER_SIZE)
+ send_headers_and_exit(HTTP_ENTITY_TOO_LARGE);
  if (DEBUG)
  bb_error_msg("header: '%s'", iobuf);

@@ -2301,32 +2306,8 @@ static void handle_incoming_and_exit(const
len_and_sockaddr *fromAddr)
  }
  }
 #endif
-#if ENABLE_FEATURE_HTTPD_CGI
- else if (STRNCASECMP(iobuf, "Cookie:") == 0) {
- if (!cookie) /* in case they send millions of these, do not OOM */
- cookie = xstrdup(skip_whitespace(iobuf + sizeof("Cookie:")-1));
- } else if (STRNCASECMP(iobuf, "Content-Type:") == 0) {
- if (!content_type)
- content_type = xstrdup(skip_whitespace(iobuf +
sizeof("Content-Type:")-1));
- } else if (STRNCASECMP(iobuf, "Referer:") == 0) {
- if (!G.referer)
- G.referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1));
- } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) {
- if (!G.user_agent)
- G.user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1));
- } else if (STRNCASECMP(iobuf, "Host:") == 0) {
- if (!G.host)
- G.host = xstrdup(skip_whitespace(iobuf + sizeof("Host:")-1));
- } else if (STRNCASECMP(iobuf, "Accept:") == 0) {
- if (!G.http_accept)
- G.http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1));
- } else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) {
- if (!G.http_accept_language)
- G.http_accept_language = xstrdup(skip_whitespace(iobuf +
sizeof("Accept-Language:")-1));
- }
-#endif
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
- if (STRNCASECMP(iobuf, "Authorization:") == 0) {
+ else if (STRNCASECMP(iobuf, "Authorization:") == 0) {
  /* We only allow Basic credentials.
  * It shows up as "Authorization: Basic <user>:<passwd>" where
  * "<user>:<passwd>" is base64 encoded.
@@ -2341,7 +2322,7 @@ static void handle_incoming_and_exit(const
len_and_sockaddr *fromAddr)
  }
 #endif
 #if ENABLE_FEATURE_HTTPD_RANGES
- if (STRNCASECMP(iobuf, "Range:") == 0) {
+ else if (STRNCASECMP(iobuf, "Range:") == 0) {
  /* We know only bytes=NNN-[MMM] */
  char *s = skip_whitespace(iobuf + sizeof("Range:")-1);
  if (is_prefixed_with(s, "bytes=")) {
@@ -2358,7 +2339,7 @@ static void handle_incoming_and_exit(const
len_and_sockaddr *fromAddr)
  }
 #endif
 #if ENABLE_FEATURE_HTTPD_GZIP
- if (STRNCASECMP(iobuf, "Accept-Encoding:") == 0) {
+ else if (STRNCASECMP(iobuf, "Accept-Encoding:") == 0) {
  /* Note: we do not support "gzip;q=0"
  * method of _disabling_ gzip
  * delivery. No one uses that, though */
@@ -2373,6 +2354,46 @@ static void handle_incoming_and_exit(const
len_and_sockaddr *fromAddr)
  //}
  }
  }
+#endif
+#if ENABLE_FEATURE_HTTPD_CGI
+ else if (STRNCASECMP(iobuf, "Content-Type:") == 0) {
+ if (!content_type)
+ content_type = xstrdup(skip_whitespace(iobuf +
sizeof("Content-Type:")-1));
+ } else {
+ HTTP_Header *cur;
+ char *after_colon = strchr(iobuf, ':');
+ char *ch = iobuf;
+
+ if (!after_colon)
+ continue;
+
+ cur = xmalloc(sizeof(HTTP_Header));
+ *after_colon++ = '\0';
+
+ while (*ch) {
+ if (isalpha(*ch))
+ *ch &= ~0x20;
+ else if (!isdigit(*ch))
+ *ch = '_';
+ ch++;
+ }
+
+ cur->name = xmalloc(sizeof("HTTP_")+strlen(iobuf));
+ memcpy(cur->name, "HTTP_", sizeof("HTTP_")-1);
+ strcpy(cur->name + sizeof("HTTP_")-1, iobuf);
+ cur->value = xstrdup(skip_whitespace(after_colon));
+
+ /* Insert new header into header list */
+ if (!G.hdr_list) {
+ G.hdr_list = cur;
+ cur->next = G.hdr_list;
+ last = cur;
+ } else {
+ cur->next = G.hdr_list;
+ last->next = cur;
+ last = cur;
+ }
+ }
 #endif
  } /* while extra header reading */
  }
@@ -2435,7 +2456,7 @@ static void handle_incoming_and_exit(const
len_and_sockaddr *fromAddr)
  /* protect listing "cgi-bin/" */
  send_headers_and_exit(HTTP_FORBIDDEN);
  }
- send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie,
content_type);
+ send_cgi_and_exit(urlcopy, urlcopy, prequest, length, content_type);
  }
 #endif

@@ -2456,7 +2477,7 @@ static void handle_incoming_and_exit(const
len_and_sockaddr *fromAddr)
  Htaccess *cur;
  for (cur = script_i; cur; cur = cur->next) {
  if (strcmp(cur->before_colon + 1, suffix) == 0) {
- send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie,
content_type);
+ send_cgi_and_exit(urlcopy, urlcopy, prequest, length, content_type);
  }
  }
  }
@@ -2470,7 +2491,7 @@ static void handle_incoming_and_exit(const
len_and_sockaddr *fromAddr)
  * Try cgi-bin/index.cgi */
  if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
  urlp[0] = '\0'; /* remove index_page */
- send_cgi_and_exit("/cgi-bin/index.cgi", urlcopy, prequest, length,
cookie, content_type);
+ send_cgi_and_exit("/cgi-bin/index.cgi", urlcopy, prequest, length,
content_type);
  }
  }
  /* else fall through to send_file, it errors out if open fails: */
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.busybox.net/pipermail/busybox/attachments/20190330/9416d553/attachment-0001.html>


More information about the busybox mailing list