On 2023/06/29 19:55:52 +0200, Florian Obser <flor...@openbsd.org> wrote:
> I'm worried that we pass un-sanitized input through to fcgi.
> Of course we are passing *a lot* of un-sanitized input through to fcgi,
> so does this matter in the grand scheme of things?
> But I'd like if server_http_parsehost() enforces syntactically valid
> hostnames first.
> 
> There is valid_domain() in usr.bin/ssh/misc.c or res_hnok in
> resolv.h. Which is probably fine to use since we don't care too much
> about portable.

right, httpd is happily accepting any nonsense as Host.  Here's an
adaptation of millert' valid_domain() from usr.bin/ssh/misc.c.
Hope noone is relying on using an empty/malformed Host :>

diff /usr/src
commit - 9832f5035d799ae12e9f848cff5650481b673260
path + /usr/src
blob - 091cc86fec946a4a9d422c04a48f5cbfae015ce0
file + usr.sbin/httpd/server_http.c
--- usr.sbin/httpd/server_http.c
+++ usr.sbin/httpd/server_http.c
@@ -831,6 +831,42 @@ char *
        return (buf);
 }
 
+static int
+valid_domain(char *name, const char **errstr)
+{
+       size_t i;
+       unsigned char c, last = '\0';
+
+       *errstr = NULL;
+
+       if (*name == '\0') {
+               *errstr = "empty domain name";
+               return 0;
+       }
+       if (!isalpha((unsigned char)name[0]) &&
+           !isdigit((unsigned char)name[0])) {
+               *errstr = "domain name starts with an invalid character";
+               return 0;
+       }
+       for (i = 0; name[i] != '\0'; ++i) {
+               c = tolower((unsigned char)name[i]);
+               name[i] = c;
+               if (last == '.' && c == '.') {
+                       *errstr = "domain name contains consecutive separators";
+                       return 0;
+               }
+               if (c != '.' && c != '-' && !isalnum(c) &&
+                   c != '_') /* technically invalid, but common */ {
+                       *errstr = "domain name contains invalid characters";
+                       return 0;
+               }
+               last = c;
+       }
+       if (name[i - 1] == '.')
+               name[i - 1] = '\0';
+       return 1;
+}
+
 char *
 server_http_parsehost(char *host, char *buf, size_t len, int *portval)
 {
@@ -863,6 +899,11 @@ server_http_parsehost(char *host, char *buf, size_t le
                port = NULL;
        }
 
+       if (!valid_domain(start, &errstr)) {
+               log_debug("%s: %s: %s", __func__, errstr, host);
+               return (NULL);
+       }
+
        if (port != NULL) {
                /* Save the requested port */
                *portval = strtonum(port, 0, 0xffff, &errstr);

Reply via email to