#include %s
\n", title);
hprint(hout, "%s\n", buf);
hprint(hout, "\n");
hprint(hout, "\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;
char *bbuf;
int n;
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';
n = strlen(p);
if (n == 0)
continue;
/* root the regular expresssion */
bbuf = malloc(n+2);
if(bbuf == nil)
sysfatal("out of memory");
bbuf[0] = '^';
strcpy(bbuf+1, p);
re = regcomp(bbuf);
free(bbuf);
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)
return(0);
if (aio != nil)
Bseek(aio, 0, 0);
if (dio != nil)
Bseek(dio, 0, 0);
/* if no deny list, assume everything is denied */
okay = (dio != nil);
/* go through denials till we find a match */
while (okay && (re = getre(dio)) != nil) {
memset(&match, 0, sizeof(match));
okay = (regexec(re, dir, &match, 1) != 1);
free(re);
}
/* go through accepts till we have a match */
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
Cannot read directory %s: %r
", dir); return; } if (vermaj) { hokheaders(connect); hprint(hout, "Content-type: text/html\r\n"); hprint(hout, "\r\n"); } doctype(); hprint(hout, "\n"); hprint(hout, "\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 %s\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 (strcmp(f, dir) != 0 && allowed(f)) hprint(hout, "\nGo to parent directory\n", f); else hprint(hout, "\nEnd of directory listing\n"); free(f); hprint(hout, "\n\n\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", "
Illegal formatted directory listing request:
\n" "%H
", 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(webroot, "/", MREPL); if(connect->req.search != nil) dosearch(connect->req.search); else error("Bad argument", "Need a search argument
"); hflush(hout); writelog(connect, "200 webls %ld %ld\n", hout->seek, hout->seek); exits(nil); }