When opening directories, re-match the location after the index file
authorreyk <reyk@openbsd.org>
Fri, 8 Aug 2014 18:29:42 +0000 (18:29 +0000)
committerreyk <reyk@openbsd.org>
Fri, 8 Aug 2014 18:29:42 +0000 (18:29 +0000)
has been appended.  This allows to use a fastcgi target as the default
index, for example index.php.

OK florian@

usr.sbin/httpd/http.h
usr.sbin/httpd/httpd.h
usr.sbin/httpd/server_fcgi.c
usr.sbin/httpd/server_file.c
usr.sbin/httpd/server_http.c

index 44f22ba..1918525 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: http.h,v 1.5 2014/08/03 21:33:27 reyk Exp $   */
+/*     $OpenBSD: http.h,v 1.6 2014/08/08 18:29:42 reyk Exp $   */
 
 /*
  * Copyright (c) 2012 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -156,6 +156,9 @@ struct http_descriptor {
        int                      http_chunked;
        char                    *http_version;
 
+       /* Rewritten path remains NULL if not used */
+       char                    *http_path_alias;
+
        /* A tree of headers and attached lists for repeated headers. */
        struct kv               *http_lastheader;
        struct kvtree            http_headers;
index 74d6904..1a80428 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: httpd.h,v 1.51 2014/08/06 18:21:14 reyk Exp $ */
+/*     $OpenBSD: httpd.h,v 1.52 2014/08/08 18:29:42 reyk Exp $ */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -516,6 +516,8 @@ int  server_response_http(struct client *, u_int, struct media_type *,
 void    server_reset_http(struct client *);
 void    server_close_http(struct client *);
 int     server_response(struct httpd *, struct client *);
+struct server_config *
+        server_getlocation(struct client *, const char *);
 const char *
         server_http_host(struct sockaddr_storage *, char *, size_t);
 void    server_http_date(char *, size_t);
index 6850dd0..4cf4aa4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server_fcgi.c,v 1.29 2014/08/07 12:43:22 florian Exp $        */
+/*     $OpenBSD: server_fcgi.c,v 1.30 2014/08/08 18:29:42 reyk Exp $   */
 
 /*
  * Copyright (c) 2014 Florian Obser <florian@openbsd.org>
@@ -190,7 +190,8 @@ server_fcgi(struct httpd *env, struct client *clt)
        h->content_len = param.total_len = 0;
 
        if (asprintf(&script, "%s%s", srv_conf->root,
-           desc->http_path) == -1 ||
+           desc->http_path_alias != NULL ?
+           desc->http_path_alias : desc->http_path) == -1 ||
            (scriptlen = path_info(script)) == -1) {
                errstr = "failed to get script name";
                goto fail;
index 65ecb01..1b35037 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server_file.c,v 1.31 2014/08/06 11:24:12 reyk Exp $   */
+/*     $OpenBSD: server_file.c,v 1.32 2014/08/08 18:29:42 reyk Exp $   */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
 #include "httpd.h"
 #include "http.h"
 
-int     server_file_access(struct client *, char *, size_t,
+int     server_file_access(struct httpd *, struct client *, char *,
+           size_t, struct stat *);
+int     server_file_request(struct httpd *, struct client *, char *,
            struct stat *);
 int     server_file_index(struct httpd *, struct client *);
+int     server_file_method(struct client *);
 
 int
-server_file_access(struct client *clt, char *path, size_t len,
-    struct stat *st)
+server_file_access(struct httpd *env, struct client *clt,
+    char *path, size_t len, struct stat *st)
 {
        struct http_descriptor  *desc = clt->clt_desc;
        struct server_config    *srv_conf = clt->clt_srv_conf;
        struct stat              stb;
        char                    *newpath;
+       int                      ret;
 
        errno = 0;
 
-       switch (desc->http_method) {
-       case HTTP_METHOD_GET:
-       case HTTP_METHOD_HEAD:
-               break;
-       default:
-               /* Other methods are not allowed */
-               return (405);
-       }
-
        if (access(path, R_OK) == -1) {
                goto fail;
        } else if (stat(path, st) == -1) {
@@ -81,7 +76,7 @@ server_file_access(struct client *clt, char *path, size_t len,
                        goto fail;
                }
 
-               if (!len) {
+               if (desc->http_path_alias != NULL) {
                        /* Recursion - the index "file" is a directory? */
                        errno = EINVAL;
                        goto fail;
@@ -93,21 +88,31 @@ server_file_access(struct client *clt, char *path, size_t len,
                            srv_conf->flags & SRVFLAG_SSL ? "s" : "",
                            desc->http_host, desc->http_path) == -1)
                                return (500);
-                       free(desc->http_path);
-                       desc->http_path = newpath;
+                       /* Path alias will be used for the redirection */
+                       desc->http_path_alias = newpath;
 
                        /* Indicate that the file has been moved */
                        return (301);
                }
 
-               /* Otherwise append the default index file */
+               /* Append the default index file to the location */
+               if (asprintf(&newpath, "%s%s", desc->http_path,
+                   srv_conf->index) == -1)
+                       return (500);
+               desc->http_path_alias = newpath;
+               if (server_getlocation(clt, newpath) != srv_conf) {
+                       /* The location has changed */
+                       return (server_file(env, clt));
+               }
+
+               /* Otherwise append the default index file to the path */
                if (strlcat(path, srv_conf->index, len) >= len) {
                        errno = EACCES;
                        goto fail;
                }
 
-               /* Check again but set len to 0 to avoid recursion */
-               if (server_file_access(clt, path, 0, &stb) == 404) {
+               ret = server_file_access(env, clt, path, len, &stb);
+               if (ret == 404) {
                        /*
                         * Index file not found; fail if auto-indexing is
                         * not enabled, otherwise return success but
@@ -118,17 +123,17 @@ server_file_access(struct client *clt, char *path, size_t len,
                                errno = EACCES;
                                goto fail;
                        }
-               } else {
-                       /* return updated stat from index file */
-                       memcpy(st, &stb, sizeof(*st));
+
+                       return (server_file_index(env, clt));
                }
+               return (ret);
        } else if (!S_ISREG(st->st_mode)) {
                /* Don't follow symlinks and ignore special files */
                errno = EACCES;
                goto fail;
        }
 
-       return (0);
+       return (server_file_request(env, clt, path, st));
 
  fail:
        switch (errno) {
@@ -148,29 +153,69 @@ server_file(struct httpd *env, struct client *clt)
 {
        struct http_descriptor  *desc = clt->clt_desc;
        struct server_config    *srv_conf = clt->clt_srv_conf;
-       struct media_type       *media;
-       const char              *errstr = NULL;
-       int                      fd = -1, ret, code = 500;
        char                     path[MAXPATHLEN];
+       const char              *errstr = NULL;
+       int                      ret = 500;
        struct stat              st;
 
+       if (srv_conf->flags & SRVFLAG_FCGI)
+               return (server_fcgi(env, clt));
+
        /* Request path is already canonicalized */
        if ((size_t)snprintf(path, sizeof(path), "%s%s",
-           srv_conf->root, desc->http_path) >= sizeof(path)) {
+           srv_conf->root,
+           desc->http_path_alias != NULL ?
+           desc->http_path_alias : desc->http_path) >= sizeof(path)) {
                errstr = desc->http_path;
                goto abort;
        }
 
        /* Returns HTTP status code on error */
-       if ((ret = server_file_access(clt, path, sizeof(path), &st)) != 0) {
-               code = ret;
-               errstr = desc->http_path;
+       if ((ret = server_file_access(env, clt, path, sizeof(path),
+           &st)) > 0) {
+               errstr = desc->http_path_alias != NULL ?
+                   desc->http_path_alias : desc->http_path;
                goto abort;
        }
 
-       if (S_ISDIR(st.st_mode)) {
-               /* List directory index */
-               return (server_file_index(env, clt));
+       return (ret);
+
+ abort:
+       if (errstr == NULL)
+               errstr = strerror(errno);
+       server_abort_http(clt, ret, errstr);
+       return (-1);
+}
+
+int
+server_file_method(struct client *clt)
+{
+       struct http_descriptor  *desc = clt->clt_desc;
+
+       switch (desc->http_method) {
+       case HTTP_METHOD_GET:
+       case HTTP_METHOD_HEAD:
+               return (0);
+       default:
+               /* Other methods are not allowed */
+               errno = EACCES;
+               return (405);
+       }
+       /* NOTREACHED */
+}
+
+int
+server_file_request(struct httpd *env, struct client *clt, char *path,
+    struct stat *st)
+{
+       struct server_config    *srv_conf = clt->clt_srv_conf;
+       struct media_type       *media;
+       const char              *errstr = NULL;
+       int                      fd = -1, ret, code = 500;
+
+       if ((ret = server_file_method(clt)) != 0) {
+               code = ret;
+               goto abort;
        }
 
        /* Now open the file, should be readable or we have another problem */
@@ -178,7 +223,7 @@ server_file(struct httpd *env, struct client *clt)
                goto abort;
 
        media = media_find(env->sc_mediatypes, path);
-       ret = server_response_http(clt, 200, media, st.st_size);
+       ret = server_response_http(clt, 200, media, st->st_size);
        switch (ret) {
        case -1:
                goto fail;
@@ -233,6 +278,7 @@ server_file_index(struct httpd *env, struct client *clt)
        struct server_config     *srv_conf = clt->clt_srv_conf;
        struct dirent           **namelist, *dp;
        int                       namesize, i, ret, fd = -1, namewidth, skip;
+       int                       code = 500;
        struct evbuffer          *evb = NULL;
        struct media_type        *media;
        const char               *style;
@@ -240,6 +286,11 @@ server_file_index(struct httpd *env, struct client *clt)
        struct tm                 tm;
        time_t                    t;
 
+       if ((ret = server_file_method(clt)) != 0) {
+               code = ret;
+               goto abort;
+       }
+
        /* Request path is already canonicalized */
        if ((size_t)snprintf(path, sizeof(path), "%s%s",
            srv_conf->root, desc->http_path) >= sizeof(path))
@@ -356,7 +407,7 @@ server_file_index(struct httpd *env, struct client *clt)
                close(fd);
        if (evb != NULL)
                evbuffer_free(evb);
-       server_abort_http(clt, 500, desc->http_path);
+       server_abort_http(clt, code, desc->http_path);
        return (-1);
 }
 
index 3ef58e5..9e09428 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server_http.c,v 1.43 2014/08/08 15:46:01 reyk Exp $   */
+/*     $OpenBSD: server_http.c,v 1.44 2014/08/08 18:29:42 reyk Exp $   */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -100,6 +100,10 @@ server_httpdesc_free(struct http_descriptor *desc)
                free(desc->http_path);
                desc->http_path = NULL;
        }
+       if (desc->http_path_alias != NULL) {
+               free(desc->http_path_alias);
+               desc->http_path_alias = NULL;
+       }
        if (desc->http_query != NULL) {
                free(desc->http_query);
                desc->http_query = NULL;
@@ -680,7 +684,7 @@ server_response(struct httpd *httpd, struct client *clt)
        char                     hostname[MAXHOSTNAMELEN];
        struct http_descriptor  *desc   = clt->clt_desc;
        struct server           *srv = clt->clt_srv;
-       struct server_config    *srv_conf = &srv->srv_conf, *location;
+       struct server_config    *srv_conf = &srv->srv_conf;
        struct kv               *kv, key, *host;
 
        /* Canonicalize the request path */
@@ -755,24 +759,33 @@ server_response(struct httpd *httpd, struct client *clt)
        if ((desc->http_host = strdup(hostname)) == NULL)
                goto fail;
 
+       /* Now search for the location */
+       srv_conf = server_getlocation(clt, desc->http_path);
+
+       return (server_file(httpd, clt));
+ fail:
+       server_abort_http(clt, 400, "bad request");
+       return (-1);
+}
+
+struct server_config *
+server_getlocation(struct client *clt, const char *path)
+{
+       struct server           *srv = clt->clt_srv;
+       struct server_config    *srv_conf = clt->clt_srv_conf, *location;
+
        /* Now search for the location */
        TAILQ_FOREACH(location, &srv->srv_hosts, entry) {
                if ((location->flags & SRVFLAG_LOCATION) &&
                    location->id == srv_conf->id &&
-                   fnmatch(location->location, desc->http_path,
-                   FNM_CASEFOLD) == 0) {
+                   fnmatch(location->location, path, FNM_CASEFOLD) == 0) {
                        /* Replace host configuration */
                        clt->clt_srv_conf = srv_conf = location;
                        break;
                }
        }
 
-       if (srv_conf->flags & SRVFLAG_FCGI)
-               return (server_fcgi(httpd, clt));
-       return (server_file(httpd, clt));
- fail:
-       server_abort_http(clt, 400, "bad request");
-       return (-1);
+       return (srv_conf);
 }
 
 int