diff options
Diffstat (limited to 'httpd/server_http.c')
-rw-r--r-- | httpd/server_http.c | 255 |
1 files changed, 194 insertions, 61 deletions
diff --git a/httpd/server_http.c b/httpd/server_http.c index 7b65a5e..9a6609e 100644 --- a/httpd/server_http.c +++ b/httpd/server_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_http.c,v 1.79 2015/05/03 18:39:58 florian Exp $ */ +/* $OpenBSD: server_http.c,v 1.89 2015/07/16 19:05:28 reyk Exp $ */ /* * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org> @@ -29,14 +29,17 @@ #include <string.h> #include <unistd.h> #include <limits.h> +#include <fnmatch.h> #include <stdio.h> #include <time.h> #include <resolv.h> #include <event.h> -#include <fnmatch.h> +#include <ctype.h> +#include <vis.h> #include "httpd.h" #include "http.h" +#include "patterns.h" static int server_httpmethod_cmp(const void *, const void *); static int server_httperror_cmp(const void *, const void *); @@ -633,6 +636,7 @@ server_reset_http(struct client *clt) clt->clt_remote_user = NULL; clt->clt_bev->readcb = server_read_http; clt->clt_srv_conf = &srv->srv_conf; + str_match_free(&clt->clt_srv_match); } ssize_t @@ -738,7 +742,7 @@ server_abort_http(struct client *clt, u_int code, const char *msg) const char *httperr = NULL, *style; char *httpmsg, *body = NULL, *extraheader = NULL; char tmbuf[32], hbuf[128]; - char buf[IBUF_READ_SIZE], *ptr = NULL; + char buf[IBUF_READ_SIZE]; int bodylen; if (code == 0) { @@ -770,16 +774,13 @@ server_abort_http(struct client *clt, u_int code, const char *msg) if (msg == NULL) break; memset(buf, 0, sizeof(buf)); - if ((ptr = server_expand_http(clt, msg, - buf, sizeof(buf))) == NULL) - goto done; - if ((ptr = url_encode(ptr)) == NULL) + if (server_expand_http(clt, msg, buf, sizeof(buf)) == NULL) goto done; - if (asprintf(&extraheader, "Location: %s\r\n", ptr) == -1) { + if (asprintf(&extraheader, "Location: %s\r\n", buf) == -1) { code = 500; extraheader = NULL; } - msg = ptr; + msg = buf; break; case 401: if (asprintf(&extraheader, @@ -858,7 +859,6 @@ server_abort_http(struct client *clt, u_int code, const char *msg) server_close(clt, httpmsg); free(httpmsg); } - free(ptr); } void @@ -877,6 +877,8 @@ server_close_http(struct client *clt) clt->clt_descresp = NULL; free(clt->clt_remote_user); clt->clt_remote_user = NULL; + + str_match_free(&clt->clt_srv_match); } char * @@ -885,20 +887,56 @@ server_expand_http(struct client *clt, const char *val, char *buf, { struct http_descriptor *desc = clt->clt_descreq; struct server_config *srv_conf = clt->clt_srv_conf; - char ibuf[128], *str; + char ibuf[128], *str, *path, *query; + const char *errstr = NULL, *p; + size_t size; + int n, ret; if (strlcpy(buf, val, len) >= len) return (NULL); + /* Find previously matched substrings by index */ + for (p = val; clt->clt_srv_match.sm_nmatch && + (p = strstr(p, "%")) != NULL; p++) { + if (!isdigit(*(p + 1))) + continue; + + /* Copy number, leading '%' char and add trailing \0 */ + size = strspn(p + 1, "0123456789") + 2; + if (size >= sizeof(ibuf)) + return (NULL); + (void)strlcpy(ibuf, p, size); + n = strtonum(ibuf + 1, 0, + clt->clt_srv_match.sm_nmatch - 1, &errstr); + if (errstr != NULL) + return (NULL); + + /* Expand variable with matched value */ + if ((str = url_encode(clt->clt_srv_match.sm_match[n])) == NULL) + return (NULL); + ret = expand_string(buf, len, ibuf, str); + free(str); + if (ret != 0) + return (NULL); + } if (strstr(val, "$DOCUMENT_URI") != NULL) { - if (expand_string(buf, len, "$DOCUMENT_URI", - desc->http_path) != 0) + if ((path = url_encode(desc->http_path)) == NULL) + return (NULL); + ret = expand_string(buf, len, "$DOCUMENT_URI", path); + free(path); + if (ret != 0) return (NULL); } if (strstr(val, "$QUERY_STRING") != NULL) { - if (expand_string(buf, len, "$QUERY_STRING", - desc->http_query == NULL ? "" : - desc->http_query) != 0) + if (desc->http_query == NULL) { + ret = expand_string(buf, len, "$QUERY_STRING", ""); + } else { + if ((query = url_encode(desc->http_query)) == NULL) + return (NULL); + ret = expand_string(buf, len, "$QUERY_STRING", query); + free(query); + } + if (ret != 0) return (NULL); } if (strstr(val, "$REMOTE_") != NULL) { @@ -919,27 +957,39 @@ server_expand_http(struct client *clt, const char *val, char *buf, } if (strstr(val, "$REMOTE_USER") != NULL) { if ((srv_conf->flags & SRVFLAG_AUTH) && - clt->clt_remote_user != NULL) - str = clt->clt_remote_user; - else - str = ""; - if (expand_string(buf, len, - "$REMOTE_USER", str) != 0) + clt->clt_remote_user != NULL) { + if ((str = url_encode(clt->clt_remote_user)) + == NULL) + return (NULL); + } else + str = strdup(""); + ret = expand_string(buf, len, "$REMOTE_USER", str); + free(str); + if (ret != 0) return (NULL); } } if (strstr(val, "$REQUEST_URI") != NULL) { + if ((path = url_encode(desc->http_path)) == NULL) + return (NULL); if (desc->http_query == NULL) { - if ((str = strdup(desc->http_path)) == NULL) + str = path; + } else { + if ((query = url_encode(desc->http_query)) == NULL) { + free(path); + return (NULL); + } + ret = asprintf(&str, "%s?%s", path, query); + free(path); + free(query); + if (ret == -1) return (NULL); - } else if (asprintf(&str, "%s?%s", - desc->http_path, desc->http_query) == -1) - return (NULL); - if (expand_string(buf, len, "$REQUEST_URI", str) != 0) { - free(str); - return (NULL); } + + ret = expand_string(buf, len, "$REQUEST_URI", str); free(str); + if (ret != 0) + return (NULL); } if (strstr(val, "$SERVER_") != NULL) { if (strstr(val, "$SERVER_ADDR") != NULL) { @@ -958,8 +1008,12 @@ server_expand_http(struct client *clt, const char *val, char *buf, return (NULL); } if (strstr(val, "$SERVER_NAME") != NULL) { - if (expand_string(buf, len, - "$SERVER_NAME", srv_conf->name) != 0) + if ((str = url_encode(srv_conf->name)) + == NULL) + return (NULL); + ret = expand_string(buf, len, "$SERVER_NAME", str); + free(str); + if (ret != 0) return (NULL); } } @@ -977,8 +1031,10 @@ server_response(struct httpd *httpd, struct client *clt) struct server *srv = clt->clt_srv; struct server_config *srv_conf = &srv->srv_conf; struct kv *kv, key, *host; - int portval = -1; + struct str_find sm; + int portval = -1, ret; char *hostval; + const char *errstr = NULL; /* Canonicalize the request path */ if (desc->http_path == NULL || @@ -1038,9 +1094,17 @@ server_response(struct httpd *httpd, struct client *clt) hostname); } #endif - if ((srv_conf->flags & SRVFLAG_LOCATION) == 0 && - fnmatch(srv_conf->name, hostname, - FNM_CASEFOLD) == 0 && + if (srv_conf->flags & SRVFLAG_LOCATION) + continue; + else if (srv_conf->flags & SRVFLAG_SERVER_MATCH) { + str_find(hostname, srv_conf->name, + &sm, 1, &errstr); + ret = errstr == NULL ? 0 : -1; + } else { + ret = fnmatch(srv_conf->name, + hostname, FNM_CASEFOLD); + } + if (ret == 0 && (portval == -1 || (portval != -1 && portval == srv_conf->port))) { /* Replace host configuration */ @@ -1110,6 +1174,8 @@ server_getlocation(struct client *clt, const char *path) { struct server *srv = clt->clt_srv; struct server_config *srv_conf = clt->clt_srv_conf, *location; + const char *errstr = NULL; + int ret; /* Now search for the location */ TAILQ_FOREACH(location, &srv->srv_hosts, entry) { @@ -1120,11 +1186,20 @@ server_getlocation(struct client *clt, const char *path) } #endif if ((location->flags & SRVFLAG_LOCATION) && - location->parent_id == srv_conf->parent_id && - fnmatch(location->location, path, FNM_CASEFOLD) == 0) { - /* Replace host configuration */ - clt->clt_srv_conf = srv_conf = location; - break; + location->parent_id == srv_conf->parent_id) { + errstr = NULL; + if (location->flags & SRVFLAG_LOCATION_MATCH) { + ret = str_match(path, location->location, + &clt->clt_srv_match, &errstr); + } else { + ret = fnmatch(location->location, + path, FNM_CASEFOLD); + } + if (ret == 0 && errstr == NULL) { + /* Replace host configuration */ + clt->clt_srv_conf = srv_conf = location; + break; + } } } @@ -1133,7 +1208,7 @@ server_getlocation(struct client *clt, const char *path) int server_response_http(struct client *clt, u_int code, - struct media_type *media, size_t size, time_t mtime) + struct media_type *media, off_t size, time_t mtime) { struct http_descriptor *desc = clt->clt_descreq; struct http_descriptor *resp = clt->clt_descresp; @@ -1174,7 +1249,7 @@ server_response_http(struct client *clt, u_int code, /* Set content length, if specified */ if ((cl = kv_add(&resp->http_headers, "Content-Length", NULL)) == NULL || - kv_set(cl, "%ld", size) == -1) + kv_set(cl, "%lld", (long long)size) == -1) return (-1); /* Set last modification time */ @@ -1352,6 +1427,13 @@ server_log_http(struct client *clt, u_int code, size_t len) struct tm *tm; struct server_config *srv_conf; struct http_descriptor *desc; + int ret = -1; + char *user = NULL; + char *path = NULL; + char *query = NULL; + char *version = NULL; + char *referrer_v = NULL; + char *agent_v = NULL; if ((srv_conf = clt->clt_srv_conf) == NULL) return (-1); @@ -1380,18 +1462,34 @@ server_log_http(struct client *clt, u_int code, size_t len) */ switch (srv_conf->logformat) { case LOG_FORMAT_COMMON: - if (evbuffer_add_printf(clt->clt_log, + /* Use vis to encode input values from the header */ + if (clt->clt_remote_user && + stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1) + goto done; + if (desc->http_version && + stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1) + goto done; + + /* The following should be URL-encoded */ + if (desc->http_path && + (path = url_encode(desc->http_path)) == NULL) + goto done; + if (desc->http_query && + (query = url_encode(desc->http_query)) == NULL) + goto done; + + ret = evbuffer_add_printf(clt->clt_log, "%s %s - %s [%s] \"%s %s%s%s%s%s\" %03d %zu\n", srv_conf->name, ip, clt->clt_remote_user == NULL ? "-" : - clt->clt_remote_user, tstamp, + user, tstamp, server_httpmethod_byid(desc->http_method), - desc->http_path == NULL ? "" : desc->http_path, + desc->http_path == NULL ? "" : path, desc->http_query == NULL ? "" : "?", - desc->http_query == NULL ? "" : desc->http_query, + desc->http_query == NULL ? "" : query, desc->http_version == NULL ? "" : " ", - desc->http_version == NULL ? "" : desc->http_version, - code, len) == -1) - return (-1); + desc->http_version == NULL ? "" : version, + code, len); + break; case LOG_FORMAT_COMBINED: @@ -1405,29 +1503,64 @@ server_log_http(struct client *clt, u_int code, size_t len) agent->kv_value == NULL) agent = NULL; - if (evbuffer_add_printf(clt->clt_log, + /* Use vis to encode input values from the header */ + if (clt->clt_remote_user && + stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1) + goto done; + if (desc->http_version && + stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1) + goto done; + if (agent && + stravis(&agent_v, agent->kv_value, HTTPD_LOGVIS) == -1) + goto done; + + /* The following should be URL-encoded */ + if (desc->http_path && + (path = url_encode(desc->http_path)) == NULL) + goto done; + if (desc->http_query && + (query = url_encode(desc->http_query)) == NULL) + goto done; + if (referrer && + (referrer_v = url_encode(referrer->kv_value)) == NULL) + goto done; + + ret = evbuffer_add_printf(clt->clt_log, "%s %s - %s [%s] \"%s %s%s%s%s%s\"" " %03d %zu \"%s\" \"%s\"\n", srv_conf->name, ip, clt->clt_remote_user == NULL ? "-" : - clt->clt_remote_user, tstamp, + user, tstamp, server_httpmethod_byid(desc->http_method), - desc->http_path == NULL ? "" : desc->http_path, + desc->http_path == NULL ? "" : path, desc->http_query == NULL ? "" : "?", - desc->http_query == NULL ? "" : desc->http_query, + desc->http_query == NULL ? "" : query, desc->http_version == NULL ? "" : " ", - desc->http_version == NULL ? "" : desc->http_version, + desc->http_version == NULL ? "" : version, code, len, - referrer == NULL ? "" : referrer->kv_value, - agent == NULL ? "" : agent->kv_value) == -1) - return (-1); + referrer == NULL ? "" : referrer_v, + agent == NULL ? "" : agent_v); + break; case LOG_FORMAT_CONNECTION: - if (evbuffer_add_printf(clt->clt_log, " [%s]", - desc->http_path == NULL ? "" : desc->http_path) == -1) - return (-1); + /* URL-encode the path */ + if (desc->http_path && + (path = url_encode(desc->http_path)) == NULL) + goto done; + + ret = evbuffer_add_printf(clt->clt_log, " [%s]", + desc->http_path == NULL ? "" : path); + break; } - return (0); +done: + free(user); + free(path); + free(query); + free(version); + free(referrer_v); + free(agent_v); + + return (ret); } |