Browse Source

Plan 9 from Bell Labs 2003-09-30

David du Colombier 20 years ago
parent
commit
97062d3957

+ 8 - 5
dist/replica/plan9.db

@@ -273,6 +273,7 @@
 386/bin/ip/httpd/imagemap - 775 sys sys 1064598228 113413
 386/bin/ip/httpd/man2html - 775 sys sys 1064598229 121681
 386/bin/ip/httpd/save - 775 sys sys 1064598231 130742
+386/bin/ip/httpd/webls - 775 sys sys 1064887865 129644
 386/bin/ip/httpd/wikipost - 775 sys sys 1064598232 111102
 386/bin/ip/imap4d - 775 sys sys 1064598233 234268
 386/bin/ip/ipconfig - 775 sys sys 1064598234 128285
@@ -511,7 +512,7 @@
 386/lib/libauth.a - 664 sys sys 1064598464 56258
 386/lib/libauthsrv.a - 664 sys sys 1056364449 33962
 386/lib/libbin.a - 664 sys sys 1045538115 2556
-386/lib/libbio.a - 664 sys sys 1045538115 28390
+386/lib/libbio.a - 664 sys sys 1064846011 28408
 386/lib/libc.a - 664 sys sys 1064598466 499658
 386/lib/libcontrol.a - 664 sys sys 1045538117 243448
 386/lib/libdisk.a - 664 sys sys 1048622925 43692
@@ -4953,7 +4954,7 @@ sys/man/8/drawterm - 664 sys sys 958419689 2458
 sys/man/8/fossilcons - 664 sys sys 1063855784 14700
 sys/man/8/fs - 664 sys sys 1055701170 15029
 sys/man/8/fsconfig - 664 sys sys 1045501600 8142
-sys/man/8/httpd - 664 sys sys 1037690024 4516
+sys/man/8/httpd - 664 sys sys 1064887873 6230
 sys/man/8/init - 664 sys sys 944959679 1430
 sys/man/8/ipconfig - 664 sys sys 1060189415 5050
 sys/man/8/ipserv - 664 sys sys 1063855796 4337
@@ -4982,7 +4983,7 @@ sys/man/8/replica - 664 sys sys 1021579979 6239
 sys/man/8/rsa - 664 sys sys 1057955511 4628
 sys/man/8/scanmail - 664 sys sys 969499895 10803
 sys/man/8/scuzz - 664 sys sys 984709640 7916
-sys/man/8/secstore - 664 sys sys 1064789017 1294
+sys/man/8/secstore - 664 sys sys 1064807283 1306
 sys/man/8/securenet - 664 sys sys 954305552 3160
 sys/man/8/send - 664 sys sys 1045501634 2168
 sys/man/8/smtp - 664 sys sys 1049408388 4111
@@ -9185,12 +9186,14 @@ sys/src/cmd/ip/httpd/imagemap.c - 664 sys sys 984773808 5229
 sys/src/cmd/ip/httpd/init.c - 664 sys sys 1015090171 2182
 sys/src/cmd/ip/httpd/log.c - 664 sys sys 1015090171 1391
 sys/src/cmd/ip/httpd/man2html.c - 664 sys sys 1015090172 8789
-sys/src/cmd/ip/httpd/mkfile - 664 sys sys 1038443005 1384
+sys/src/cmd/ip/httpd/mkfile - 664 sys sys 1064887854 1615
 sys/src/cmd/ip/httpd/netlib_find.c - 664 sys sys 1015090172 6247
 sys/src/cmd/ip/httpd/netlib_history.c - 664 sys sys 1015096252 4744
 sys/src/cmd/ip/httpd/redirect.c - 664 sys sys 1042522766 2978
 sys/src/cmd/ip/httpd/save.c - 664 sys sys 1015090172 3175
 sys/src/cmd/ip/httpd/sendfd.c - 664 sys sys 1017679317 12134
+sys/src/cmd/ip/httpd/webls.c - 664 sys sys 1064887840 6940
+sys/src/cmd/ip/httpd/webls.denied - 664 sys sys 1064887847 3
 sys/src/cmd/ip/httpd/wikipost.c - 664 sys sys 1019678647 5917
 sys/src/cmd/ip/imap4d - 20000000775 sys sys 988249981 0
 sys/src/cmd/ip/imap4d/auth.c - 664 sys sys 1015013075 3510
