Add canonicalize_path() to canonicalize the requested URL path.
authorreyk <reyk@openbsd.org>
Wed, 23 Jul 2014 19:03:56 +0000 (19:03 +0000)
committerreyk <reyk@openbsd.org>
Wed, 23 Jul 2014 19:03:56 +0000 (19:03 +0000)
usr.sbin/httpd/httpd.c
usr.sbin/httpd/httpd.h
usr.sbin/httpd/server_file.c

index f8f0323..100ea54 100644 (file)
@@ -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)
 {
index d23e6a8..ae275bf 100644 (file)
@@ -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);
index 2b51115..e23086f 100644 (file)
@@ -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));