This just made my day. -----Original Message----- From: "Ted Unangst" <t...@tedunangst.com> Sent: 11/20/2014 6:26 PM To: "tech@openbsd.org" <tech@openbsd.org> Subject: httpd errata
[on behalf of reyk] Many people want to test the new httpd in OpenBSD 5.6; so we decided to provide various improvements from -current for 5.6. See the description below for more details. untrusted comment: signature from openbsd 5.6 base private key RWR0EANmo9nqhn3Gnfk2/2x+xII6do92zreKp/t5zOwfkVgsQAI4ZCPkWAazbbnWNV7Ptkle876f/kb6C2KuvnTqvwUItsyvogA= OpenBSD 5.6 errata 9, Nov 18, 2014: httpd was developed very rapidly in the weeks before 5.6 release, and it has a few flaws. It would be nice to get these flaws fully remediated before the next release, and that requires the community to want to use it. Therefore here is a "jumbo" patch that brings in the most important fixes. Apply patch using: signify -Vep /etc/signify/openbsd-56-base.pub -x 009_httpd.patch.sig \ -m - | (cd /usr/src && patch -p0) Then build and install httpd: cd /usr/src/usr.sbin/httpd make obj make make install Index: usr.sbin/httpd/config.c =================================================================== RCS file: /cvs/src/usr.sbin/httpd/config.c,v retrieving revision 1.21 diff -u -p -r1.21 config.c --- usr.sbin/httpd/config.c 6 Aug 2014 18:21:14 -0000 1.21 +++ usr.sbin/httpd/config.c 18 Nov 2014 15:02:54 -0000 @@ -223,7 +223,7 @@ config_getserver_config(struct httpd *en #ifdef DEBUG struct privsep *ps = env->sc_ps; #endif - struct server_config *srv_conf; + struct server_config *srv_conf, *parent; u_int8_t *p = imsg->data; u_int f; @@ -233,18 +233,28 @@ config_getserver_config(struct httpd *en IMSG_SIZE_CHECK(imsg, srv_conf); memcpy(srv_conf, p, sizeof(*srv_conf)); + /* Reset these variables to avoid free'ing invalid pointers */ + serverconfig_reset(srv_conf); + + TAILQ_FOREACH(parent, &srv->srv_hosts, entry) { + if (strcmp(parent->name, srv_conf->name) == 0) + break; + } + if (parent == NULL) + parent = &srv->srv_conf; + if (srv_conf->flags & SRVFLAG_LOCATION) { /* Inherit configuration from the parent */ f = SRVFLAG_INDEX|SRVFLAG_NO_INDEX; if ((srv_conf->flags & f) == 0) { - srv_conf->flags |= srv->srv_conf.flags & f; - (void)strlcpy(srv_conf->index, srv->srv_conf.index, + srv_conf->flags |= parent->flags & f; + (void)strlcpy(srv_conf->index, parent->index, sizeof(srv_conf->index)); } f = SRVFLAG_AUTO_INDEX|SRVFLAG_NO_AUTO_INDEX; if ((srv_conf->flags & f) == 0) - srv_conf->flags |= srv->srv_conf.flags & f; + srv_conf->flags |= parent->flags & f; f = SRVFLAG_SOCKET|SRVFLAG_FCGI; if ((srv_conf->flags & f) == SRVFLAG_FCGI) { @@ -255,48 +265,48 @@ config_getserver_config(struct httpd *en f = SRVFLAG_ROOT; if ((srv_conf->flags & f) == 0) { - srv_conf->flags |= srv->srv_conf.flags & f; - (void)strlcpy(srv_conf->root, srv->srv_conf.root, + srv_conf->flags |= parent->flags & f; + (void)strlcpy(srv_conf->root, parent->root, sizeof(srv_conf->root)); } f = SRVFLAG_FCGI|SRVFLAG_NO_FCGI; if ((srv_conf->flags & f) == 0) - srv_conf->flags |= srv->srv_conf.flags & f; + srv_conf->flags |= parent->flags & f; f = SRVFLAG_LOG|SRVFLAG_NO_LOG; if ((srv_conf->flags & f) == 0) { - srv_conf->flags |= srv->srv_conf.flags & f; - srv_conf->logformat = srv->srv_conf.logformat; + srv_conf->flags |= parent->flags & f; + srv_conf->logformat = parent->logformat; } f = SRVFLAG_SYSLOG|SRVFLAG_NO_SYSLOG; if ((srv_conf->flags & f) == 0) - srv_conf->flags |= srv->srv_conf.flags & f; + srv_conf->flags |= parent->flags & f; f = SRVFLAG_SSL; - srv_conf->flags |= srv->srv_conf.flags & f; + srv_conf->flags |= parent->flags & f; f = SRVFLAG_ACCESS_LOG; if ((srv_conf->flags & f) == 0) { - srv_conf->flags |= srv->srv_conf.flags & f; + srv_conf->flags |= parent->flags & f; (void)strlcpy(srv_conf->accesslog, - srv->srv_conf.accesslog, + parent->accesslog, sizeof(srv_conf->accesslog)); } f = SRVFLAG_ERROR_LOG; if ((srv_conf->flags & f) == 0) { - srv_conf->flags |= srv->srv_conf.flags & f; + srv_conf->flags |= parent->flags & f; (void)strlcpy(srv_conf->errorlog, - srv->srv_conf.errorlog, + parent->errorlog, sizeof(srv_conf->errorlog)); } - memcpy(&srv_conf->timeout, &srv->srv_conf.timeout, + memcpy(&srv_conf->timeout, &parent->timeout, sizeof(srv_conf->timeout)); - srv_conf->maxrequests = srv->srv_conf.maxrequests; - srv_conf->maxrequestbody = srv->srv_conf.maxrequestbody; + srv_conf->maxrequests = parent->maxrequests; + srv_conf->maxrequestbody = parent->maxrequestbody; DPRINTF("%s: %s %d location \"%s\", " "parent \"%s\", flags: %s", @@ -330,6 +340,9 @@ config_getserver(struct httpd *env, stru IMSG_SIZE_CHECK(imsg, &srv_conf); memcpy(&srv_conf, p, sizeof(srv_conf)); s = sizeof(srv_conf); + + /* Reset these variables to avoid free'ing invalid pointers */ + serverconfig_reset(&srv_conf); if ((u_int)(IMSG_DATA_SIZE(imsg) - s) < (srv_conf.ssl_cert_len + srv_conf.ssl_key_len)) { Index: usr.sbin/httpd/http.h =================================================================== RCS file: /cvs/src/usr.sbin/httpd/http.h,v retrieving revision 1.5 diff -u -p -r1.5 http.h --- usr.sbin/httpd/http.h 3 Aug 2014 21:33:27 -0000 1.5 +++ usr.sbin/httpd/http.h 18 Nov 2014 15:02:54 -0000 @@ -44,6 +44,32 @@ enum httpmethod { HTTP_METHOD_LOCK, HTTP_METHOD_UNLOCK, + /* WebDAV Versioning Extension, RFC 3253 */ + HTTP_METHOD_VERSION_CONTROL, + HTTP_METHOD_REPORT, + HTTP_METHOD_CHECKOUT, + HTTP_METHOD_CHECKIN, + HTTP_METHOD_UNCHECKOUT, + HTTP_METHOD_MKWORKSPACE, + HTTP_METHOD_UPDATE, + HTTP_METHOD_LABEL, + HTTP_METHOD_MERGE, + HTTP_METHOD_BASELINE_CONTROL, + HTTP_METHOD_MKACTIVITY, + + /* WebDAV Ordered Collections, RFC 3648 */ + HTTP_METHOD_ORDERPATCH, + + /* WebDAV Access Control, RFC 3744 */ + HTTP_METHOD_ACL, + + /* WebDAV Redirect Reference Resources, RFC 4437 */ + HTTP_METHOD_MKREDIRECTREF, + HTTP_METHOD_UPDATEREDIRECTREF, + + /* WebDAV Search, RFC 5323 */ + HTTP_METHOD_SEARCH, + /* PATCH, RFC 5789 */ HTTP_METHOD_PATCH, @@ -71,6 +97,22 @@ struct http_method { { HTTP_METHOD_MOVE, "MOVE" }, \ { HTTP_METHOD_LOCK, "LOCK" }, \ { HTTP_METHOD_UNLOCK, "UNLOCK" }, \ + { HTTP_METHOD_VERSION_CONTROL, "VERSION-CONTROL" }, \ + { HTTP_METHOD_REPORT, "REPORT" }, \ + { HTTP_METHOD_CHECKOUT, "CHECKOUT" }, \ + { HTTP_METHOD_CHECKIN, "CHECKIN" }, \ + { HTTP_METHOD_UNCHECKOUT, "UNCHECKOUT" }, \ + { HTTP_METHOD_MKWORKSPACE, "MKWORKSPACE" }, \ + { HTTP_METHOD_UPDATE, "UPDATE" }, \ + { HTTP_METHOD_LABEL, "LABEL" }, \ + { HTTP_METHOD_MERGE, "MERGE" }, \ + { HTTP_METHOD_BASELINE_CONTROL, "BASELINE-CONTROL" }, \ + { HTTP_METHOD_MKACTIVITY, "MKACTIVITY" }, \ + { HTTP_METHOD_ORDERPATCH, "ORDERPATCH" }, \ + { HTTP_METHOD_ACL, "ACL" }, \ + { HTTP_METHOD_MKREDIRECTREF, "MKREDIRECTREF" }, \ + { HTTP_METHOD_UPDATEREDIRECTREF, "UPDATEREDIRECTREF" }, \ + { HTTP_METHOD_SEARCH, "SEARCH" }, \ { HTTP_METHOD_PATCH, "PATCH" }, \ { HTTP_METHOD_NONE, NULL } \ } @@ -155,6 +197,9 @@ struct http_descriptor { enum httpmethod http_method; 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; Index: usr.sbin/httpd/httpd.c =================================================================== RCS file: /cvs/src/usr.sbin/httpd/httpd.c,v retrieving revision 1.17 diff -u -p -r1.17 httpd.c --- usr.sbin/httpd/httpd.c 5 Aug 2014 15:36:59 -0000 1.17 +++ usr.sbin/httpd/httpd.c 18 Nov 2014 15:02:54 -0000 @@ -289,10 +289,20 @@ parent_configure(struct httpd *env) fatal("send media"); } + /* First send the servers... */ TAILQ_FOREACH(srv, env->sc_servers, srv_entry) { + if (srv->srv_conf.flags & SRVFLAG_LOCATION) + continue; if (config_setserver(env, srv) == -1) fatal("send server"); } + /* ...and now send the locations */ + TAILQ_FOREACH(srv, env->sc_servers, srv_entry) { + if ((srv->srv_conf.flags & SRVFLAG_LOCATION) == 0) + continue; + if (config_setserver(env, srv) == -1) + fatal("send location"); + } /* The servers need to reload their config. */ env->sc_reload = env->sc_prefork_server + 1; @@ -526,6 +536,46 @@ canonicalize_host(const char *host, char } const char * +url_decode(char *url) +{ + char *p, *q; + char hex[3]; + u_long x; + + hex[2] = '\0'; + p = q = url; + + while (*p != '\0') { + switch (*p) { + case '%': + /* Encoding character is followed by two hex chars */ + if (!(isxdigit(p[1]) && isxdigit(p[2]))) + return (NULL); + + hex[0] = p[1]; + hex[1] = p[2]; + + /* + * We don't have to validate "hex" because it is + * guaranteed to include two hex chars followed by nul. + */ + x = strtoul(hex, NULL, 16); + *q = (char)x; + p += 2; + break; + default: + *q = *p; + break; + } + p++; + q++; + } + *q = '\0'; + + return(url); +} + +const char * canonicalize_path(const char *input, char *path, size_t len) { const char *i; @@ -580,28 +630,33 @@ canonicalize_path(const char *input, cha return (path); } -ssize_t -path_info(char *name) +size_t +path_info(char *path) { - char *p, *start, *end; - char path[MAXPATHLEN]; + char *p, *start, *end, ch; struct stat st; - - if (strlcpy(path, name, sizeof(path)) >= sizeof(path)) - return (-1); + int ret; start = path; end = start + strlen(path); for (p = end; p > start; p--) { - if (*p != '/') + /* Scan every path component from the end and at each '/' */ + if (p < end && *p != '/') continue; - if (stat(path, &st) == 0) - break; + + /* Temporarily cut the path component out */ + ch = *p; *p = '\0'; + ret = stat(path, &st); + *p = ch; + + /* Break if the initial path component was found */ + if (ret == 0) + break; } - return (strlen(path)); + return (p - start); } void @@ -623,6 +678,40 @@ socket_rlimit(int maxfd) rl.rlim_cur = MAX(rl.rlim_max, (rlim_t)maxfd); if (setrlimit(RLIMIT_NOFILE, &rl) == -1) fatal("socket_rlimit: failed to set resource limit"); +} + +char * +evbuffer_getline(struct evbuffer *evb) +{ + u_int8_t *ptr = EVBUFFER_DATA(evb); + size_t len = EVBUFFER_LENGTH(evb); + char *str; + u_int i; + + /* Safe version of evbuffer_readline() */ + if ((str = get_string(ptr, len)) == NULL) + return (NULL); + + for (i = 0; str[i] != '\0'; i++) { + if (str[i] == '\r' || str[i] == '\n') + break; + } + + if (i == len) { + free(str); + return (NULL); + } + + str[i] = '\0'; + + if ((i + 1) < len) { + if (ptr[i] == '\r' && ptr[i + 1] == '\n') + i++; + } + + evbuffer_drain(evb, ++i); + + return (str); } char * Index: usr.sbin/httpd/httpd.h =================================================================== RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v retrieving revision 1.51 diff -u -p -r1.51 httpd.h --- usr.sbin/httpd/httpd.h 6 Aug 2014 18:21:14 -0000 1.51 +++ usr.sbin/httpd/httpd.h 18 Nov 2014 15:02:54 -0000 @@ -276,7 +276,8 @@ struct client { size_t clt_buflen; struct evbuffer *clt_output; struct event clt_ev; - void *clt_desc; + void *clt_descreq; + void *clt_descresp; int clt_sndbufsiz; int clt_fd; @@ -294,6 +295,8 @@ struct client { int clt_fcgi_toread; int clt_fcgi_padding_len; int clt_fcgi_type; + int clt_fcgi_chunked; + int clt_fcgi_end; struct evbuffer *clt_srvevb; struct evbuffer *clt_log; @@ -463,6 +466,8 @@ pid_t server(struct privsep *, struct p int server_ssl_load_keypair(struct server *); int server_privinit(struct server *); void server_purge(struct server *); +void serverconfig_free(struct server_config *); +void serverconfig_reset(struct server_config *); int server_socket_af(struct sockaddr_storage *, in_port_t); in_port_t server_socket_getport(struct sockaddr_storage *); @@ -477,6 +482,8 @@ void server_sendlog(struct server_confi void server_close(struct client *, const char *); void server_dump(struct client *, const void *, size_t); int server_client_cmp(struct client *, struct client *); +int server_bufferevent_printf(struct client *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); int server_bufferevent_print(struct client *, const char *); int server_bufferevent_write_buffer(struct client *, struct evbuffer *); @@ -508,17 +515,20 @@ const char void server_read_httpcontent(struct bufferevent *, void *); void server_read_httpchunks(struct bufferevent *, void *); int server_writeheader_http(struct client *clt, struct kv *, void *); -int server_headers(struct client *, +int server_headers(struct client *, void *, int (*)(struct client *, struct kv *, void *), void *); int server_writeresponse_http(struct client *); int server_response_http(struct client *, u_int, struct media_type *, - size_t); + size_t, time_t); 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); +char *server_http_parsehost(char *, char *, size_t, int *); +ssize_t server_http_time(time_t, char *, size_t); int server_log_http(struct client *, u_int, size_t); /* server_file.c */ @@ -533,13 +543,15 @@ int fcgi_add_stdin(struct client *, str void event_again(struct event *, int, short, void (*)(int, short, void *), struct timeval *, struct timeval *, void *); +const char *url_decode(char *); const char *canonicalize_host(const char *, char *, size_t); const char *canonicalize_path(const char *, char *, size_t); -ssize_t path_info(char *); +size_t path_info(char *); 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); void socket_rlimit(int); +char *evbuffer_getline(struct evbuffer *); char *get_string(u_int8_t *, size_t); void *get_data(u_int8_t *, size_t); int sockaddr_cmp(struct sockaddr *, struct sockaddr *, int); @@ -575,6 +587,7 @@ void log_warn(const char *, ...) __attri void log_warnx(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void log_info(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void log_debug(const char *, ...) __attribute__((__format__ (printf, 1, 2))); +void logit(int, const char *, ...) __attribute__((__format__ (printf, 2, 3))); void vlog(int, const char *, va_list) __attribute__((__format__ (printf, 2, 0))); __dead void fatal(const char *); __dead void fatalx(const char *); Index: usr.sbin/httpd/logger.c =================================================================== RCS file: /cvs/src/usr.sbin/httpd/logger.c,v retrieving revision 1.5 diff -u -p -r1.5 logger.c --- usr.sbin/httpd/logger.c 6 Aug 2014 12:56:58 -0000 1.5 +++ usr.sbin/httpd/logger.c 18 Nov 2014 15:02:55 -0000 @@ -194,6 +194,9 @@ logger_open(struct server *srv, struct s { struct log_file *log, *logfile = NULL, *errfile = NULL; + if (srv_conf->flags & SRVFLAG_SYSLOG) + return(0); + /* disassociate */ srv_conf->logaccess = srv_conf->logerror = NULL; Index: usr.sbin/httpd/parse.y =================================================================== RCS file: /cvs/src/usr.sbin/httpd/parse.y,v retrieving revision 1.34 diff -u -p -r1.34 parse.y --- usr.sbin/httpd/parse.y 6 Aug 2014 20:29:54 -0000 1.34 +++ usr.sbin/httpd/parse.y 18 Nov 2014 15:02:55 -0000 @@ -180,7 +180,7 @@ main : PREFORK NUMBER { break; if ($2 <= 0 || $2 > SERVER_MAXPROC) { yyerror("invalid number of preforked " - "servers: %d", $2); + "servers: %lld", $2); YYERROR; } conf->sc_prefork_server = $2; @@ -198,15 +198,6 @@ server : SERVER STRING { YYACCEPT; } - TAILQ_FOREACH(s, conf->sc_servers, srv_entry) - if (!strcmp(s->srv_conf.name, $2)) - break; - if (s != NULL) { - yyerror("server %s defined twice", $2); - free($2); - YYERROR; - } - if ((s = calloc(1, sizeof (*s))) == NULL) fatal("out of memory"); @@ -252,18 +243,46 @@ server : SERVER STRING { srv_conf = &srv->srv_conf; SPLAY_INIT(&srv->srv_clients); - TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry); } '{' optnl serveropts_l '}' { + struct server *s = NULL; + + TAILQ_FOREACH(s, conf->sc_servers, srv_entry) { + if ((s->srv_conf.flags & + SRVFLAG_LOCATION) == 0 && + strcmp(s->srv_conf.name, + srv->srv_conf.name) == 0 && + s->srv_conf.port == srv->srv_conf.port && + sockaddr_cmp( + (struct sockaddr *)&s->srv_conf.ss, + (struct sockaddr *)&srv->srv_conf.ss, + s->srv_conf.prefixlen) == 0) + break; + } + if (s != NULL) { + yyerror("server \"%s\" defined twice", + srv->srv_conf.name); + serverconfig_free(srv_conf); + free(srv); + YYABORT; + } + if (srv->srv_conf.ss.ss_family == AF_UNSPEC) { yyerror("listen address not specified"); - free($2); + serverconfig_free(srv_conf); + free(srv); YYERROR; } + if (server_ssl_load_keypair(srv) == -1) { yyerror("failed to load public/private keys " "for server %s", srv->srv_conf.name); + serverconfig_free(srv_conf); + free(srv); YYERROR; } + + TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry); + srv = NULL; srv_conf = NULL; } @@ -367,17 +386,6 @@ serveroptsl : LISTEN ON STRING optssl po YYACCEPT; } - TAILQ_FOREACH(s, conf->sc_servers, srv_entry) - if (strcmp(s->srv_conf.name, - srv->srv_conf.name) == 0 && - strcmp(s->srv_conf.location, $2) == 0) - break; - if (s != NULL) { - yyerror("location %s defined twice", $2); - free($2); - YYERROR; - } - if ((s = calloc(1, sizeof (*s))) == NULL) fatal("out of memory"); @@ -416,12 +424,31 @@ serveroptsl : LISTEN ON STRING optssl po srv = s; srv_conf = &srv->srv_conf; SPLAY_INIT(&srv->srv_clients); - TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry); } '{' optnl serveropts_l '}' { + struct server *s = NULL; + + TAILQ_FOREACH(s, conf->sc_servers, srv_entry) { + if ((s->srv_conf.flags & SRVFLAG_LOCATION) && + s->srv_conf.id == srv_conf->id && + strcmp(s->srv_conf.location, + srv_conf->location) == 0) + break; + } + if (s != NULL) { + yyerror("location \"%s\" defined twice", + srv->srv_conf.location); + serverconfig_free(srv_conf); + free(srv); + YYABORT; + } + + TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry); + srv = parentsrv; srv_conf = &parentsrv->srv_conf; parentsrv = NULL; } + | include ; fastcgi : NO FCGI { @@ -623,7 +650,7 @@ tcpflags : SACK { srv_conf->tcpflags | } | BACKLOG NUMBER { if ($2 < 0 || $2 > SERVER_MAX_CLIENTS) { - yyerror("invalid backlog: %d", $2); + yyerror("invalid backlog: %lld", $2); YYERROR; } srv_conf->tcpbacklog = $2; @@ -631,13 +658,13 @@ tcpflags : SACK { srv_conf->tcpflags | | SOCKET BUFFER NUMBER { srv_conf->tcpflags |= TCPFLAG_BUFSIZ; if ((srv_conf->tcpbufsiz = $3) < 0) { - yyerror("invalid socket buffer size: %d", $3); + yyerror("invalid socket buffer size: %lld", $3); YYERROR; } } | IP STRING NUMBER { if ($3 < 0) { - yyerror("invalid ttl: %d", $3); + yyerror("invalid ttl: %lld", $3); free($2); YYERROR; } @@ -694,6 +721,9 @@ medianamesl : STRING { } free($1); + if (!loadcfg) + break; + if (media_add(conf->sc_mediatypes, &media) == NULL) { yyerror("failed to add media type"); YYERROR; @@ -729,7 +759,7 @@ port : PORT STRING { } | PORT NUMBER [The entire original message is not included.]