From 33cf7fc3b04dd7a52821a978e20e654cc9e91be1 Mon Sep 17 00:00:00 2001 From: reyk Date: Wed, 23 Jul 2014 19:03:56 +0000 Subject: [PATCH] Add canonicalize_path() to canonicalize the requested URL path. --- usr.sbin/httpd/httpd.c | 65 +++++++++++++++++++++++++++++++++++- usr.sbin/httpd/httpd.h | 5 ++- usr.sbin/httpd/server_file.c | 20 +++++++---- 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 @@ -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 @@ -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 @@ -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)); -- 2.20.1