From 4624b10aa033d27387c2c73a4ef73a9336d9afc2 Mon Sep 17 00:00:00 2001 From: chrisz Date: Sun, 4 Jan 2015 22:23:58 +0000 Subject: [PATCH] add new url stripping option: strip number Strip number path components from the beginning of the request URI before looking up the stripped-down URI at the document root. reviewed with much patience and OK by reyk@ --- usr.sbin/httpd/httpd.conf.5 | 15 ++++++++++--- usr.sbin/httpd/httpd.h | 5 ++++- usr.sbin/httpd/parse.y | 42 +++++++++++++++++++++++++----------- usr.sbin/httpd/server_fcgi.c | 29 ++++++++++++++++++------- usr.sbin/httpd/server_file.c | 17 +++++++++------ usr.sbin/httpd/server_http.c | 17 ++++++++++++++- 6 files changed, 92 insertions(+), 33 deletions(-) diff --git a/usr.sbin/httpd/httpd.conf.5 b/usr.sbin/httpd/httpd.conf.5 index ff0152acc2a..b582385ddb7 100644 --- a/usr.sbin/httpd/httpd.conf.5 +++ b/usr.sbin/httpd/httpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: httpd.conf.5,v 1.41 2015/01/03 15:49:18 reyk Exp $ +.\" $OpenBSD: httpd.conf.5,v 1.42 2015/01/04 22:23:58 chrisz Exp $ .\" .\" Copyright (c) 2014 Reyk Floeter .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: January 3 2015 $ +.Dd $Mdocdate: January 4 2015 $ .Dt HTTPD.CONF 5 .Os .Sh NAME @@ -250,7 +250,10 @@ Enable or disable logging to .Xr syslog 3 instead of the log files. .El -.It Ic root Ar directory +.It Ic root Ar option +Valid options are: +.Bl -tag -width Ds +.It Ar directory Set the document root of the server. The .Ar directory @@ -260,6 +263,12 @@ root directory of .Nm httpd . If not specified, it defaults to .Pa /htdocs . +.It Ic strip Ar number +Strip +.Ar number +path components from the beginning of the request URI before +looking up the stripped-down URI at the document root. +.El .It Ic tcp Ar option Enable or disable the specified TCP/IP options; see .Xr tcp 4 diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h index 3bcd4b703a3..12aa1a101e9 100644 --- a/usr.sbin/httpd/httpd.h +++ b/usr.sbin/httpd/httpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: httpd.h,v 1.65 2015/01/02 19:09:52 reyk Exp $ */ +/* $OpenBSD: httpd.h,v 1.66 2015/01/04 22:23:58 chrisz Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -385,6 +385,7 @@ struct server_config { char *tls_key_file; u_int32_t flags; + int strip; u_int8_t tcpflags; int tcpbufsiz; int tcpbacklog; @@ -524,6 +525,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 *); +const char * + server_root_strip(const char *, int); struct server_config * server_getlocation(struct client *, const char *); const char * diff --git a/usr.sbin/httpd/parse.y b/usr.sbin/httpd/parse.y index e8f9b1016d2..5a9616c88dc 100644 --- a/usr.sbin/httpd/parse.y +++ b/usr.sbin/httpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.49 2015/01/03 23:54:25 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.50 2015/01/04 22:23:58 chrisz Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter @@ -130,7 +130,7 @@ typedef struct { %token ACCESS ALIAS AUTO BACKLOG BODY BUFFER CERTIFICATE CHROOT CIPHERS COMMON %token COMBINED CONNECTION DIRECTORY ERR FCGI INDEX IP KEY LISTEN LOCATION %token LOG LOGDIR MAXIMUM NO NODELAY ON PORT PREFORK REQUEST REQUESTS ROOT -%token SACK SERVER SOCKET STYLE SYSLOG TCP TIMEOUT TLS TYPES +%token SACK SERVER SOCKET STRIP STYLE SYSLOG TCP TIMEOUT TLS TYPES %token ERROR INCLUDE %token STRING %token NUMBER @@ -434,17 +434,8 @@ serveroptsl : LISTEN ON STRING opttls port { YYERROR; } } tls - | ROOT STRING { - if (strlcpy(srv->srv_conf.root, $2, - sizeof(srv->srv_conf.root)) >= - sizeof(srv->srv_conf.root)) { - yyerror("document root too long"); - free($2); - YYERROR; - } - free($2); - srv->srv_conf.flags |= SRVFLAG_ROOT; - } + | ROOT rootflags + | ROOT '{' rootflags_l '}' | DIRECTORY dirflags | DIRECTORY '{' dirflags_l '}' | logformat @@ -625,6 +616,30 @@ tlsopts : CERTIFICATE STRING { } ; +rootflags_l : rootflags comma rootflags_l + | rootflags + ; + +rootflags : STRING { + if (strlcpy(srv->srv_conf.root, $1, + sizeof(srv->srv_conf.root)) >= + sizeof(srv->srv_conf.root)) { + yyerror("document root too long"); + free($1); + YYERROR; + } + free($1); + srv->srv_conf.flags |= SRVFLAG_ROOT; + } + | STRIP NUMBER { + if ($2 < 0 || $2 > INT_MAX) { + yyerror("invalid strip number"); + YYERROR; + } + srv->srv_conf.strip = $2; + } + ; + dirflags_l : dirflags comma dirflags_l | dirflags ; @@ -962,6 +977,7 @@ lookup(char *s) { "sack", SACK }, { "server", SERVER }, { "socket", SOCKET }, + { "strip", STRIP }, { "style", STYLE }, { "syslog", SYSLOG }, { "tcp", TCP }, diff --git a/usr.sbin/httpd/server_fcgi.c b/usr.sbin/httpd/server_fcgi.c index 842214e9ccf..a6055cd157e 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.43 2014/12/21 00:54:49 guenther Exp $ */ +/* $OpenBSD: server_fcgi.c,v 1.44 2015/01/04 22:23:58 chrisz Exp $ */ /* * Copyright (c) 2014 Florian Obser @@ -101,8 +101,8 @@ server_fcgi(struct httpd *env, struct client *clt) size_t scriptlen; int pathlen; int fd = -1, ret; - const char *errstr = NULL; - char *str, *p, *script = NULL; + const char *stripped, *p, *alias, *errstr = NULL; + char *str, *script = NULL; if (srv_conf->socket[0] == ':') { struct sockaddr_storage ss; @@ -190,9 +190,13 @@ server_fcgi(struct httpd *env, struct client *clt) h->type = FCGI_PARAMS; h->content_len = param.total_len = 0; - if ((pathlen = asprintf(&script, "%s%s", srv_conf->root, - desc->http_path_alias != NULL ? - desc->http_path_alias : desc->http_path)) == -1) { + alias = desc->http_path_alias != NULL + ? desc->http_path_alias + : desc->http_path; + + stripped = server_root_strip(alias, srv_conf->strip); + if ((pathlen = asprintf(&script, "%s%s", srv_conf->root, stripped)) + == -1) { errstr = "failed to get script name"; goto fail; } @@ -213,8 +217,17 @@ server_fcgi(struct httpd *env, struct client *clt) script[scriptlen] = '\0'; } - if (fcgi_add_param(¶m, "SCRIPT_NAME", - script + strlen(srv_conf->root), clt) == -1) { + /* + * calculate length of http SCRIPT_NAME: + * add length of stripped prefix, + * subtract length of prepended local root + */ + scriptlen += (stripped - alias) - strlen(srv_conf->root); + if ((str = strndup(alias, scriptlen)) == NULL) + goto fail; + ret = fcgi_add_param(¶m, "SCRIPT_NAME", str, clt); + free(str); + if (ret == -1) { errstr = "failed to encode param"; goto fail; } diff --git a/usr.sbin/httpd/server_file.c b/usr.sbin/httpd/server_file.c index c2eca71c858..6167bdb2304 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.43 2015/01/01 14:15:02 reyk Exp $ */ +/* $OpenBSD: server_file.c,v 1.44 2015/01/04 22:23:58 chrisz Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -150,17 +150,19 @@ server_file(struct httpd *env, struct client *clt) struct http_descriptor *desc = clt->clt_descreq; struct server_config *srv_conf = clt->clt_srv_conf; char path[MAXPATHLEN]; - const char *errstr = NULL; + const char *stripped, *errstr = NULL; int ret = 500; 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, + stripped = server_root_strip( desc->http_path_alias != NULL ? - desc->http_path_alias : desc->http_path) >= sizeof(path)) { + desc->http_path_alias : desc->http_path, + srv_conf->strip); + if ((size_t)snprintf(path, sizeof(path), "%s%s", + srv_conf->root, stripped) >= sizeof(path)) { errstr = desc->http_path; goto abort; } @@ -276,7 +278,7 @@ server_file_index(struct httpd *env, struct client *clt, struct stat *st) int code = 500; struct evbuffer *evb = NULL; struct media_type *media; - const char *style; + const char *stripped, *style; struct tm tm; time_t t, dir_mtime; @@ -286,8 +288,9 @@ server_file_index(struct httpd *env, struct client *clt, struct stat *st) } /* Request path is already canonicalized */ + stripped = server_root_strip(desc->http_path, srv_conf->strip); if ((size_t)snprintf(path, sizeof(path), "%s%s", - srv_conf->root, desc->http_path) >= sizeof(path)) + srv_conf->root, stripped) >= sizeof(path)) goto abort; /* Now open the file, should be readable or we have another problem */ diff --git a/usr.sbin/httpd/server_http.c b/usr.sbin/httpd/server_http.c index 3a5d84e7ada..d83c7b4556a 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.58 2015/01/01 14:15:02 reyk Exp $ */ +/* $OpenBSD: server_http.c,v 1.59 2015/01/04 22:23:58 chrisz Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -887,6 +887,21 @@ server_response(struct httpd *httpd, struct client *clt) return (-1); } +const char * +server_root_strip(const char *path, int n) +{ + const char *p; + + /* Strip strip leading directories. Leading '/' is ignored. */ + for (; n > 0 && *path != '\0'; n--) + if ((p = strchr(++path, '/')) == NULL) + path = strchr(path, '\0'); + else + path = p; + + return (path); +} + struct server_config * server_getlocation(struct client *clt, const char *path) { -- 2.20.1