summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2014-07-23 19:03:57 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2014-07-23 19:03:57 +0000
commit8132acf260748049d64ae8e1ab169af4e554676c (patch)
tree9dcc598d76d7bac938780d6e9a7ac1baa33365df /usr.sbin
parenta5b7c143a85150c2b9d571c4dc6f2df1ce3e560d (diff)
Add canonicalize_path() to canonicalize the requested URL path.
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/httpd/httpd.c65
-rw-r--r--usr.sbin/httpd/httpd.h5
-rw-r--r--usr.sbin/httpd/server_file.c20
3 files changed, 81 insertions, 9 deletions
diff --git a/usr.sbin/httpd/httpd.c b/usr.sbin/httpd/httpd.c
index f8f03238476..100ea540cee 100644
--- a/usr.sbin/httpd/httpd.c
+++ b/usr.sbin/httpd/httpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: httpd.c,v 1.2 2014/07/13 14:17:37 reyk Exp $ */
+/* $OpenBSD: httpd.c,v 1.3 2014/07/23 19:03:56 reyk Exp $ */
/*
* Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
@@ -465,6 +465,69 @@ canonicalize_host(const char *host, char *name, size_t len)
return (NULL);
}
+const char *
+canonicalize_path(const char *root, const char *input, char *path, size_t len)
+{
+ const char *i;
+ char *p, *start, *end;
+ size_t n;
+
+ /* assuming input starts with '/' and is nul-terminated */
+ i = input;
+ p = path;
+
+ /* prepend root directory, if specified */
+ if (root != NULL) {
+ if ((n = strlcpy(path, root, len)) >= len)
+ return (NULL);
+ len -= n;
+ p += n;
+ }
+
+ if (*input != '/' || len < 3)
+ return (NULL);
+
+ start = p;
+ end = p + (len - 1);
+
+ /* Set path pointer and make sure that we start with '/' */
+ *p = '\0';
+
+ while (*i != '\0' && p <= end) {
+ /* 1. check for special path elements */
+ if (i[0] == '/') {
+ if (i[1] == '/') {
+ /* a) skip repeating '//' slashes */
+ while (i[1] == '/')
+ i++;
+ continue;
+ } else if (i[1] == '.' && i[2] == '.' &&
+ (i[3] == '/' || i[3] == '\0')) {
+ /* b) revert '..' to previous directory */
+ i += 3;
+ while (p > start && *p != '/')
+ p--;
+ *p = '\0';
+ continue;
+ } else if (i[1] == '.' &&
+ (i[2] == '/' || i[2] == '\0')) {
+ /* c) skip unnecessary '.' current dir */
+ i += 2;
+ continue;
+ }
+ }
+
+ /* 2. copy any other characters */
+ *p++ = *i;
+ i++;
+ }
+ if (p == start)
+ *p++ = '/';
+ *p++ = '\0';
+
+ return (path);
+}
+
void
socket_rlimit(int maxfd)
{
diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h
index d23e6a8d97a..ae275bfcff0 100644
--- a/usr.sbin/httpd/httpd.h
+++ b/usr.sbin/httpd/httpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: httpd.h,v 1.6 2014/07/23 13:26:39 reyk Exp $ */
+/* $OpenBSD: httpd.h,v 1.7 2014/07/23 19:03:56 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -31,6 +31,8 @@
#define HTTPD_SOCKET "/var/run/httpd.sock"
#define HTTPD_USER "www"
#define HTTPD_SERVERNAME "OpenBSD httpd"
+#define HTTPD_DOCROOT "/htdocs"
+#define HTTPD_INDEX "index.html"
#define FD_RESERVE 5
#define SERVER_MAX_CLIENTS 1024
@@ -413,6 +415,7 @@ void event_again(struct event *, int, short,
void (*)(int, short, void *),
struct timeval *, struct timeval *, void *);
const char *canonicalize_host(const char *, char *, size_t);
+const char *canonicalize_path(const char *, const char *, char *, size_t);
void imsg_event_add(struct imsgev *);
int imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t,
pid_t, int, void *, u_int16_t);
diff --git a/usr.sbin/httpd/server_file.c b/usr.sbin/httpd/server_file.c
index 2b511158cfa..e23086fd93d 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.6 2014/07/16 10:25:28 reyk Exp $ */
+/* $OpenBSD: server_file.c,v 1.7 2014/07/23 19:03:56 reyk Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -64,12 +64,18 @@ server_file(struct httpd *env, struct client *clt)
* XXX Don't expect anything from this code yet,
*/
- strlcpy(path, "/htdocs", sizeof(path));
- if (desc->http_path[0] != '/')
- strlcat(path, "/", sizeof(path));
- strlcat(path, desc->http_path, sizeof(path));
- if (desc->http_path[strlen(desc->http_path) - 1] == '/')
- strlcat(path, "index.html", sizeof(path));
+ if (canonicalize_path(HTTPD_DOCROOT,
+ desc->http_path, path, sizeof(path)) == NULL) {
+ server_abort_http(clt, 404, path);
+ return (-1);
+ }
+
+ /* Prepend default index file */
+ if (path[strlen(path) - 1] == '/' &&
+ strlcat(path, HTTPD_INDEX, sizeof(path)) >= sizeof(path)) {
+ server_abort_http(clt, 404, path);
+ return (-1);
+ }
if (access(path, R_OK) == -1) {
strlcpy(path, desc->http_path, sizeof(path));