@@ -11054,7 +11057,7 @@ sys/src/libbio - 20000000775 sys sys 1014927637 0
 sys/src/libbio/bbuffered.c - 664 sys sys 950315725 309
 sys/src/libbio/bfildes.c - 664 sys sys 944961707 100
 sys/src/libbio/bflush.c - 664 sys sys 964455730 479
-sys/src/libbio/bgetc.c - 664 sys sys 944961707 924
+sys/src/libbio/bgetc.c - 664 sys sys 1064846001 948
 sys/src/libbio/bgetd.c - 664 sys sys 944961707 401
 sys/src/libbio/bgetrune.c - 664 sys sys 944961707 634
 sys/src/libbio/binit.c - 664 sys sys 944961707 1859

+ 8 - 0
dist/replica/plan9.log

@@ -13828,3 +13828,11 @@
 1064790103 1 c sys/src/cmd/auth/secstore/SConn.c - 664 sys sys 1064789003 4419
 1064790103 2 c sys/src/cmd/auth/secstore/secstored.c - 664 sys sys 1064789004 7955
 1064799112 0 c 386/bin/auth/secstored - 775 sys sys 1064798342 194075
+1064808005 0 c sys/man/8/secstore - 664 sys sys 1064807283 1306
+1064847673 0 c 386/lib/libbio.a - 664 sys sys 1064846011 28408
+1064847673 1 c sys/src/libbio/bgetc.c - 664 sys sys 1064846001 948
+1064889121 0 a 386/bin/ip/httpd/webls - 775 sys sys 1064887865 129644
+1064889121 1 c sys/man/8/httpd - 664 sys sys 1064887873 6230
+1064889121 2 c sys/src/cmd/ip/httpd/mkfile - 664 sys sys 1064887854 1615
+1064889121 3 a sys/src/cmd/ip/httpd/webls.c - 664 sys sys 1064887840 6940
+1064889121 4 a sys/src/cmd/ip/httpd/webls.denied - 664 sys sys 1064887847 3

+ 79 - 1
sys/man/8/httpd

@@ -1,6 +1,6 @@
 .TH HTTPD 8
 .SH NAME
-httpd, mirror, save, imagemap, man2html \- HTTP server
+httpd, mirror, save, imagemap, man2html, webls \- HTTP server
 .SH SYNOPSIS
 .PP
 .B ip/httpd/httpd
@@ -69,6 +69,20 @@ httpd, mirror, save, imagemap, man2html \- HTTP server
 .IR netdir ]
 .I method version uri
 .RI [ search ]
+.PP
+.B ip/httpd/webls
+.RB [ -b
+.IR inbuf ]
+.RB [ -d
+.IR domain ]
+.RB [ -r
+.IR remoteip ]
+.RB [ -w
+.IR webroot ]
+.RB [ -N
+.IR netdir ]
+.I method version uri
+.RI [ search ]
 .SH DESCRIPTION
 .I Httpd
 serves the
@@ -184,6 +198,64 @@ converts
 .IR man (6)
 format manual pages into html.
 It includes some abilities to search the manuals.
+.PP
+.I Webls
+produces directory listings on the fly, with
+output in the style of
+.IR ls (1).
+If
+.B /sys/lib/webls.allowed
+and/or
+.B /sys/lib/webls.denied
+exist, they contain regular expressions describing
+what parts of
+.I httpd's
+namespace may and may not be listed, respectively.
+Security conscious sites will always want
+.B webls.denied
+to contain `.*', limiting access to only those
+directories described in
+.BR webls.allowed .
+This is the default configuration.
+.PP
+Other sites will note that if neither
+.B webls.denied
+nor
+.B webls.allowed
+exist, any portion of
+.I httpd's
+namespace can be listed (however,
+.I webls
+will always endeavor to prevent listing of `.' and `..').
+If
+.B webls.allowed
+exists but
+.B webls.denied
+does not, any directory to be listed must be described
+by a regular expression in
+.BR webls.allowed .
+Similarly, if
+.B webls.denied
+exists but
+.B webls.allowed
+does not, any directory to be listed must
+.I not
+be described by a regular expression in
+.BR webls.denied .
+If both exist, a directory is listable if either
+it doesn't appear in
+.BR webls.denied ,
+or it appears in both
+.B webls.denied
+and
+.BR webls.allowed .
+In other words,
+.B webls.allowed
+overrides
+.BR webls.denied .
+If a listing for a directory is requested and access
+is denied, or another error occurs, a simple error
+page is returned.
 .SH FILES
 .TF /lib/namespace.httpd
 .TP
