From de6550b143b4867d7b323aa57e845c3161977b02 Mon Sep 17 00:00:00 2001 From: reyk Date: Fri, 8 Aug 2014 18:29:42 +0000 Subject: [PATCH] When opening directories, re-match the location after the index file 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 | 5 +- usr.sbin/httpd/httpd.h | 4 +- usr.sbin/httpd/server_fcgi.c | 5 +- usr.sbin/httpd/server_file.c | 121 +++++++++++++++++++++++++---------- usr.sbin/httpd/server_http.c | 33 +++++++--- 5 files changed, 119 insertions(+), 49 deletions(-) diff --git a/usr.sbin/httpd/http.h b/usr.sbin/httpd/http.h index 44f22ba7006..1918525be5a 100644 --- a/usr.sbin/httpd/http.h +++ b/usr.sbin/httpd/http.h @@ -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 @@ -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; diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h index 74d6904e206..1a8042840c0 100644 --- a/usr.sbin/httpd/httpd.h +++ b/usr.sbin/httpd/httpd.h @@ -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 @@ -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); diff --git a/usr.sbin/httpd/server_fcgi.c b/usr.sbin/httpd/server_fcgi.c index 6850dd0a30f..4cf4aa40551 100644 --- a/usr.sbin/httpd/server_fcgi.c +++ b/usr.sbin/httpd/server_fcgi.c @@ -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 @@ -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; diff --git a/usr.sbin/httpd/server_file.c b/usr.sbin/httpd/server_file.c index 65ecb0100b8..1b35037ed85 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.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 @@ -46,30 +46,25 @@ #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); } diff --git a/usr.sbin/httpd/server_http.c b/usr.sbin/httpd/server_http.c index 3ef58e5e902..9e09428a628 100644 --- a/usr.sbin/httpd/server_http.c +++ b/usr.sbin/httpd/server_http.c @@ -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 @@ -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 -- 2.20.1