diff options
| author | martin <martin@NetBSD.org> | 2018-08-24 11:41:16 +0000 |
|---|---|---|
| committer | martin <martin@NetBSD.org> | 2018-08-24 11:41:16 +0000 |
| commit | 03a1be6b7cc7227bf692c64758c63165ed933637 (patch) | |
| tree | 53ca08bcfd2c197eae793208e9d6e3eab8f9036f /libexec/httpd | |
| parent | 8b1fbbc2572dd225a74c18fdbcb9622422c16c30 (diff) | |
Add support for remapping requested paths via a .bzredirect file.
Fixes PR 52772. Ok: mrg@
Diffstat (limited to 'libexec/httpd')
| -rw-r--r-- | libexec/httpd/bozohttpd.8 | 35 | ||||
| -rw-r--r-- | libexec/httpd/bozohttpd.c | 171 | ||||
| -rw-r--r-- | libexec/httpd/testsuite/Makefile | 2 | ||||
| -rw-r--r-- | libexec/httpd/testsuite/data/.bzremap | 2 | ||||
| -rw-r--r-- | libexec/httpd/testsuite/t12.in | 2 | ||||
| -rw-r--r-- | libexec/httpd/testsuite/t12.out | 11 | ||||
| -rw-r--r-- | libexec/httpd/testsuite/t13.in | 2 | ||||
| -rw-r--r-- | libexec/httpd/testsuite/t13.out | 11 |
8 files changed, 231 insertions, 5 deletions
diff --git a/libexec/httpd/bozohttpd.8 b/libexec/httpd/bozohttpd.8 index 87ddd912108..ca5c632dcab 100644 --- a/libexec/httpd/bozohttpd.8 +++ b/libexec/httpd/bozohttpd.8 @@ -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 security, with minimal features and regular code audits. This manual documents .Nm -version 20170201. +version 20180824. .Sh AUTHORS .An -nosplit .Nm diff --git a/libexec/httpd/bozohttpd.c b/libexec/httpd/bozohttpd.c index 9fa04e1fbad..344b4247f6a 100644 --- a/libexec/httpd/bozohttpd.c +++ b/libexec/httpd/bozohttpd.c @@ -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 *request, const char *name) 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); } diff --git a/libexec/httpd/testsuite/Makefile b/libexec/httpd/testsuite/Makefile index 52d44e6b11f..fe00cc243ba 100644 --- a/libexec/httpd/testsuite/Makefile +++ b/libexec/httpd/testsuite/Makefile @@ -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 diff --git a/libexec/httpd/testsuite/data/.bzremap b/libexec/httpd/testsuite/data/.bzremap new file mode 100644 index 00000000000..7557bb2652c --- /dev/null +++ b/libexec/httpd/testsuite/data/.bzremap @@ -0,0 +1,2 @@ +/nic/update:/auth-dir/updipv4.pl +/update\:all:/auth-dir/updall.pl diff --git a/libexec/httpd/testsuite/t12.in b/libexec/httpd/testsuite/t12.in new file mode 100644 index 00000000000..c3da726b5f5 --- /dev/null +++ b/libexec/httpd/testsuite/t12.in @@ -0,0 +1,2 @@ +get /nic/update HTTP/1.1 +Host: diff --git a/libexec/httpd/testsuite/t12.out b/libexec/httpd/testsuite/t12.out new file mode 100644 index 00000000000..818cd560044 --- /dev/null +++ b/libexec/httpd/testsuite/t12.out @@ -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> diff --git a/libexec/httpd/testsuite/t13.in b/libexec/httpd/testsuite/t13.in new file mode 100644 index 00000000000..1d2e85a62c2 --- /dev/null +++ b/libexec/httpd/testsuite/t13.in @@ -0,0 +1,2 @@ +get /update:all HTTP/1.1 +Host: diff --git a/libexec/httpd/testsuite/t13.out b/libexec/httpd/testsuite/t13.out new file mode 100644 index 00000000000..8616fd5964d --- /dev/null +++ b/libexec/httpd/testsuite/t13.out @@ -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> |
