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

Reply via email to