summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2014-07-29 16:17:29 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2014-07-29 16:17:29 +0000
commit36d5193fe140b35a17941240b01dbfe340639b6f (patch)
tree6cefaf21a239f1d4ad8d15aade53668dce708523
parentb5a9d2c4c1157bdcc78399bff18d5c37b4ffd23e (diff)
Add extended directory index options: "[no] index" and "[no] auto index".
The option "directory auto index" implements basic directory listing and is turned off by default. ok deraadt@
-rw-r--r--etc/examples/httpd.conf3
-rw-r--r--usr.sbin/httpd/httpd.conf.517
-rw-r--r--usr.sbin/httpd/httpd.h14
-rw-r--r--usr.sbin/httpd/parse.y50
-rw-r--r--usr.sbin/httpd/server_file.c178
5 files changed, 245 insertions, 17 deletions
diff --git a/etc/examples/httpd.conf b/etc/examples/httpd.conf
index ab76a53fe1c..826f0260f92 100644
--- a/etc/examples/httpd.conf
+++ b/etc/examples/httpd.conf
@@ -1,4 +1,4 @@
-# $OpenBSD: httpd.conf,v 1.2 2014/07/26 10:27:19 reyk Exp $
+# $OpenBSD: httpd.conf,v 1.3 2014/07/29 16:17:28 reyk Exp $
# Macros
ext_addr="egress"
@@ -20,6 +20,7 @@ server "www.example.com" {
# Another server on a different internal IPv4 address
server "intranet.example.com" {
listen on 10.0.0.1 port 80
+ directory { auto index, index "default.htm" }
root "/htdocs/internet.example.com"
}
diff --git a/usr.sbin/httpd/httpd.conf.5 b/usr.sbin/httpd/httpd.conf.5
index 2cb442f2c86..7877f8769ca 100644
--- a/usr.sbin/httpd/httpd.conf.5
+++ b/usr.sbin/httpd/httpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: httpd.conf.5,v 1.7 2014/07/25 17:49:11 reyk Exp $
+.\" $OpenBSD: httpd.conf.5,v 1.8 2014/07/29 16:17:28 reyk Exp $
.\"
.\" Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: July 25 2014 $
+.Dd $Mdocdate: July 29 2014 $
.Dt HTTPD.CONF 5
.Os
.Sh NAME
@@ -111,8 +111,19 @@ runs 3 server processes by default.
.Sh SERVERS
The configured web servers.
.Pp
-The following general table options are available:
+The following general server options are available:
.Bl -tag -width Ds
+.It Ic directory Oo Ic no Oc Ic auto index
+If no index file is found, automatically generate a directory listing.
+This is disabled by default.
+.It Ic directory Ic index Ar string
+Set the directory index file.
+If not specified, it defaults to
+.Pa index.html .
+.It Ic directory no index
+Disable the directory index.
+.Nm httpd
+will neither display nor generate a directory index.
.It Ic listen on Ar address Ic port Ar number
Set the listen address and port.
.It Ic root Ar directory
diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h
index 7e195336f58..081e6387231 100644
--- a/usr.sbin/httpd/httpd.h
+++ b/usr.sbin/httpd/httpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: httpd.h,v 1.16 2014/07/29 12:16:36 reyk Exp $ */
+/* $OpenBSD: httpd.h,v 1.17 2014/07/29 16:17:28 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -272,6 +272,14 @@ struct client {
};
SPLAY_HEAD(client_tree, client);
+#define SRVFLAG_INDEX 0x01
+#define SRVFLAG_NO_INDEX 0x02
+#define SRVFLAG_AUTO_INDEX 0x04
+#define SRVFLAG_NO_AUTO_INDEX 0x08
+
+#define SRVFLAG_BITS \
+ "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX"
+
#define TCPFLAG_NODELAY 0x01
#define TCPFLAG_NNODELAY 0x02
#define TCPFLAG_SACK 0x04
@@ -288,14 +296,16 @@ SPLAY_HEAD(client_tree, client);
struct server_config {
u_int32_t id;
- u_int32_t flags;
char name[MAXHOSTNAMELEN];
char docroot[MAXPATHLEN];
+ char index[NAME_MAX];
+
in_port_t port;
struct sockaddr_storage ss;
int prefixlen;
struct timeval timeout;
+ u_int8_t flags;
u_int8_t tcpflags;
int tcpbufsiz;
int tcpbacklog;
diff --git a/usr.sbin/httpd/parse.y b/usr.sbin/httpd/parse.y
index 1105e55c2a2..97485e31a12 100644
--- a/usr.sbin/httpd/parse.y
+++ b/usr.sbin/httpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.7 2014/07/25 17:04:47 reyk Exp $ */
+/* $OpenBSD: parse.y,v 1.8 2014/07/29 16:17:28 reyk Exp $ */
/*
* Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -126,8 +126,9 @@ typedef struct {
%}
-%token ALL PORT LISTEN PREFORK ROOT SERVER ERROR LOG VERBOSE ON TYPES
-%token UPDATES INCLUDE
+%token ALL AUTO DIRECTORY INDEX LISTEN LOG NO ON PORT PREFORK ROOT SERVER
+%token TYPES UPDATES VERBOSE
+%token ERROR INCLUDE
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.number> loglevel
@@ -217,6 +218,8 @@ server : SERVER STRING {
strlcpy(s->srv_conf.docroot, HTTPD_DOCROOT,
sizeof(s->srv_conf.docroot));
+ strlcpy(s->srv_conf.index, HTTPD_INDEX,
+ sizeof(s->srv_conf.index));
s->srv_conf.id = ++last_server_id;
s->srv_conf.timeout.tv_sec = SERVER_TIMEOUT;
@@ -277,6 +280,38 @@ serveroptsl : LISTEN ON STRING port {
}
free($2);
}
+ | DIRECTORY dirflags
+ | DIRECTORY '{' dirflags_l '}'
+ ;
+
+dirflags_l : dirflags comma dirflags_l
+ | dirflags
+ ;
+
+dirflags : INDEX STRING {
+ if (strlcpy(srv->srv_conf.index, $2,
+ sizeof(srv->srv_conf.index)) >=
+ sizeof(srv->srv_conf.index)) {
+ yyerror("index file too long");
+ free($2);
+ YYERROR;
+ }
+ srv->srv_conf.flags &= ~SRVFLAG_NO_INDEX;
+ srv->srv_conf.flags |= SRVFLAG_INDEX;
+ free($2);
+ }
+ | NO INDEX {
+ srv->srv_conf.flags &= ~SRVFLAG_INDEX;
+ srv->srv_conf.flags |= SRVFLAG_NO_INDEX;
+ }
+ | AUTO INDEX {
+ srv->srv_conf.flags &= ~SRVFLAG_NO_AUTO_INDEX;
+ srv->srv_conf.flags |= SRVFLAG_AUTO_INDEX;
+ }
+ | NO AUTO INDEX {
+ srv->srv_conf.flags &= ~SRVFLAG_AUTO_INDEX;
+ srv->srv_conf.flags |= SRVFLAG_NO_AUTO_INDEX;
+ }
;
types : TYPES '{' optnl mediaopts_l '}'
@@ -364,6 +399,11 @@ loglevel : UPDATES { $$ = HTTPD_OPT_LOGUPDATE; }
| ALL { $$ = HTTPD_OPT_LOGALL; }
;
+comma : ','
+ | nl
+ | /* empty */
+ ;
+
optnl : '\n' optnl
|
;
@@ -406,9 +446,13 @@ lookup(char *s)
/* this has to be sorted always */
static const struct keywords keywords[] = {
{ "all", ALL },
+ { "auto", AUTO },
+ { "directory", DIRECTORY },
{ "include", INCLUDE },
+ { "index", INDEX },
{ "listen", LISTEN },
{ "log", LOG },
+ { "no", NO },
{ "on", ON },
{ "port", PORT },
{ "prefork", PREFORK },
diff --git a/usr.sbin/httpd/server_file.c b/usr.sbin/httpd/server_file.c
index e19adb08da4..3d364ed7ed2 100644
--- a/usr.sbin/httpd/server_file.c
+++ b/usr.sbin/httpd/server_file.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server_file.c,v 1.17 2014/07/26 22:38:38 reyk Exp $ */
+/* $OpenBSD: server_file.c,v 1.18 2014/07/29 16:17:28 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -38,6 +38,8 @@
#include <string.h>
#include <unistd.h>
#include <stdio.h>
+#include <dirent.h>
+#include <time.h>
#include <err.h>
#include <event.h>
@@ -46,15 +48,20 @@
#include "httpd.h"
#include "http.h"
-int server_file_access(struct http_descriptor *, char *, size_t,
+int server_file_access(struct client *, char *, size_t,
struct stat *);
+int server_file_index(struct httpd *, struct client *);
void server_file_error(struct bufferevent *, short, void *);
int
-server_file_access(struct http_descriptor *desc, char *path, size_t len,
+server_file_access(struct client *clt, char *path, size_t len,
struct stat *st)
{
- char *newpath;
+ struct http_descriptor *desc = clt->clt_desc;
+ struct server_config *srv_conf = clt->clt_srv_conf;
+ struct stat stb;
+ char *newpath;
+
errno = 0;
if (access(path, R_OK) == -1) {
@@ -62,7 +69,11 @@ server_file_access(struct http_descriptor *desc, char *path, size_t len,
} else if (stat(path, st) == -1) {
goto fail;
} else if (S_ISDIR(st->st_mode)) {
- /* XXX Should we support directory listing? */
+ /* Deny access if directory indexing is disabled */
+ if (srv_conf->flags & SRVFLAG_NO_INDEX) {
+ errno = EACCES;
+ goto fail;
+ }
if (!len) {
/* Recursion - the index "file" is a directory? */
@@ -83,13 +94,27 @@ server_file_access(struct http_descriptor *desc, char *path, size_t len,
}
/* Otherwise append the default index file */
- if (strlcat(path, HTTPD_INDEX, len) >= len) {
+ if (strlcat(path, srv_conf->index, len) >= len) {
errno = EACCES;
goto fail;
}
/* Check again but set len to 0 to avoid recursion */
- return (server_file_access(desc, path, 0, st));
+ if (server_file_access(clt, path, 0, &stb) == 404) {
+ /*
+ * Index file not found; fail if auto-indexing is
+ * not enabled, otherwise return success but
+ * indicate directory with S_ISDIR of the previous
+ * stat.
+ */
+ if ((srv_conf->flags & SRVFLAG_AUTO_INDEX) == 0) {
+ errno = EACCES;
+ goto fail;
+ }
+ } else {
+ /* return updated stat from index file */
+ memcpy(st, &stb, sizeof(*st));
+ }
} else if (!S_ISREG(st->st_mode)) {
/* Don't follow symlinks and ignore special files */
errno = EACCES;
@@ -131,11 +156,16 @@ server_file(struct httpd *env, struct client *clt)
}
/* Returns HTTP status code on error */
- if ((ret = server_file_access(desc, path, sizeof(path), &st)) != 0) {
+ if ((ret = server_file_access(clt, path, sizeof(path), &st)) != 0) {
server_abort_http(clt, ret, desc->http_path);
return (-1);
}
+ if (S_ISDIR(st.st_mode)) {
+ /* List directory index */
+ return (server_file_index(env, clt));
+ }
+
/* Now open the file, should be readable or we have another problem */
if ((fd = open(path, O_RDONLY)) == -1)
goto fail;
@@ -180,6 +210,138 @@ server_file(struct httpd *env, struct client *clt)
return (-1);
}
+int
+server_file_index(struct httpd *env, struct client *clt)
+{
+ char path[MAXPATHLEN];
+ char tmstr[21];
+ struct http_descriptor *desc = clt->clt_desc;
+ struct server_config *srv_conf = clt->clt_srv_conf;
+ struct dirent **namelist, *dp;
+ int namesize, i, ret, fd = -1, namewidth, skip;
+ struct evbuffer *evb = NULL;
+ struct media_type *media;
+ const char *style;
+ struct stat st;
+ struct tm tm;
+ time_t t;
+
+ /* Request path is already canonicalized */
+ if ((size_t)snprintf(path, sizeof(path), "%s%s",
+ srv_conf->docroot, desc->http_path) >= sizeof(path))
+ goto fail;
+
+ /* Now open the file, should be readable or we have another problem */
+ if ((fd = open(path, O_RDONLY)) == -1)
+ goto fail;
+
+ /* File descriptor is opened, decrement inflight counter */
+ server_inflight_dec(clt, __func__);
+
+ if ((evb = evbuffer_new()) == NULL)
+ goto fail;
+
+ if ((namesize = scandir(path, &namelist, NULL, alphasort)) == -1)
+ goto fail;
+
+ /* Indicate failure but continue going through the list */
+ skip = 0;
+
+ /* A CSS stylesheet allows minimal customization by the user */
+ style = "body { background-color: white; color: black; font-family: "
+ "sans-serif; }";
+ /* Generate simple HTML index document */
+ if (evbuffer_add_printf(evb,
+ "<!DOCTYPE HTML PUBLIC "
+ "\"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
+ "<html>\n"
+ "<head>\n"
+ "<title>Index of %s</title>\n"
+ "<style type=\"text/css\"><!--\n%s\n--></style>\n"
+ "</head>\n"
+ "<body>\n"
+ "<h1>Index of %s</h1>\n"
+ "<hr>\n<pre>\n",
+ desc->http_path, style, desc->http_path) == -1)
+ skip = 1;
+
+ for (i = 0; i < namesize; i++) {
+ dp = namelist[i];
+
+ if (skip ||
+ fstatat(fd, dp->d_name, &st, 0) == -1) {
+ free(dp);
+ continue;
+ }
+
+ t = st.st_mtime;
+ localtime_r(&t, &tm);
+ strftime(tmstr, sizeof(tmstr), "%d-%h-%Y %R", &tm);
+ namewidth = 51 - strlen(dp->d_name);
+
+ if (dp->d_name[0] == '.' &&
+ !(dp->d_name[1] == '.' && dp->d_name[2] == '\0')) {
+ /* ignore hidden files starting with a dot */
+ } else if (dp->d_type == DT_DIR) {
+ namewidth -= 1; /* trailing slash */
+ if (evbuffer_add_printf(evb,
+ "<a href=\"%s\">%s/</a>%*s%s%20s\n",
+ dp->d_name, dp->d_name,
+ MAX(namewidth, 0), " ", tmstr, "-") == -1)
+ skip = 1;
+ } else if (dp->d_type == DT_REG) {
+ if (evbuffer_add_printf(evb,
+ "<a href=\"%s\">%s</a>%*s%s%20llu\n",
+ dp->d_name, dp->d_name,
+ MAX(namewidth, 0), " ", tmstr, st.st_size) == -1)
+ skip = 1;
+ }
+ free(dp);
+ }
+ free(namelist);
+
+ if (skip ||
+ evbuffer_add_printf(evb,
+ "</pre>\n<hr>\n</body>\n</html>\n") == -1)
+ goto fail;
+
+ close(fd);
+
+ media = media_find(env->sc_mediatypes, "index.html");
+ ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb));
+ switch (ret) {
+ case -1:
+ goto fail;
+ case 0:
+ /* Connection is already finished */
+ evbuffer_free(evb);
+ return (0);
+ default:
+ break;
+ }
+
+ if (server_bufferevent_write_buffer(clt, evb) == -1)
+ goto fail;
+ evbuffer_free(evb);
+
+ bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
+ if (clt->clt_persist)
+ clt->clt_toread = TOREAD_HTTP_HEADER;
+ else
+ clt->clt_toread = TOREAD_HTTP_NONE;
+ clt->clt_done = 0;
+
+ return (0);
+
+ fail:
+ if (fd != -1)
+ close(fd);
+ if (evb != NULL)
+ evbuffer_free(evb);
+ server_abort_http(clt, 500, desc->http_path);
+ return (-1);
+}
+
void
server_file_error(struct bufferevent *bev, short error, void *arg)
{