Module Name: src Committed By: martin Date: Fri Aug 24 11:41:16 UTC 2018
Modified Files: src/libexec/httpd: bozohttpd.8 bozohttpd.c src/libexec/httpd/testsuite: Makefile Added Files: src/libexec/httpd/testsuite: t12.in t12.out t13.in t13.out src/libexec/httpd/testsuite/data: .bzremap Log Message: Add support for remapping requested paths via a .bzredirect file. Fixes PR 52772. Ok: mrg@ To generate a diff of this commit: cvs rdiff -u -r1.68 -r1.69 src/libexec/httpd/bozohttpd.8 cvs rdiff -u -r1.87 -r1.88 src/libexec/httpd/bozohttpd.c cvs rdiff -u -r1.7 -r1.8 src/libexec/httpd/testsuite/Makefile cvs rdiff -u -r0 -r1.1 src/libexec/httpd/testsuite/t12.in \ src/libexec/httpd/testsuite/t12.out src/libexec/httpd/testsuite/t13.in \ src/libexec/httpd/testsuite/t13.out cvs rdiff -u -r0 -r1.1 src/libexec/httpd/testsuite/data/.bzremap Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/libexec/httpd/bozohttpd.8 diff -u src/libexec/httpd/bozohttpd.8:1.68 src/libexec/httpd/bozohttpd.8:1.69 --- src/libexec/httpd/bozohttpd.8:1.68 Tue Nov 28 12:22:27 2017 +++ src/libexec/httpd/bozohttpd.8 Fri Aug 24 11:41:16 2018 @@ -1,4 +1,4 @@ -.\" $NetBSD: bozohttpd.8,v 1.68 2017/11/28 12:22:27 wiz Exp $ +.\" $NetBSD: bozohttpd.8,v 1.69 2018/08/24 11:41:16 martin Exp $ .\" .\" $eterna: bozohttpd.8,v 1.101 2011/11/18 01:25:11 mrg Exp $ .\" @@ -489,6 +489,37 @@ will redirect to Otherwise provided schema will be used i.e. symbolic link to .Em ftp://NetBSD.org/ will redirect to the provided URL. +If a +.Pa .bzremap +file is found at the root of a (virtual) server, it is expected to contain +rewrite mappings for URLs. +.Pp +These remappings are performed internally in the server before authentication +happens and can be used to hide implementation details, like the CGI handler +specific suffix for non cgi scripts in authorized directories. +.Pp +The map file consists of lines two paths separated by a colon, where the left +side needs to exactly match a (sub) path of the request and will be replaced +by the right side. +.Pp +The first match always wins. +.Pp +A +.Pa .bzremap +file could look like this: +.Bd -literal +/nic/update:/auth-dir/updipv4.pl +.Ed +.Pp +The remap file should be short, access to it is slow and needs to happen +on each request. +If a request path needs to include a colon +.Pq Li \&: +character, it can be escaped +with a backslash +.Pq Li \e +The right hand side of the colon is always used verbatim, no escape sequences +are interpreted. .Sh EXAMPLES To configure set of virtual hosts, one would use an .Xr inetd.conf 5 @@ -554,7 +585,7 @@ The focus has always been simplicity and and regular code audits. This manual documents .Nm -version 20170201. +version 20180824. .Sh AUTHORS .An -nosplit .Nm Index: src/libexec/httpd/bozohttpd.c diff -u src/libexec/httpd/bozohttpd.c:1.87 src/libexec/httpd/bozohttpd.c:1.88 --- src/libexec/httpd/bozohttpd.c:1.87 Sun Jan 28 13:37:39 2018 +++ src/libexec/httpd/bozohttpd.c Fri Aug 24 11:41:16 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: bozohttpd.c,v 1.87 2018/01/28 13:37:39 maya Exp $ */ +/* $NetBSD: bozohttpd.c,v 1.88 2018/08/24 11:41:16 martin Exp $ */ /* $eterna: bozohttpd.c,v 1.178 2011/11/18 09:21:15 mrg Exp $ */ @@ -109,7 +109,7 @@ #define INDEX_HTML "index.html" #endif #ifndef SERVER_SOFTWARE -#define SERVER_SOFTWARE "bozohttpd/20170201" +#define SERVER_SOFTWARE "bozohttpd/20180824" #endif #ifndef DIRECT_ACCESS_FILE #define DIRECT_ACCESS_FILE ".bzdirect" @@ -120,6 +120,15 @@ #ifndef ABSREDIRECT_FILE #define ABSREDIRECT_FILE ".bzabsredirect" #endif +#ifndef REMAP_FILE +#define REMAP_FILE ".bzremap" +#endif + +/* + * When you add some .bz* file, make sure to also check it in + * bozo_check_special_files() + */ + #ifndef PUBLIC_HTML #define PUBLIC_HTML "public_html" #endif @@ -149,6 +158,7 @@ #include <signal.h> #include <stdarg.h> #include <stdlib.h> +#include <stdbool.h> #include <string.h> #include <syslog.h> #include <time.h> @@ -1069,6 +1079,154 @@ head: } /* + * Like strncmp(), but s_esc may contain characters escaped by \. + * The len argument does not include the backslashes used for escaping, + * that is: it gives the raw len, after unescaping the string. + */ +static int +esccmp(const char *s_plain, const char *s_esc, size_t len) +{ + bool esc = false; + + while (len) { + if (!esc && *s_esc == '\\') { + esc = true; + s_esc++; + continue; + } + esc = false; + if (*s_plain == 0 || *s_esc == 0 || *s_plain != *s_esc) + return *s_esc - *s_plain; + s_esc++; + s_plain++; + len--; + } + return 0; +} + +/* + * Check if the request refers to a uri that is mapped via a .bzremap. + * We have /requested/path:/re/mapped/to/this.html lines in there, + * and the : separator may be use in the left hand side escaped with + * \ to encode a path containig a : character. + */ +static void +check_mapping(bozo_httpreq_t *request) +{ + bozohttpd_t *httpd = request->hr_httpd; + char *file = request->hr_file, *newfile; + void *fmap; + const char *replace, *map_to, *p; + struct stat st; + int mapfile; + size_t avail, len, rlen, reqlen, num_esc = 0; + bool escaped = false; + + mapfile = open(REMAP_FILE, O_RDONLY, 0); + if (mapfile == -1) + return; + debug((httpd, DEBUG_FAT, "remap file found")); + if (fstat(mapfile, &st) == -1) { + bozowarn(httpd, "could not stat " REMAP_FILE ", errno: %d", + errno); + close(mapfile); + return; + } + + fmap = mmap(NULL, st.st_size, PROT_READ, 0, mapfile, 0); + if (fmap == NULL) { + bozowarn(httpd, "could not mmap " REMAP_FILE ", error %d", + errno); + close(mapfile); + return; + } + reqlen = strlen(file); + for (p = fmap, avail = st.st_size; avail; ) { + /* + * We have lines like: + * /this/url:/replacement/that/url + * If we find a matching left hand side, replace will point + * to it and len will be its length. map_to will point to + * the right hand side and rlen wil be its length. + * If we have no match, both pointers will be NULL. + */ + + /* skip empty lines */ + while ((*p == '\r' || *p == '\n') && avail) { + p++; + avail--; + } + replace = p; + escaped = false; + while (avail) { + if (*p == '\r' || *p == '\n') + break; + if (!escaped && *p == ':') + break; + if (escaped) { + escaped = false; + num_esc++; + } else if (*p == '\\') { + escaped = true; + } + p++; + avail--; + } + if (!avail || *p != ':') { + replace = NULL; + map_to = NULL; + break; + } + len = p - replace - num_esc; + /* + * reqlen < len: the left hand side is too long, can't be a + * match + * reqlen == len: full string has to match + * reqlen > len: make sure there is a path separator at 'len' + * avail < 2: we are at eof, missing right hand side + */ + if (avail < 2 || reqlen < len || + (reqlen == len && esccmp(file, replace, len) != 0) || + (reqlen > len && (file[len] != '/' || + esccmp(file, replace, len) != 0))) { + + /* non-match, skip to end of line and continue */ + while (*p != '\r' && *p != '\n' && avail) { + p++; + avail--; + } + replace = NULL; + map_to = NULL; + continue; + } + p++; + avail--; + + /* found a match, parse the target */ + map_to = p; + while (*p != '\r' && *p != '\n' && avail) { + p++; + avail--; + } + rlen = p - map_to; + break; + } + + if (replace && map_to) { + newfile = bozomalloc(httpd, strlen(file) + rlen - len + 1); + memcpy(newfile, map_to, rlen); + strcpy(newfile+rlen, file + len); + debug((httpd, DEBUG_NORMAL, "remapping found ``%s'' ", + newfile)); + free(request->hr_file); + request->hr_file = newfile; + } + + munmap(fmap, st.st_size); + close(mapfile); +} + +/* * deal with virtual host names; we do this: * if we have a virtual path root (httpd->virtbase), and we are given a * virtual host spec (Host: ho.st or http://ho.st/), see if this @@ -1191,6 +1349,12 @@ use_slashdir: if (chdir(s) < 0) return bozo_http_error(httpd, 404, request, "can't chdir to slashdir"); + + /* + * is there a mapping for this request? + */ + check_mapping(request); + return 0; } @@ -1707,6 +1871,9 @@ bozo_check_special_files(bozo_httpreq_t if (strcmp(name, ABSREDIRECT_FILE) == 0) return bozo_http_error(httpd, 403, request, "no permission to open redirect file"); + if (strcmp(name, REMAP_FILE) == 0) + return bozo_http_error(httpd, 403, request, + "no permission to open redirect file"); return bozo_auth_check_special_files(request, name); } Index: src/libexec/httpd/testsuite/Makefile diff -u src/libexec/httpd/testsuite/Makefile:1.7 src/libexec/httpd/testsuite/Makefile:1.8 --- src/libexec/httpd/testsuite/Makefile:1.7 Tue Jan 31 14:33:54 2017 +++ src/libexec/httpd/testsuite/Makefile Fri Aug 24 11:41:16 2018 @@ -1,6 +1,6 @@ # $eterna: Makefile,v 1.14 2009/05/22 21:51:39 mrg Exp $ -SIMPLETESTS= t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 +SIMPLETESTS= t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t12 t13 CGITESTS= t11 BIGFILETESTS= partial4000 partial8000 Added files: Index: src/libexec/httpd/testsuite/t12.in diff -u /dev/null src/libexec/httpd/testsuite/t12.in:1.1 --- /dev/null Fri Aug 24 11:41:16 2018 +++ src/libexec/httpd/testsuite/t12.in Fri Aug 24 11:41:16 2018 @@ -0,0 +1,2 @@ +get /nic/update HTTP/1.1 +Host: Index: src/libexec/httpd/testsuite/t12.out diff -u /dev/null src/libexec/httpd/testsuite/t12.out:1.1 --- /dev/null Fri Aug 24 11:41:16 2018 +++ src/libexec/httpd/testsuite/t12.out Fri Aug 24 11:41:16 2018 @@ -0,0 +1,11 @@ +HTTP/1.1 404 Not Found +Content-Type: text/html +Content-Length: 197 +Server: bozohttpd/20170201 +Allow: GET, HEAD, POST + +<html><head><title>404 Not Found</title></head> +<body><h1>404 Not Found</h1> +auth-dir/updipv4.pl: <pre>This item has not been found</pre> +<hr><address><a href="// /"> </a></address> +</body></html> Index: src/libexec/httpd/testsuite/t13.in diff -u /dev/null src/libexec/httpd/testsuite/t13.in:1.1 --- /dev/null Fri Aug 24 11:41:16 2018 +++ src/libexec/httpd/testsuite/t13.in Fri Aug 24 11:41:16 2018 @@ -0,0 +1,2 @@ +get /update:all HTTP/1.1 +Host: Index: src/libexec/httpd/testsuite/t13.out diff -u /dev/null src/libexec/httpd/testsuite/t13.out:1.1 --- /dev/null Fri Aug 24 11:41:16 2018 +++ src/libexec/httpd/testsuite/t13.out Fri Aug 24 11:41:16 2018 @@ -0,0 +1,11 @@ +HTTP/1.1 404 Not Found +Content-Type: text/html +Content-Length: 196 +Server: bozohttpd/20170201 +Allow: GET, HEAD, POST + +<html><head><title>404 Not Found</title></head> +<body><h1>404 Not Found</h1> +auth-dir/updall.pl: <pre>This item has not been found</pre> +<hr><address><a href="// /"> </a></address> +</body></html> Index: src/libexec/httpd/testsuite/data/.bzremap diff -u /dev/null src/libexec/httpd/testsuite/data/.bzremap:1.1 --- /dev/null Fri Aug 24 11:41:16 2018 +++ src/libexec/httpd/testsuite/data/.bzremap Fri Aug 24 11:41:16 2018 @@ -0,0 +1,2 @@ +/nic/update:/auth-dir/updipv4.pl +/update\:all:/auth-dir/updall.pl