@@ -195,6 +267,12 @@ default namespace file for httpd
 .TP
 .B /sys/lib/httpd.rewrite
 redirection file
+.TP
+.B /sys/lib/webls.allowed
+regular expressions describing explicitly listable pathnames; overrides webls.denied
+.TP
+.B /sys/lib/webls.denied
+regular expressions describing explicitly unlistable pathnames
 .SH SOURCE
 .B /sys/src/cmd/ip/httpd
 .SH "SEE ALSO"

+ 22 - 2
sys/src/cmd/ip/httpd/mkfile

@@ -9,6 +9,24 @@ TARG=\
 	imagemap\
 	man2html\
 	save\
+	netlib_find\
+	netlib_history\
+	P\
+	post_find\
+	to\
+	test9down\
+	spin_register\
+	webls\
+	wikipost\
+	9down4e\
+
+XTARG=\
+	httpd\
+	imagemap\
+	netlib_find\
+	netlib_history\
+	man2html\
+	save\
 	wikipost\
 
 LIB=libhttps.a.$O
@@ -30,8 +48,7 @@ BIN=/$objtype/bin/ip/httpd
 UPDATE=\
 	$HFILES\
 	${LIBSOFILES:%.$O=%.c}\
-	${TARG:%=%.c}\
-	${TARG:%=/386/bin/ip/httpd/%}\
+	${XTARG:%=%.c}\
 
 </sys/src/cmd/mkmany
 
@@ -63,6 +80,9 @@ $O.9down: 9down.$O whois.$O classify.$O $LIB
 $O.9down4e: 9down4e.$O whois.$O classify.$O $LIB
 	$LD -o $target $prereq
 
+$O.9down4e2: 9down4e2.$O whois.$O classify.$O $LIB
+	$LD -o $target $prereq
+
 $O.test9down: 9down4e.$O whois.$O classify.$O $LIB
 	$LD -o $target $prereq
 

+ 305 - 0
sys/src/cmd/ip/httpd/webls.c

@@ -0,0 +1,305 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <bio.h>
+#include <regexp.h>
+#include <fcall.h>
+#include "httpd.h"
+#include "httpsrv.h"
+
+static	Hio		*hout;
+static	Hio		houtb;
+static	HConnect	*connect;
+static	int		vermaj, gidwidth, uidwidth, lenwidth, devwidth;
+static	Biobuf		*aio, *dio;
+
+static void
+doctype(void)
+{
+	hprint(hout, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n");
+	hprint(hout, "    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n");
+}
+
+void
+error(char *title, char *fmt, ...)
+{
+	va_list arg;
+	char buf[1024], *out;
+
+	va_start(arg, fmt);
+	out = vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	*out = 0;
+
+	hprint(hout, "%s 404 %s\r\n", hversion, title);
+	hprint(hout, "Date: %D\r\n", time(nil));
+	hprint(hout, "Server: Plan9\r\n");
+	hprint(hout, "Content-type: text/html\r\n");
+	hprint(hout, "\r\n");
+	doctype();
+	hprint(hout, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
+	hprint(hout, "<head><title>%s</title></head>\n", title);
+	hprint(hout, "<body>\n");
+	hprint(hout, "<h1>%s</h1>\n", title);
+	hprint(hout, "%s\n", buf);
+	hprint(hout, "</body>\n");
+	hprint(hout, "</html>\n");
+	hflush(hout);
+	writelog(connect, "Reply: 404\nReason: %s\n", title);
+	exits(nil);
+}
+
+/*
+ * Are we actually allowed to look in here?
+ *
+ * Rules:
+ *	1) If neither allowed nor denied files exist, access is granted.
+ *	2) If allowed exists and denied does not, dir *must* be in allowed
+ *	   for access to be granted, otherwise, access is denied.
+ *	3) If denied exists and allowed does not, dir *must not* be in
+ *	   denied for access to be granted, otherwise, access is enied.
+ *	4) If both exist, okay if either (a) file is not in denied, or
+ *	   (b) in denied and in allowed.  Otherwise, access is denied.
+ */
+static Reprog *
+getre(Biobuf *buf)
+{
+	Reprog	*re;
+	char	*p, *t;
+
+	if (buf == nil)
+		return(nil);
+	for ( ; ; free(p)) {
+		p = Brdstr(buf, '\n', 0);
+		if (p == nil)
+			return(nil);
+		t = strchr(p, '#');
+		if (t != nil)
+			*t = '\0';
+		t = p + strlen(p);
+		while (--t > p && isspace(*t))
+			*t = '\0';
+		if (strlen(p) == 0)
+			continue;
+		re = regcomp(p);
+		if (re == nil)
+			continue;
+		free(p);
+		return(re);
+	}
+}
+
+static int
+allowed(char *dir)
+{
+	Reprog	*re;
+	int	okay;
+	Resub	match;
+
+	if (strcmp(dir, "..") == 0 || strncmp(dir, "../", 3) == 0)
+		return(0);
+	if (aio == nil && dio == nil)
+		return(1);
+	if (aio != nil)
+		Bseek(aio, 0, 0);
+	if (dio != nil)
+		Bseek(dio, 0, 0);
+
+	okay = (dio != nil);
+	while (okay && (re = getre(dio)) != nil) {
+		memset(&match, 0, sizeof(match));
+		okay = (regexec(re, dir, &match, 1) != 1);
+		free(re);
+	}
+	if (aio == nil)
+		return(okay);
+	while (!okay && (re = getre(aio)) != nil) {
+		memset(&match, 0, sizeof(match));
+		okay = (regexec(re, dir, &match, 1) == 1);
+		free(re);
+	}
+	return(okay);
+}
+
+/*
+ * Comparison routine for sorting the directory.
+ */
+static int
+compar(Dir *a, Dir *b)
+{
+	return(strcmp(a->name, b->name));
+}
+
+/*
+ * These is for formating; how wide are variable-length
+ * fields?
+ */
+static void
+maxwidths(Dir *dp, long n)
+{
+	long	i;
+	char	scratch[64];
+
+	for (i = 0; i < n; i++) {
+		if (snprint(scratch, sizeof scratch, "%ud", dp[i].dev) > devwidth)
+			devwidth = strlen(scratch);
+		if (strlen(dp[i].uid) > uidwidth)
+			uidwidth = strlen(dp[i].uid);
+		if (strlen(dp[i].gid) > gidwidth)
+			gidwidth = strlen(dp[i].gid);
+		if (snprint(scratch, sizeof scratch, "%lld", dp[i].length) > lenwidth)
+			lenwidth = strlen(scratch);
+	}
+}
+
+/*
+ * Do an actual directory listing.
+ * asciitime is lifted directly out of ls.
+ */
+char *
+asciitime(long l)
+{
+	ulong clk;
+	static char buf[32];
+	char *t;
+
+	clk = time(nil);
+	t = ctime(l);
+	/* 6 months in the past or a day in the future */
+	if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
+		memmove(buf, t+4, 7);		/* month and day */
+		memmove(buf+7, t+23, 5);		/* year */
+	}else
+		memmove(buf, t+4, 12);		/* skip day of week */
+	buf[12] = 0;
+	return buf;
+}
+
+static void
+dols(char *dir)
+{
+	Dir	*d;
+	char	*f, *p;
+	long	i, n;
+	int	fd;
+
+	cleanname(dir);
+	if (!allowed(dir)) {
+		error("Permission denied", "<p>Cannot list directory %s: Access prohibited</p>", dir);
+		return;
+	}
+	fd = open(dir, OREAD);
+	if (fd < 0) {
+		error("Cannot read directory", "<p>Cannot read directory %s: %r</p>", dir);
+		return;
+	}
+	if (vermaj) {
+		hokheaders(connect);
+		hprint(hout, "Content-type: text/html\r\n");
+		hprint(hout, "\r\n");
+	}
+	doctype();
+	hprint(hout, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
+	hprint(hout, "<head><title>Index of %s</title></head>\n", dir);
+	hprint(hout, "<body>\n");
+	hprint(hout, "<h1>Index of %s</h1>\n", dir);
+	n = dirreadall(fd, &d);
+	close(fd);
+	maxwidths(d, n);
+	qsort(d, n, sizeof(Dir), (int (*)(void *, void *))compar);
+	hprint(hout, "<pre>\n");
+	for (i = 0; i < n; i++) {
+		f = smprint("%s/%s", dir, d[i].name);
+		cleanname(f);
+		if (d[i].mode & DMDIR) {
+			p = smprint("/magic/webls?dir=%H", f);
+			free(f);
+			f = p;
+		}
+		hprint(hout, "%M %C %*ud %-*s %-*s %*lld %s <a href=\"%s\">%s</a>\n",
+		    d[i].mode, d[i].type,
+		    devwidth, d[i].dev,
+		    uidwidth, d[i].uid,
+		    gidwidth, d[i].gid,
+		    lenwidth, d[i].length,
+		    asciitime(d[i].mtime), f, d[i].name);
+		free(f);
+	}
+	f = smprint("%s/..", dir);
+	cleanname(f);
+	if (allowed(f))
+		hprint(hout, "\nGo to <a href=\"/magic/webls?dir=%H\">parent</a> directory\n", f);
+	else
+		hprint(hout, "\nEnd of directory listing\n");
+	free(f);
+	hprint(hout, "</pre>\n</body>\n</html>\n");
+	hflush(hout);
+	free(d);
+}
+
+/*
+ * Handle unpacking the request in the URI and
+ * invoking the actual handler.
+ */
+static void
+dosearch(char *search)
+{
+	if (strncmp(search, "dir=", 4) == 0){
+		search = hurlunesc(connect, search+4);
+		dols(search);
+		return;
+	}
+
+	/*
+	 * Otherwise, we've gotten an illegal request.
+	 * spit out a non-apologetic error.
+	 */
+	search = hurlunesc(connect, search);
+	error("Bad directory listing request",
+	    "<p>Illegal formatted directory listing request:</p>\n"
+	    "<p>%H</p>", search);
+}
+
+void
+main(int argc, char **argv)
+{
+	fmtinstall('H', httpfmt);
+	fmtinstall('U', hurlfmt);
+	fmtinstall('M', dirmodefmt);
+
+	aio = Bopen("/sys/lib/webls.allowed", OREAD);
+	dio = Bopen("/sys/lib/webls.denied", OREAD);
+
+	if(argc == 2){
+		hinit(&houtb, 1, Hwrite);
+		hout = &houtb;
+		dols(argv[1]);
+		exits(nil);
+	}
+	close(2);
+
+	connect = init(argc, argv);
+	hout = &connect->hout;
+	vermaj = connect->req.vermaj;
+	if(hparseheaders(connect, HSTIMEOUT) < 0)
+		exits("failed");
+
+	if(strcmp(connect->req.meth, "GET") != 0 && strcmp(connect->req.meth, "HEAD") != 0){
+		hunallowed(connect, "GET, HEAD");
+		exits("not allowed");
+	}
+	if(connect->head.expectother || connect->head.expectcont){
+		hfail(connect, HExpectFail, nil);
+		exits("failed");
+	}
+
+	bind("/usr/web", "/", MREPL);
+
+	if(connect->req.search != nil)
+		dosearch(connect->req.search);
+	else
+		error("Bad argument", "<p>Need a search argument</p>");
+	hflush(hout);
+	writelog(connect, "200 webls %ld %ld\n", hout->seek, hout->seek);
+	exits(nil);
+}

+ 1 - 0
sys/src/cmd/ip/httpd/webls.denied

@@ -0,0 +1 @@
+.*

+ 1 - 0
sys/src/libbio/bgetc.c

@@ -27,6 +27,7 @@ loop:
 	i = read(bp->fid, bp->bbuf, bp->bsize);
 	bp->gbuf = bp->bbuf;
 	if(i <= 0) {
+		bp->state = Bracteof;
 		if(i < 0)
 			bp->state = Binactive;
 		return Beof;