Index: CHANGES =================================================================== RCS file: /cvsroot/src/libexec/httpd/CHANGES,v retrieving revision 1.19.2.1 diff -p -u -u -r1.19.2.1 CHANGES --- CHANGES 19 Apr 2015 04:44:03 -0000 1.19.2.1 +++ CHANGES 15 Apr 2016 08:15:12 -0000 @@ -1,5 +1,15 @@ $eterna: CHANGES,v 1.78 2011/11/18 01:25:11 mrg Exp $ +changes in bozohttpd 20151028: + o add CGI support for ~user translation (-E switch) + o add redirects to ~user translation + o fix bugs around ~user translation + o add schema detection for absolute redirects + o fixed few memory leaks + o bunch of minor tweaks + o removed -r support + o smarter redirects + changes in bozohttpd 20150320: o fix redirection handling o support transport stream (.ts) and video object (.vob) files Index: Makefile =================================================================== RCS file: /cvsroot/src/libexec/httpd/Makefile,v retrieving revision 1.22.2.1 diff -p -u -u -r1.22.2.1 Makefile --- Makefile 23 Apr 2015 19:38:11 -0000 1.22.2.1 +++ Makefile 15 Apr 2016 08:15:12 -0000 @@ -15,6 +15,10 @@ # DO_HTPASSWD /* support .htpasswd files */ # NO_LUA_SUPPORT /* don't support Lua for dynamic content */ # +# other system specific defines: +# HAVE_NBUTIL_H /* netbsd compat is in +# (don't forget to also enable -lnbutil) +# # these are usually set via the "COPTS" variable, or some other method # for setting CFLAGS relevant to your make, eg # % make COPTS="-DDO_HTPASSWD" @@ -33,6 +37,17 @@ DPADD= ${LIBCRYPT} ${LIBLUA} ${LIBM} WARNS?= 4 +.if defined(.OS.MAKE) +OPSYS= ${.OS.MAKE} +.else +OPSYS:= ${:!uname -s!:S/-//g:S/\///g} +.endif + +.if ${OPSYS} == "QNX" +CPPFLAGS+= -DHAVE_NBUTIL_H +LDADD+= -lnbutil +.endif + .include .if ${MKCRYPTO} != "no" Index: auth-bozo.c =================================================================== RCS file: /cvsroot/src/libexec/httpd/auth-bozo.c,v retrieving revision 1.13.2.1 diff -p -u -u -r1.13.2.1 auth-bozo.c --- auth-bozo.c 12 Jan 2015 10:02:29 -0000 1.13.2.1 +++ auth-bozo.c 15 Apr 2016 08:15:12 -0000 @@ -72,10 +72,10 @@ bozo_auth_check(bozo_httpreq_t *request, if (bozo_check_special_files(request, basename)) return 1; } - request->hr_authrealm = bozostrdup(httpd, dir); + request->hr_authrealm = bozostrdup(httpd, request, dir); - if ((size_t)snprintf(authfile, sizeof(authfile), "%s/%s", dir, AUTH_FILE) >= - sizeof(authfile)) { + if ((size_t)snprintf(authfile, sizeof(authfile), "%s/%s", dir, + AUTH_FILE) >= sizeof(authfile)) { return bozo_http_error(httpd, 404, request, "authfile path too long"); } @@ -136,7 +136,8 @@ bozo_auth_cleanup(bozo_httpreq_t *reques } int -bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str, ssize_t len) +bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str, + ssize_t len) { bozohttpd_t *httpd = request->hr_httpd; @@ -159,8 +160,8 @@ bozo_auth_check_headers(bozo_httpreq_t * *pass++ = '\0'; free(request->hr_authuser); free(request->hr_authpass); - request->hr_authuser = bozostrdup(httpd, authbuf); - request->hr_authpass = bozostrdup(httpd, pass); + request->hr_authuser = bozostrdup(httpd, request, authbuf); + request->hr_authpass = bozostrdup(httpd, request, pass); debug((httpd, DEBUG_FAT, "decoded authorization `%s' as `%s':`%s'", str, request->hr_authuser, request->hr_authpass)); @@ -190,8 +191,8 @@ bozo_auth_check_401(bozo_httpreq_t *requ if (code == 401) bozo_printf(httpd, "WWW-Authenticate: Basic realm=\"%s\"\r\n", - (request && request->hr_authrealm) ? - request->hr_authrealm : "default realm"); + request->hr_authrealm ? + request->hr_authrealm : "default realm"); } #ifndef NO_CGIBIN_SUPPORT Index: bozohttpd.8 =================================================================== RCS file: /cvsroot/src/libexec/httpd/bozohttpd.8,v retrieving revision 1.46.4.4 diff -p -u -u -r1.46.4.4 bozohttpd.8 --- bozohttpd.8 9 May 2015 08:50:42 -0000 1.46.4.4 +++ bozohttpd.8 15 Apr 2016 08:15:12 -0000 @@ -26,7 +26,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 1, 2015 +.Dd December 12, 2015 .Dt BOZOHTTPD 8 .Os .Sh NAME @@ -34,13 +34,14 @@ .Nd hyper text transfer protocol version 1.1 daemon .Sh SYNOPSIS .Nm -.Op Fl CIMPSZciptvx +.Op Fl EHVXefhnsu .Op Fl C Ar suffix cgihandler .Op Fl I Ar port .Op Fl L Ar prefix script .Op Fl M Ar suffix type encoding encoding11 .Op Fl P Ar pidfile .Op Fl S Ar server_software +.Op Fl U Ar username .Op Fl Z Ar cert privkey .Op Fl c Ar cgibin .Op Fl i Ar address @@ -48,6 +49,7 @@ .Op Fl t Ar chrootdir .Op Fl v Ar virtualroot .Op Fl x Ar index +.Op Fl z Ar ciphers .Ar slashdir .Op Ar myname .Sh DESCRIPTION @@ -111,9 +113,11 @@ is a valid CGI program in the directory. In other words, all CGI URL's must begin with .Em \%/cgi-bin/ . -Note that the CGI/1.1 interface is not available with +Note that the CGI/1.1 interface is available with .Em ~user -translation. +translation using +.Fl E +switch. .It Fl e Causes .Nm @@ -223,15 +227,6 @@ translations from .Dq public_html to .Ar pubdir . -.It Fl r -Forces pages besides the -.Dq index.html -(see the -.Fl X -option) page to require that the Referrer: header be present and -refer to this web server, otherwise a redirect to the -.Dq index.html -page will be returned instead. .It Fl S Ar server_software Sets the internal server version to .Ar server_software . @@ -270,6 +265,12 @@ into the directory (but see the .Fl p option above). +.It Fl E +Enables CGI/1.1 interface for +.Em ~user +translation. +Note that enabling this support implies that users can run +commands as web server user, this may have security implications. .It Fl V Sets the default virtual host directory to .Ar slashdir . @@ -303,6 +304,9 @@ Changes the default file read for direct .Dq index.html to .Ar index . +.It Fl z Ar ciphers +Sets the list of SSL ciphers (see +.Xr SSL_CTX_set_cipher_list 3 ) . .It Fl Z Ar certificate_path privatekey_path Sets the path to the server certificate file and the private key file in pem format. @@ -333,7 +337,8 @@ by default to process incoming TCP conne option), .Nm has little internal networking knowledge. -(Indeed, you can run it on the command line with little change of functionality.) +(Indeed, you can run it on the command line with little change of +functionality.) A typical .Xr inetd.conf 5 entry would be: @@ -425,7 +430,7 @@ It may require linking with the crypt li .Dq -lcrypt . .Ss SSL SUPPORT .Nm -has support for SSLv2, SSLv3, and TLSv1 protocols that is included by +has support for TLSv1.1 and TLSv1.2 protocols that are included by default. It requires linking with the crypto and ssl library, using .Dq -lcrypto -lssl . @@ -462,12 +467,23 @@ symbolic link is found, .Nm will perform a smart redirect to the target of this symlink. The target is assumed to live on the same server. +If target starts with slash then absolute redirection is performed, +otherwise it's handled as relative. If a .Pa .bzabsredirect symbolic link is found, .Nm will redirect to the absolute url pointed to by this symlink. This is useful to redirect to different servers. +Two forms of redirection are supported - symbolic link without schema will use +.Em http:// +as default i.e. link to +.Em NetBSD.org +will redirect to +.Em http://NetBSD.org/ +Otherwise provided schema will be used i.e. symbolic link to +.Em ftp://NetBSD.org/ +will redirect to provided the URL. .Sh EXAMPLES To configure set of virtual hosts, one would use an .Xr inetd.conf 5 @@ -597,8 +613,9 @@ provided many fixes and enhancements for .It .An Mateusz Kocielski .Aq Mt shm@NetBSD.org -fixed memory leaks, information disclosure issues and added support -for using CGI handlers with directory indexing. +fixed memory leaks, various issues with userdir support, +information disclosure issues, added support for using CGI handlers +with directory indexing and provided various other fixes. .It .An Arnaud Lacombe .Aq Mt alc@NetBSD.org @@ -612,7 +629,7 @@ provided man page fixes .Aq Mt jmmv@NetBSD.org Added the .Fl P -option. +option (pidfile support) and provided some man page fixes. .It .An Luke Mewburn .Aq Mt lukem@NetBSD.org @@ -665,9 +682,6 @@ provided http authorisation fixes provided chroot and change-to-user support, and other various fixes .It Coyote Point provided various CGI fixes. -.It -.An Julio Merino -added pidfile support and provided some man page fixes. .El .Pp There are probably others I have forgotten (let me know if you care) Index: bozohttpd.c =================================================================== RCS file: /cvsroot/src/libexec/httpd/bozohttpd.c,v retrieving revision 1.56.2.4 diff -p -u -u -r1.56.2.4 bozohttpd.c --- bozohttpd.c 9 May 2015 08:50:42 -0000 1.56.2.4 +++ bozohttpd.c 15 Apr 2016 08:15:12 -0000 @@ -109,7 +109,7 @@ #define INDEX_HTML "index.html" #endif #ifndef SERVER_SOFTWARE -#define SERVER_SOFTWARE "bozohttpd/20150501" +#define SERVER_SOFTWARE "bozohttpd/20151231" #endif #ifndef DIRECT_ACCESS_FILE #define DIRECT_ACCESS_FILE ".bzdirect" @@ -171,61 +171,59 @@ volatile sig_atomic_t alarmhit; * check there's enough space in the prefs and names arrays. */ static int -size_arrays(bozoprefs_t *bozoprefs, unsigned needed) +size_arrays(bozoprefs_t *bozoprefs, size_t needed) { char **temp; if (bozoprefs->size == 0) { /* only get here first time around */ - bozoprefs->size = needed; - if ((bozoprefs->name = calloc(sizeof(char *), needed)) == NULL) { - (void) fprintf(stderr, "size_arrays: bad alloc\n"); + bozoprefs->name = calloc(sizeof(char *), needed); + if (bozoprefs->name == NULL) return 0; - } - if ((bozoprefs->value = calloc(sizeof(char *), needed)) == NULL) { + bozoprefs->value = calloc(sizeof(char *), needed); + if (bozoprefs->value == NULL) { free(bozoprefs->name); - (void) fprintf(stderr, "size_arrays: bad alloc\n"); return 0; } - } else if (bozoprefs->c == bozoprefs->size) { + bozoprefs->size = needed; + } else if (bozoprefs->count == bozoprefs->size) { /* only uses 'needed' when filled array */ - bozoprefs->size += needed; temp = realloc(bozoprefs->name, sizeof(char *) * needed); - if (temp == NULL) { - (void) fprintf(stderr, "size_arrays: bad alloc\n"); + if (temp == NULL) return 0; - } bozoprefs->name = temp; temp = realloc(bozoprefs->value, sizeof(char *) * needed); - if (temp == NULL) { - (void) fprintf(stderr, "size_arrays: bad alloc\n"); + if (temp == NULL) return 0; - } bozoprefs->value = temp; + bozoprefs->size += needed; } return 1; } -static int +static ssize_t findvar(bozoprefs_t *bozoprefs, const char *name) { - unsigned i; + size_t i; - for (i = 0 ; i < bozoprefs->c && strcmp(bozoprefs->name[i], name) != 0; i++) - ; - return (i == bozoprefs->c) ? -1 : (int)i; + for (i = 0; i < bozoprefs->count; i++) + if (strcmp(bozoprefs->name[i], name) == 0) + return (ssize_t)i; + return -1; } int -bozo_set_pref(bozoprefs_t *bozoprefs, const char *name, const char *value) +bozo_set_pref(bozohttpd_t *httpd, bozoprefs_t *bozoprefs, + const char *name, const char *value) { - int i; + ssize_t i; if ((i = findvar(bozoprefs, name)) < 0) { /* add the element to the array */ - if (size_arrays(bozoprefs, bozoprefs->size + 15)) { - bozoprefs->name[i = bozoprefs->c++] = strdup(name); - } + if (!size_arrays(bozoprefs, bozoprefs->size + 15)) + return 0; + i = bozoprefs->count++; + bozoprefs->name[i] = bozostrdup(httpd, NULL, name); } else { /* replace the element in the array */ if (bozoprefs->value[i]) { @@ -233,8 +231,7 @@ bozo_set_pref(bozoprefs_t *bozoprefs, co bozoprefs->value[i] = NULL; } } - /* sanity checks for range of values go here */ - bozoprefs->value[i] = strdup(value); + bozoprefs->value[i] = bozostrdup(httpd, NULL, value); return 1; } @@ -244,10 +241,10 @@ bozo_set_pref(bozoprefs_t *bozoprefs, co char * bozo_get_pref(bozoprefs_t *bozoprefs, const char *name) { - int i; + ssize_t i; - return ((i = findvar(bozoprefs, name)) < 0) ? NULL : - bozoprefs->value[i]; + i = findvar(bozoprefs, name); + return i < 0 ? NULL : bozoprefs->value[i]; } char * @@ -309,9 +306,9 @@ parse_request(bozohttpd_t *httpd, char * } /* allocate private copies */ - *file = bozostrdup(httpd, *file); + *file = bozostrdup(httpd, NULL, *file); if (*query) - *query = bozostrdup(httpd, *query); + *query = bozostrdup(httpd, NULL, *query); debug((httpd, DEBUG_FAT, "url: method: \"%s\" file: \"%s\" query: \"%s\" proto: \"%s\"", @@ -341,6 +338,7 @@ bozo_clean_request(bozo_httpreq_t *reque free(request->hr_oldfile); free(request->hr_query); free(request->hr_host); + bozo_user_free(request->hr_user); bozo_auth_cleanup(request); for (hdr = SIMPLEQ_FIRST(&request->hr_headers); hdr; hdr = SIMPLEQ_NEXT(hdr, h_next)) { @@ -350,6 +348,15 @@ bozo_clean_request(bozo_httpreq_t *reque ohdr = hdr; } free(ohdr); + ohdr = NULL; + for (hdr = SIMPLEQ_FIRST(&request->hr_replheaders); hdr; + hdr = SIMPLEQ_NEXT(hdr, h_next)) { + free(hdr->h_value); + free(hdr->h_header); + free(ohdr); + ohdr = hdr; + } + free(ohdr); free(request); } @@ -365,49 +372,76 @@ alarmer(int sig) } /* + * a list of header quirks: currently, a list of headers that + * can't be folded into a single line. + */ +const char *header_quirks[] = { "WWW-Authenticate", NULL }; + +/* * add or merge this header (val: str) into the requests list */ static bozoheaders_t * -addmerge_header(bozo_httpreq_t *request, char *val, - char *str, ssize_t len) +addmerge_header(bozo_httpreq_t *request, struct qheaders *headers, + const char *val, const char *str, ssize_t len) { - struct bozoheaders *hdr; + struct bozohttpd_t *httpd = request->hr_httpd; + struct bozoheaders *hdr = NULL; + const char **quirk; USE_ARG(len); - /* do we exist already? */ - SIMPLEQ_FOREACH(hdr, &request->hr_headers, h_next) { - if (strcasecmp(val, hdr->h_header) == 0) + for (quirk = header_quirks; *quirk; quirk++) + if (strcasecmp(*quirk, val) == 0) break; + + if (*quirk == NULL) { + /* do we exist already? */ + SIMPLEQ_FOREACH(hdr, headers, h_next) { + if (strcasecmp(val, hdr->h_header) == 0) + break; + } } if (hdr) { /* yup, merge it in */ char *nval; - if (asprintf(&nval, "%s, %s", hdr->h_value, str) == -1) { - (void)bozo_http_error(request->hr_httpd, 500, NULL, - "memory allocation failure"); - return NULL; - } + bozoasprintf(httpd, &nval, "%s, %s", hdr->h_value, str); free(hdr->h_value); hdr->h_value = nval; } else { /* nope, create a new one */ - hdr = bozomalloc(request->hr_httpd, sizeof *hdr); - hdr->h_header = bozostrdup(request->hr_httpd, val); + hdr = bozomalloc(httpd, sizeof *hdr); + hdr->h_header = bozostrdup(httpd, request, val); if (str && *str) - hdr->h_value = bozostrdup(request->hr_httpd, str); + hdr->h_value = bozostrdup(httpd, request, str); else - hdr->h_value = bozostrdup(request->hr_httpd, " "); + hdr->h_value = bozostrdup(httpd, request, " "); - SIMPLEQ_INSERT_TAIL(&request->hr_headers, hdr, h_next); + SIMPLEQ_INSERT_TAIL(headers, hdr, h_next); request->hr_nheaders++; } return hdr; } +bozoheaders_t * +addmerge_reqheader(bozo_httpreq_t *request, const char *val, const char *str, + ssize_t len) +{ + + return addmerge_header(request, &request->hr_headers, val, str, len); +} + +bozoheaders_t * +addmerge_replheader(bozo_httpreq_t *request, const char *val, const char *str, + ssize_t len) +{ + + return addmerge_header(request, &request->hr_replheaders, + val, str, len); +} + /* * as the prototype string is not constant (eg, "HTTP/1.1" is equivalent * to "HTTP/001.01"), we MUST parse this. @@ -415,13 +449,14 @@ addmerge_header(bozo_httpreq_t *request, static int process_proto(bozo_httpreq_t *request, const char *proto) { + struct bozohttpd_t *httpd = request->hr_httpd; char majorstr[16], *minorstr; int majorint, minorint; if (proto == NULL) { got_proto_09: - request->hr_proto = request->hr_httpd->consts.http_09; - debug((request->hr_httpd, DEBUG_FAT, "request %s is http/0.9", + request->hr_proto = httpd->consts.http_09; + debug((httpd, DEBUG_FAT, "request %s is http/0.9", request->hr_file)); return 0; } @@ -445,25 +480,25 @@ got_proto_09: goto got_proto_09; case 1: if (minorint == 0) - request->hr_proto = request->hr_httpd->consts.http_10; + request->hr_proto = httpd->consts.http_10; else if (minorint == 1) - request->hr_proto = request->hr_httpd->consts.http_11; + request->hr_proto = httpd->consts.http_11; else break; - debug((request->hr_httpd, DEBUG_FAT, "request %s is %s", + debug((httpd, DEBUG_FAT, "request %s is %s", request->hr_file, request->hr_proto)); SIMPLEQ_INIT(&request->hr_headers); request->hr_nheaders = 0; return 0; } bad: - return bozo_http_error(request->hr_httpd, 404, NULL, "unknown prototype"); + return bozo_http_error(httpd, 404, NULL, "unknown prototype"); } /* * process each type of HTTP method, setting this HTTP requests - # method type. + * method type. */ static struct method_map { const char *name; @@ -485,9 +520,10 @@ static struct method_map { static int process_method(bozo_httpreq_t *request, const char *method) { + struct bozohttpd_t *httpd = request->hr_httpd; struct method_map *mmp; - if (request->hr_proto == request->hr_httpd->consts.http_11) + if (request->hr_proto == httpd->consts.http_11) request->hr_allow = "GET, HEAD, POST"; for (mmp = method_map; mmp->name; mmp++) @@ -497,7 +533,7 @@ process_method(bozo_httpreq_t *request, return 0; } - return bozo_http_error(request->hr_httpd, 404, request, "unknown method"); + return bozo_http_error(httpd, 404, request, "unknown method"); } /* @@ -541,6 +577,7 @@ bozo_read_request(bozohttpd_t *httpd) request->hr_virthostname = NULL; request->hr_file = NULL; request->hr_oldfile = NULL; + SIMPLEQ_INIT(&request->hr_replheaders); bozo_auth_init(request); slen = sizeof(ss); @@ -560,9 +597,9 @@ bozo_read_request(bozohttpd_t *httpd) host = NULL; } if (host != NULL) - request->hr_remotehost = bozostrdup(request->hr_httpd, host); + request->hr_remotehost = bozostrdup(httpd, request, host); if (addr != NULL) - request->hr_remoteaddr = bozostrdup(request->hr_httpd, addr); + request->hr_remoteaddr = bozostrdup(httpd, request, addr); slen = sizeof(ss); /* @@ -578,15 +615,16 @@ bozo_read_request(bozohttpd_t *httpd) if (getsockname(0, (struct sockaddr *)(void *)&ss, &slen) < 0) port = NULL; else { - if (getnameinfo((struct sockaddr *)(void *)&ss, slen, NULL, 0, - bufport, sizeof bufport, NI_NUMERICSERV) == 0) + if (getnameinfo((struct sockaddr *)(void *)&ss, slen, + NULL, 0, bufport, sizeof bufport, + NI_NUMERICSERV) == 0) port = bufport; else port = NULL; } } if (port != NULL) - request->hr_serverport = bozostrdup(request->hr_httpd, port); + request->hr_serverport = bozostrdup(httpd, request, port); /* * setup a timer to make sure the request is not hung @@ -595,7 +633,7 @@ bozo_read_request(bozohttpd_t *httpd) sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGALRM); sa.sa_flags = 0; - sigaction(SIGALRM, &sa, NULL); /* XXX */ + sigaction(SIGALRM, &sa, NULL); alarm(MAX_WAIT_TIME); while ((str = bozodgetln(httpd, STDIN_FILENO, &len, bozo_read)) != NULL) { @@ -614,11 +652,11 @@ bozo_read_request(bozohttpd_t *httpd) "null method"); goto cleanup; } - - bozo_warn(httpd, "got request ``%s'' from host %s to port %s", - str, - host ? host : addr ? addr : "", - port ? port : ""); + bozowarn(httpd, + "got request ``%s'' from host %s to port %s", + str, + host ? host : addr ? addr : "", + port ? port : ""); /* we allocate return space in file and query only */ parse_request(httpd, str, &method, &file, &query, &proto); @@ -675,14 +713,15 @@ bozo_read_request(bozohttpd_t *httpd) if (bozo_auth_check_headers(request, val, str, len)) goto next_header; - hdr = addmerge_header(request, val, str, len); + hdr = addmerge_reqheader(request, val, str, len); if (strcasecmp(hdr->h_header, "content-type") == 0) request->hr_content_type = hdr->h_value; else if (strcasecmp(hdr->h_header, "content-length") == 0) request->hr_content_length = hdr->h_value; else if (strcasecmp(hdr->h_header, "host") == 0) - request->hr_host = bozostrdup(httpd, hdr->h_value); + request->hr_host = bozostrdup(httpd, request, + hdr->h_value); /* RFC 2616 (HTTP/1.1): 14.20 */ else if (strcasecmp(hdr->h_header, "expect") == 0) { (void)bozo_http_error(httpd, 417, request, @@ -797,7 +836,7 @@ mmap_and_write_part(bozohttpd_t *httpd, addr = mmap(0, mappedsz, PROT_READ, MAP_SHARED, fd, mappedoffset); if (addr == (char *)-1) { - bozo_warn(httpd, "mmap failed: %s", strerror(errno)); + bozowarn(httpd, "mmap failed: %s", strerror(errno)); return -1; } mappedaddr = addr; @@ -808,7 +847,7 @@ mmap_and_write_part(bozohttpd_t *httpd, while (sz > BOZO_WRSZ) { if (bozo_write(httpd, STDOUT_FILENO, addr + wroffset, BOZO_WRSZ) != BOZO_WRSZ) { - bozo_warn(httpd, "write failed: %s", strerror(errno)); + bozowarn(httpd, "write failed: %s", strerror(errno)); goto out; } debug((httpd, DEBUG_OBESE, "wrote %d bytes", BOZO_WRSZ)); @@ -817,13 +856,13 @@ mmap_and_write_part(bozohttpd_t *httpd, } if (sz && (size_t)bozo_write(httpd, STDOUT_FILENO, addr + wroffset, sz) != sz) { - bozo_warn(httpd, "final write failed: %s", strerror(errno)); + bozowarn(httpd, "final write failed: %s", strerror(errno)); goto out; } debug((httpd, DEBUG_OBESE, "wrote %d bytes", (int)sz)); out: if (munmap(mappedaddr, mappedsz) < 0) { - bozo_warn(httpd, "munmap failed"); + bozowarn(httpd, "munmap failed"); return -1; } @@ -851,10 +890,11 @@ parse_http_date(const char *val, time_t /* * given an url, encode it ala rfc 3986. ie, escape ? and friends. * note that this function returns a static buffer, and thus needs - * to be updated for any sort of parallel processing. + * to be updated for any sort of parallel processing. escape only + * chosen characters for absolute redirects */ char * -bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url) +bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url, int absolute) { static char *buf; static size_t buflen = 0; @@ -868,11 +908,6 @@ bozo_escape_rfc3986(bozohttpd_t *httpd, buf = bozorealloc(httpd, buf, buflen); } - if (url == NULL) { - buf[0] = 0; - return buf; - } - for (len = 0, s = url, d = buf; *s;) { if (*s & 0x80) goto encode_it; @@ -895,11 +930,18 @@ bozo_escape_rfc3986(bozohttpd_t *httpd, case ';': case '=': case '%': + case '"': + if (absolute) + goto leave_it; + case '\n': + case '\r': + case ' ': encode_it: - snprintf(d, 4, "%%%2X", *s++); + snprintf(d, 4, "%%%02X", *s++); d += 3; len += 3; break; + leave_it: default: *d++ = *s++; len++; @@ -912,115 +954,118 @@ bozo_escape_rfc3986(bozohttpd_t *httpd, } /* - * checks to see if this request has a valid .bzdirect file. returns - * 0 on failure and 1 on success. - */ -static int -check_direct_access(bozo_httpreq_t *request) -{ - FILE *fp; - struct stat sb; - char dir[MAXPATHLEN], dirfile[MAXPATHLEN], *basename; - - snprintf(dir, sizeof(dir), "%s", request->hr_file + 1); - debug((request->hr_httpd, DEBUG_FAT, "check_direct_access: dir %s", dir)); - basename = strrchr(dir, '/'); - - if ((!basename || basename[1] != '\0') && - lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) - /* nothing */; - else if (basename == NULL) - strcpy(dir, "."); - else { - *basename++ = '\0'; - bozo_check_special_files(request, basename); - } - - if ((size_t)snprintf(dirfile, sizeof(dirfile), "%s/%s", dir, - DIRECT_ACCESS_FILE) >= sizeof(dirfile)) { - bozo_http_error(request->hr_httpd, 404, request, - "directfile path too long"); - return 0; - } - if (stat(dirfile, &sb) < 0 || - (fp = fopen(dirfile, "r")) == NULL) - return 0; - fclose(fp); - return 1; -} - -/* - * do automatic redirection -- if there are query parameters for the URL - * we will tack these on to the new (redirected) URL. + * do automatic redirection -- if there are query parameters or userdir for + * the URL we will tack these on to the new (redirected) URL. */ static void -handle_redirect(bozo_httpreq_t *request, - const char *url, int absolute) +handle_redirect(bozo_httpreq_t *request, const char *url, int absolute) { bozohttpd_t *httpd = request->hr_httpd; - char *urlbuf; + char *finalurl, *urlbuf; +#ifndef NO_USER_SUPPORT + char *userbuf; +#endif /* !NO_USER_SUPPORT */ char portbuf[20]; + const char *scheme, *query, *quest; const char *hostname = BOZOHOST(httpd, request); - int query = 0; + int absproto = 0; /* absolute redirect provides own schema */ if (url == NULL) { - if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0) - bozo_err(httpd, 1, "asprintf"); + bozoasprintf(httpd, &urlbuf, "/%s/", request->hr_file); url = urlbuf; } else urlbuf = NULL; - url = bozo_escape_rfc3986(request->hr_httpd, url); - if (request->hr_query && strlen(request->hr_query)) - query = 1; +#ifndef NO_USER_SUPPORT + if (request->hr_user && !absolute) { + bozoasprintf(httpd, &userbuf, "/~%s%s", request->hr_user, url); + url = userbuf; + } else + userbuf = NULL; +#endif /* !NO_USER_SUPPORT */ - if (request->hr_serverport && strcmp(request->hr_serverport, "80") != 0) - snprintf(portbuf, sizeof(portbuf), ":%s", - request->hr_serverport); - else + if (absolute) { + char *sep = NULL; + const char *s; + + /* + * absolute redirect may specify own protocol i.e. to redirect + * to another schema like https:// or ftp://. + * Details: RFC 3986, section 3. + */ + + /* 1. check if url contains :// */ + sep = strstr(url, "://"); + + /* + * RFC 3986, section 3.1: + * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + */ + if (sep) { + for (s = url; s != sep;) { + if (!isalnum((int)*s) && + *s != '+' && *s != '-' && *s != '.') + break; + if (++s == sep) { + absproto = 1; + } + } + } + } + + /* construct final redirection url */ + + scheme = absproto ? "" : httpd->sslinfo ? "https://" : "http://"; + + if (absolute) { + hostname = ""; portbuf[0] = '\0'; - if (absolute) - bozo_warn(httpd, "redirecting %s", url); - else - bozo_warn(httpd, "redirecting %s%s%s", hostname, portbuf, url); - debug((httpd, DEBUG_FAT, "redirecting %s", url)); + } else { + const char *defport = httpd->sslinfo ? "443" : "80"; + + if (request->hr_serverport && + strcmp(request->hr_serverport, defport) != 0) + snprintf(portbuf, sizeof(portbuf), ":%s", + request->hr_serverport); + else + portbuf[0] = '\0'; + } + + url = bozo_escape_rfc3986(httpd, url, absolute); + + if (request->hr_query && strlen(request->hr_query)) { + query = request->hr_query; + quest = "?"; + } else { + query = quest = ""; + } + + bozoasprintf(httpd, &finalurl, "%s%s%s%s%s%s", + scheme, hostname, portbuf, url, quest, query); + + bozowarn(httpd, "redirecting %s", finalurl); + debug((httpd, DEBUG_FAT, "redirecting %s", finalurl)); + bozo_printf(httpd, "%s 301 Document Moved\r\n", request->hr_proto); if (request->hr_proto != httpd->consts.http_09) bozo_print_header(request, NULL, "text/html", NULL); - if (request->hr_proto != httpd->consts.http_09) { - bozo_printf(httpd, "Location: http://"); - if (absolute == 0) - bozo_printf(httpd, "%s%s", hostname, portbuf); - if (query) { - bozo_printf(httpd, "%s?%s\r\n", url, request->hr_query); - } else { - bozo_printf(httpd, "%s\r\n", url); - } - } + if (request->hr_proto != httpd->consts.http_09) + bozo_printf(httpd, "Location: %s\r\n", finalurl); bozo_printf(httpd, "\r\n"); if (request->hr_method == HTTP_HEAD) goto head; bozo_printf(httpd, "Document Moved\n"); bozo_printf(httpd, "

Document Moved

\n"); - bozo_printf(httpd, "This document had moved hr_query); - else - bozo_printf(httpd, "%s%s%s?%s", hostname, - portbuf, url, request->hr_query); - } else { - if (absolute) - bozo_printf(httpd, "%s", url); - else - bozo_printf(httpd, "%s%s%s", hostname, - portbuf, url); - } - bozo_printf(httpd, "\">here\n"); + bozo_printf(httpd, "This document had moved here\n", + finalurl); bozo_printf(httpd, "\n"); head: bozo_flush(httpd, stdout); free(urlbuf); + free(finalurl); +#ifndef NO_USER_SUPPORT + free(userbuf); +#endif /* !NO_USER_SUPPORT */ } /* @@ -1037,9 +1082,6 @@ check_virtual(bozo_httpreq_t *request) char *file = request->hr_file, *s; size_t len; - if (!httpd->virtbase) - goto use_slashdir; - /* * convert http://virtual.host/ to request->hr_host */ @@ -1050,12 +1092,12 @@ check_virtual(bozo_httpreq_t *request) file += 7; /* RFC 2616 (HTTP/1.1), 5.2: URI takes precedence over Host: */ free(request->hr_host); - request->hr_host = bozostrdup(request->hr_httpd, file); + request->hr_host = bozostrdup(httpd, request, file); if ((s = strchr(request->hr_host, '/')) != NULL) *s = '\0'; s = strchr(file, '/'); free(request->hr_file); - request->hr_file = bozostrdup(request->hr_httpd, s ? s : "/"); + request->hr_file = bozostrdup(httpd, request, s ? s : "/"); debug((httpd, DEBUG_OBESE, "got host ``%s'' file is now ``%s''", request->hr_host, request->hr_file)); } else if (!request->hr_host) @@ -1069,6 +1111,29 @@ check_virtual(bozo_httpreq_t *request) request->hr_host[len - 3] = '\0'; len = strlen(request->hr_host); } + + if (!httpd->virtbase) { + + /* + * if we don't use vhost support, then set virthostname if + * user supplied Host header. It will be used for possible + * redirections + */ + + if (request->hr_host) { + s = strrchr(request->hr_host, ':'); + if (s != NULL) + /* truncate Host: as we want to copy it without port part */ + *s = '\0'; + request->hr_virthostname = bozostrdup(httpd, request, + request->hr_host); + if (s != NULL) + /* fix Host: again, if we truncated it */ + *s = ':'; + } + + goto use_slashdir; + } /* * ok, we have a virtual host, use opendir(3) to find a case @@ -1093,15 +1158,14 @@ check_virtual(bozo_httpreq_t *request) } debug((httpd, DEBUG_OBESE, "looking at dir``%s''", d->d_name)); - if (d->d_namlen == len && strcmp(d->d_name, - request->hr_host) == 0) { + if (strcmp(d->d_name, request->hr_host) == 0) { /* found it, punch it */ debug((httpd, DEBUG_OBESE, "found it punch it")); request->hr_virthostname = - bozostrdup(httpd, d->d_name); - if (asprintf(&s, "%s/%s", httpd->virtbase, - request->hr_virthostname) < 0) - bozo_err(httpd, 1, "asprintf"); + bozostrdup(httpd, request, d->d_name); + bozoasprintf(httpd, &s, "%s/%s", + httpd->virtbase, + request->hr_virthostname); break; } } @@ -1138,6 +1202,7 @@ use_slashdir: static int check_bzredirect(bozo_httpreq_t *request) { + bozohttpd_t *httpd = request->hr_httpd; struct stat sb; char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1], path[MAXPATHLEN]; @@ -1150,27 +1215,31 @@ check_bzredirect(bozo_httpreq_t *request */ if((size_t)snprintf(dir, sizeof(dir), "%s", request->hr_file + 1) >= sizeof(dir)) { - bozo_http_error(request->hr_httpd, 404, request, + bozo_http_error(httpd, 404, request, "file path too long"); return -1; } - debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir)); + debug((httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir)); basename = strrchr(dir, '/'); if ((!basename || basename[1] != '\0') && - lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) - /* nothing */; - else if (basename == NULL) - strcpy(dir, "."); - else { + lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) { + strcpy(path, dir); + } else if (basename == NULL) { + strcpy(path, "."); + strcpy(dir, ""); + } else { *basename++ = '\0'; bozo_check_special_files(request, basename); + strcpy(path, dir); } - if ((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir, + debug((httpd, DEBUG_FAT, "check_bzredirect: path %s", path)); + + if ((size_t)snprintf(redir, sizeof(redir), "%s/%s", path, REDIRECT_FILE) >= sizeof(redir)) { - bozo_http_error(request->hr_httpd, 404, request, - "redirectfile path too long"); + bozo_http_error(httpd, 404, request, + "redirectfile path too long"); return -1; } if (lstat(redir, &sb) == 0) { @@ -1178,9 +1247,9 @@ check_bzredirect(bozo_httpreq_t *request return 0; absolute = 0; } else { - if((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir, + if((size_t)snprintf(redir, sizeof(redir), "%s/%s", path, ABSREDIRECT_FILE) >= sizeof(redir)) { - bozo_http_error(request->hr_httpd, 404, request, + bozo_http_error(httpd, 404, request, "redirectfile path too long"); return -1; } @@ -1188,16 +1257,14 @@ check_bzredirect(bozo_httpreq_t *request return 0; absolute = 1; } - debug((request->hr_httpd, DEBUG_FAT, - "check_bzredirect: calling readlink")); + debug((httpd, DEBUG_FAT, "check_bzredirect: calling readlink")); rv = readlink(redir, redirpath, sizeof redirpath - 1); if (rv == -1 || rv == 0) { - debug((request->hr_httpd, DEBUG_FAT, "readlink failed")); + debug((httpd, DEBUG_FAT, "readlink failed")); return 0; } redirpath[rv] = '\0'; - debug((request->hr_httpd, DEBUG_FAT, - "readlink returned \"%s\"", redirpath)); + debug((httpd, DEBUG_FAT, "readlink returned \"%s\"", redirpath)); /* check if we need authentication */ snprintf(path, sizeof(path), "%s/", dir); @@ -1205,19 +1272,17 @@ check_bzredirect(bozo_httpreq_t *request return 1; /* now we have the link pointer, redirect to the real place */ - if (absolute) - finalredir = redirpath; - else { - if ((size_t)snprintf(finalredir = redir, sizeof(redir), "/%s/%s", - dir, redirpath) >= sizeof(redir)) { - bozo_http_error(request->hr_httpd, 404, request, + if (!absolute && redirpath[0] != '/') { + if ((size_t)snprintf(finalredir = redir, sizeof(redir), "%s%s/%s", + (strlen(dir) > 0 ? "/" : ""), dir, redirpath) >= sizeof(redir)) { + bozo_http_error(httpd, 404, request, "redirect path too long"); return -1; } - } + } else + finalredir = redirpath; - debug((request->hr_httpd, DEBUG_FAT, - "check_bzredirect: new redir %s", finalredir)); + debug((httpd, DEBUG_FAT, "check_bzredirect: new redir %s", finalredir)); handle_redirect(request, finalredir, absolute); return 1; } @@ -1297,7 +1362,6 @@ fix_url_percent(bozo_httpreq_t *request) * transform_request does this: * - ``expand'' %20 crapola * - punt if it doesn't start with / - * - check httpd->untrustedref / referrer * - look for "http://myname/" and deal with it. * - maybe call bozo_process_cgi() * - check for ~user and call bozo_user_transform() if so @@ -1315,7 +1379,6 @@ transform_request(bozo_httpreq_t *reques bozohttpd_t *httpd = request->hr_httpd; char *file, *newfile = NULL; size_t len; - const char *hostname = BOZOHOST(httpd, request); file = NULL; *isindex = 0; @@ -1333,84 +1396,49 @@ transform_request(bozo_httpreq_t *reques goto bad_done; } - switch(check_bzredirect(request)) { - case -1: - goto bad_done; - case 1: - return 0; - } - - if (httpd->untrustedref) { - int to_indexhtml = 0; + /* omit additional slashes at the beginning */ + while (file[1] == '/') + file++; -#define TOP_PAGE(x) (strcmp((x), "/") == 0 || \ - strcmp((x) + 1, httpd->index_html) == 0 || \ - strcmp((x) + 1, "favicon.ico") == 0) - - debug((httpd, DEBUG_EXPLODING, "checking httpd->untrustedref")); - /* - * first check that this path isn't allowed via .bzdirect file, - * and then check referrer; make sure that people come via the - * real name... otherwise if we aren't looking at / or - * /index.html, redirect... we also special case favicon.ico. - */ - if (check_direct_access(request)) - /* nothing */; - else if (request->hr_referrer) { - const char *r = request->hr_referrer; - - debug((httpd, DEBUG_FAT, - "checking referrer \"%s\" vs virthostname %s", - r, hostname)); - if (strncmp(r, "http://", 7) != 0 || - (strncasecmp(r + 7, hostname, - strlen(hostname)) != 0 && - !TOP_PAGE(file))) - to_indexhtml = 1; - } else { - const char *h = request->hr_host; - - debug((httpd, DEBUG_FAT, "url has no referrer at all")); - /* if there's no referrer, let / or /index.html past */ - if (!TOP_PAGE(file) || - (h && strncasecmp(h, hostname, - strlen(hostname)) != 0)) - to_indexhtml = 1; - } - - if (to_indexhtml) { - char *slashindexhtml; - - if (asprintf(&slashindexhtml, "/%s", - httpd->index_html) < 0) - bozo_err(httpd, 1, "asprintf"); - debug((httpd, DEBUG_FAT, - "httpd->untrustedref: redirecting %s to %s", - file, slashindexhtml)); - handle_redirect(request, slashindexhtml, 0); - free(slashindexhtml); - return 0; - } - } + /* fix file provided by user as it's used in other handlers */ + request->hr_file = file; len = strlen(file); - if (/*CONSTCOND*/0) { + #ifndef NO_USER_SUPPORT - } else if (len > 1 && httpd->enable_users && file[1] == '~') { + /* first of all expand user path */ + if (len > 1 && httpd->enable_users && file[1] == '~') { if (file[2] == '\0') { (void)bozo_http_error(httpd, 404, request, "missing username"); goto bad_done; } if (strchr(file + 2, '/') == NULL) { - handle_redirect(request, NULL, 0); + char *userredirecturl; + bozoasprintf(httpd, &userredirecturl, "%s/", file); + handle_redirect(request, userredirecturl, 0); + free(userredirecturl); return 0; } debug((httpd, DEBUG_FAT, "calling bozo_user_transform")); - return bozo_user_transform(request, isindex); + if (!bozo_user_transform(request)) + return 0; + + file = request->hr_file; + len = strlen(file); + } #endif /* NO_USER_SUPPORT */ - } else if (len > 1) { + + + switch (check_bzredirect(request)) { + case -1: + goto bad_done; + case 1: + return 0; + } + + if (len > 1) { debug((httpd, DEBUG_FAT, "file[len-1] == %c", file[len-1])); if (file[len-1] == '/') { /* append index.html */ *isindex = 1; @@ -1420,10 +1448,10 @@ transform_request(bozo_httpreq_t *reques strcpy(newfile, file + 1); strcat(newfile, httpd->index_html); } else - newfile = bozostrdup(request->hr_httpd, file + 1); + newfile = bozostrdup(httpd, request, file + 1); } else if (len == 1) { debug((httpd, DEBUG_EXPLODING, "tf_req: len == 1")); - newfile = bozostrdup(request->hr_httpd, httpd->index_html); + newfile = bozostrdup(httpd, request, httpd->index_html); *isindex = 1; } else { /* len == 0 ? */ (void)bozo_http_error(httpd, 500, request, @@ -1437,15 +1465,14 @@ transform_request(bozo_httpreq_t *reques } /* - * look for "http://myname/" and deal with it as necessary. - */ - - /* * stop traversing outside our domain * * XXX true security only comes from our parent using chroot(2) * before execve(2)'ing us. or our own built in chroot(2) support. */ + + debug((httpd, DEBUG_FAT, "newfile: %s", newfile)); + if (*newfile == '/' || strcmp(newfile, "..") == 0 || strstr(newfile, "/..") || strstr(newfile, "../")) { (void)bozo_http_error(httpd, 403, request, "illegal request"); @@ -1549,7 +1576,7 @@ bozo_process_request(bozo_httpreq_t *req fd = -1; encoding = NULL; if (can_gzip(request)) { - asprintf(&file, "%s.gz", request->hr_file); + bozoasprintf(httpd, &file, "%s.gz", request->hr_file); fd = open(file, O_RDONLY); if (fd >= 0) encoding = "gzip"; @@ -1563,7 +1590,7 @@ bozo_process_request(bozo_httpreq_t *req if (fd < 0) { debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno))); - switch(errno) { + switch (errno) { case EPERM: case EACCES: (void)bozo_http_error(httpd, 403, request, @@ -1642,9 +1669,6 @@ bozo_process_request(bozo_httpreq_t *req while (szleft) { size_t sz; - /* This should take care of the first unaligned chunk */ - if ((cur_byte_pos & (httpd->page_size - 1)) != 0) - sz = (size_t)(cur_byte_pos & ~httpd->page_size); if ((off_t)httpd->mmapsz < szleft) sz = httpd->mmapsz; else @@ -1696,6 +1720,12 @@ bozo_print_header(bozo_httpreq_t *reques bozohttpd_t *httpd = request->hr_httpd; off_t len; char date[40]; + bozoheaders_t *hdr; + + SIMPLEQ_FOREACH(hdr, &request->hr_replheaders, h_next) { + bozo_printf(httpd, "%s: %s\r\n", hdr->h_header, + hdr->h_value); + } bozo_printf(httpd, "Date: %s\r\n", bozo_http_date(date, sizeof(date))); bozo_printf(httpd, "Server: %s\r\n", httpd->server_software); @@ -1726,7 +1756,7 @@ bozo_print_header(bozo_httpreq_t *reques len = sbp->st_size; bozo_printf(httpd, "Content-Length: %qd\r\n", (long long)len); } - if (request && request->hr_proto == httpd->consts.http_11) + if (request->hr_proto == httpd->consts.http_11) bozo_printf(httpd, "Connection: close\r\n"); bozo_flush(httpd, stdout); } @@ -1756,7 +1786,7 @@ debug__(bozohttpd_t *httpd, int level, c /* these are like warn() and err(), except for syslog not stderr */ void -bozo_warn(bozohttpd_t *httpd, const char *fmt, ...) +bozowarn(bozohttpd_t *httpd, const char *fmt, ...) { va_list ap; @@ -1771,7 +1801,7 @@ bozo_warn(bozohttpd_t *httpd, const char } void -bozo_err(bozohttpd_t *httpd, int code, const char *fmt, ...) +bozoerr(bozohttpd_t *httpd, int code, const char *fmt, ...) { va_list ap; @@ -1786,6 +1816,20 @@ bozo_err(bozohttpd_t *httpd, int code, c exit(code); } +void +bozoasprintf(bozohttpd_t *httpd, char **str, const char *fmt, ...) +{ + va_list ap; + int e; + + va_start(ap, fmt); + e = vasprintf(str, fmt, ap); + va_end(ap); + + if (e < 0) + bozoerr(httpd, EXIT_FAILURE, "asprintf"); +} + /* * this escapes HTML tags. returns allocated escaped * string if needed, or NULL on allocation failure or @@ -1903,10 +1947,11 @@ bozo_http_error(bozohttpd_t *httpd, int const char *proto = (request && request->hr_proto) ? request->hr_proto : httpd->consts.http_11; int size; + bozoheaders_t *hdr; debug((httpd, DEBUG_FAT, "bozo_http_error %d: %s", code, msg)); if (header == NULL || reason == NULL) { - bozo_err(httpd, 1, + bozoerr(httpd, 1, "bozo_http_error() failed (short = %p, long = %p)", header, reason); return code; @@ -1920,32 +1965,59 @@ bozo_http_error(bozohttpd_t *httpd, int portbuf[0] = '\0'; if (request && request->hr_file) { - char *file = NULL; + char *file = NULL, *user = NULL, *user_escaped = NULL; + int file_alloc = 0; const char *hostname = BOZOHOST(httpd, request); /* bozo_escape_html() failure here is just too bad. */ file = bozo_escape_html(NULL, request->hr_file); - if (file == NULL) + if (file == NULL) file = request->hr_file; + else + file_alloc = 1; + +#ifndef NO_USER_SUPPORT + if (request->hr_user != NULL) { + user_escaped = bozo_escape_html(NULL, request->hr_user); + if (user_escaped == NULL) + user_escaped = request->hr_user; + /* expand username to ~user/ */ + bozoasprintf(httpd, &user, "~%s/", user_escaped); + if (user_escaped != request->hr_user) + free(user_escaped); + } +#endif /* !NO_USER_SUPPORT */ + size = snprintf(httpd->errorbuf, BUFSIZ, "%s\n" "

%s

\n" - "%s:
%s
\n" + "%s%s:
%s
\n" "
%s%s
\n" "\n", - header, header, file, reason, - hostname, portbuf, hostname, portbuf); + header, header, + user ? user : "", file, + reason, hostname, portbuf, hostname, portbuf); + free(user); if (size >= (int)BUFSIZ) { - bozo_warn(httpd, + bozowarn(httpd, "bozo_http_error buffer too small, truncated"); size = (int)BUFSIZ; } + + if (file_alloc) + free(file); } else size = 0; bozo_printf(httpd, "%s %s\r\n", proto, header); - if (request) + + if (request) { bozo_auth_check_401(request, code); + SIMPLEQ_FOREACH(hdr, &request->hr_replheaders, h_next) { + bozo_printf(httpd, "%s: %s\r\n", hdr->h_header, + hdr->h_value); + } + } bozo_printf(httpd, "Content-Type: text/html\r\n"); bozo_printf(httpd, "Content-Length: %d\r\n", size); @@ -2078,12 +2150,11 @@ bozorealloc(bozohttpd_t *httpd, void *pt void *p; p = realloc(ptr, size); - if (p == NULL) { - (void)bozo_http_error(httpd, 500, NULL, - "memory allocation failure"); - exit(1); - } - return (p); + if (p) + return p; + + (void)bozo_http_error(httpd, 500, NULL, "memory allocation failure"); + exit(EXIT_FAILURE); } void * @@ -2092,26 +2163,27 @@ bozomalloc(bozohttpd_t *httpd, size_t si void *p; p = malloc(size); - if (p == NULL) { - (void)bozo_http_error(httpd, 500, NULL, - "memory allocation failure"); - exit(1); - } - return (p); + if (p) + return p; + + (void)bozo_http_error(httpd, 500, NULL, "memory allocation failure"); + exit(EXIT_FAILURE); } char * -bozostrdup(bozohttpd_t *httpd, const char *str) +bozostrdup(bozohttpd_t *httpd, bozo_httpreq_t *request, const char *str) { char *p; p = strdup(str); - if (p == NULL) { - (void)bozo_http_error(httpd, 500, NULL, - "memory allocation failure"); - exit(1); - } - return (p); + if (p) + return p; + + if (!request) + bozoerr(httpd, EXIT_FAILURE, "strdup"); + + (void)bozo_http_error(httpd, 500, request, "memory allocation failure"); + exit(EXIT_FAILURE); } /* set default values in bozohttpd_t struct */ @@ -2144,15 +2216,16 @@ bozo_init_httpd(bozohttpd_t *httpd) /* set default values in bozoprefs_t struct */ int -bozo_init_prefs(bozoprefs_t *prefs) +bozo_init_prefs(bozohttpd_t *httpd, bozoprefs_t *prefs) { /* make sure everything is clean */ (void) memset(prefs, 0x0, sizeof(*prefs)); /* set up default values */ - bozo_set_pref(prefs, "server software", SERVER_SOFTWARE); - bozo_set_pref(prefs, "index.html", INDEX_HTML); - bozo_set_pref(prefs, "public_html", PUBLIC_HTML); + if (!bozo_set_pref(httpd, prefs, "server software", SERVER_SOFTWARE) || + !bozo_set_pref(httpd, prefs, "index.html", INDEX_HTML) || + !bozo_set_pref(httpd, prefs, "public_html", PUBLIC_HTML)) + return 0; return 1; } @@ -2161,7 +2234,7 @@ bozo_init_prefs(bozoprefs_t *prefs) int bozo_set_defaults(bozohttpd_t *httpd, bozoprefs_t *prefs) { - return bozo_init_httpd(httpd) && bozo_init_prefs(prefs); + return bozo_init_httpd(httpd) && bozo_init_prefs(httpd, prefs); } /* set the virtual host name, port and root */ @@ -2183,16 +2256,15 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs if (vhost == NULL) { httpd->virthostname = bozomalloc(httpd, MAXHOSTNAMELEN+1); - /* XXX we do not check for FQDN here */ if (gethostname(httpd->virthostname, MAXHOSTNAMELEN+1) < 0) - bozo_err(httpd, 1, "gethostname"); + bozoerr(httpd, 1, "gethostname"); httpd->virthostname[MAXHOSTNAMELEN] = '\0'; } else { - httpd->virthostname = strdup(vhost); + httpd->virthostname = bozostrdup(httpd, NULL, vhost); } - httpd->slashdir = strdup(root); + httpd->slashdir = bozostrdup(httpd, NULL, root); if ((portnum = bozo_get_pref(prefs, "port number")) != NULL) { - httpd->bindport = strdup(portnum); + httpd->bindport = bozostrdup(httpd, NULL, portnum); } /* go over preferences now */ @@ -2200,16 +2272,12 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs strcmp(cp, "true") == 0) { httpd->numeric = 1; } - if ((cp = bozo_get_pref(prefs, "trusted referal")) != NULL && - strcmp(cp, "true") == 0) { - httpd->untrustedref = 1; - } if ((cp = bozo_get_pref(prefs, "log to stderr")) != NULL && strcmp(cp, "true") == 0) { httpd->logstderr = 1; } if ((cp = bozo_get_pref(prefs, "bind address")) != NULL) { - httpd->bindaddress = strdup(cp); + httpd->bindaddress = bozostrdup(httpd, NULL, cp); } if ((cp = bozo_get_pref(prefs, "background")) != NULL) { httpd->background = atoi(cp); @@ -2219,19 +2287,23 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs httpd->foreground = 1; } if ((cp = bozo_get_pref(prefs, "pid file")) != NULL) { - httpd->pidfile = strdup(cp); + httpd->pidfile = bozostrdup(httpd, NULL, cp); } if ((cp = bozo_get_pref(prefs, "unknown slash")) != NULL && strcmp(cp, "true") == 0) { httpd->unknown_slash = 1; } if ((cp = bozo_get_pref(prefs, "virtual base")) != NULL) { - httpd->virtbase = strdup(cp); + httpd->virtbase = bozostrdup(httpd, NULL, cp); } if ((cp = bozo_get_pref(prefs, "enable users")) != NULL && strcmp(cp, "true") == 0) { httpd->enable_users = 1; } + if ((cp = bozo_get_pref(prefs, "enable user cgibin")) != NULL && + strcmp(cp, "true") == 0) { + httpd->enable_cgi_users = 1; + } if ((cp = bozo_get_pref(prefs, "dirty environment")) != NULL && strcmp(cp, "true") == 0) { dirtyenv = 1; @@ -2245,11 +2317,12 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs httpd->dir_indexing = 1; } if ((cp = bozo_get_pref(prefs, "public_html")) != NULL) { - httpd->public_html = strdup(cp); + httpd->public_html = bozostrdup(httpd, NULL, cp); } httpd->server_software = - strdup(bozo_get_pref(prefs, "server software")); - httpd->index_html = strdup(bozo_get_pref(prefs, "index.html")); + bozostrdup(httpd, NULL, bozo_get_pref(prefs, "server software")); + httpd->index_html = + bozostrdup(httpd, NULL, bozo_get_pref(prefs, "index.html")); /* * initialise ssl and daemon mode if necessary. @@ -2257,39 +2330,33 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs bozo_ssl_init(httpd); bozo_daemon_init(httpd); - if ((username = bozo_get_pref(prefs, "username")) == NULL) { - if ((pw = getpwuid(uid = 0)) == NULL) - bozo_err(httpd, 1, "getpwuid(0): %s", strerror(errno)); - httpd->username = strdup(pw->pw_name); - } else { - httpd->username = strdup(username); - if ((pw = getpwnam(httpd->username)) == NULL) - bozo_err(httpd, 1, "getpwnam(%s): %s", httpd->username, - strerror(errno)); + username = bozo_get_pref(prefs, "username"); + if (username != NULL) { + if ((pw = getpwnam(username)) == NULL) + bozoerr(httpd, 1, "getpwnam(%s): %s", username, + strerror(errno)); if (initgroups(pw->pw_name, pw->pw_gid) == -1) - bozo_err(httpd, 1, "initgroups: %s", strerror(errno)); + bozoerr(httpd, 1, "initgroups: %s", strerror(errno)); if (setgid(pw->pw_gid) == -1) - bozo_err(httpd, 1, "setgid(%u): %s", pw->pw_gid, - strerror(errno)); + bozoerr(httpd, 1, "setgid(%u): %s", pw->pw_gid, + strerror(errno)); uid = pw->pw_uid; } /* * handle chroot. */ if ((chrootdir = bozo_get_pref(prefs, "chroot dir")) != NULL) { - httpd->rootdir = strdup(chrootdir); + httpd->rootdir = bozostrdup(httpd, NULL, chrootdir); if (chdir(httpd->rootdir) == -1) - bozo_err(httpd, 1, "chdir(%s): %s", httpd->rootdir, + bozoerr(httpd, 1, "chdir(%s): %s", httpd->rootdir, strerror(errno)); if (chroot(httpd->rootdir) == -1) - bozo_err(httpd, 1, "chroot(%s): %s", httpd->rootdir, + bozoerr(httpd, 1, "chroot(%s): %s", httpd->rootdir, strerror(errno)); } - if (username != NULL) - if (setuid(uid) == -1) - bozo_err(httpd, 1, "setuid(%d): %s", uid, - strerror(errno)); + if (username != NULL && setuid(uid) == -1) + bozoerr(httpd, 1, "setuid(%d): %s", uid, strerror(errno)); /* * prevent info leakage between different compartments. Index: bozohttpd.h =================================================================== RCS file: /cvsroot/src/libexec/httpd/bozohttpd.h,v retrieving revision 1.33.2.2 diff -p -u -u -r1.33.2.2 bozohttpd.h --- bozohttpd.h 9 May 2015 08:50:42 -0000 1.33.2.2 +++ bozohttpd.h 15 Apr 2016 08:15:12 -0000 @@ -41,6 +41,11 @@ #endif #include +/* QNX provides a lot of NetBSD things in nbutil.h */ +#ifdef HAVE_NBUTIL_H +#include +#endif + /* lots of "const" but gets free()'ed etc at times, sigh */ /* headers */ @@ -49,6 +54,7 @@ typedef struct bozoheaders { /*const*/ char *h_value; /* this gets free()'ed etc at times */ SIMPLEQ_ENTRY(bozoheaders) h_next; } bozoheaders_t; +SIMPLEQ_HEAD(qheaders, bozoheaders); #ifndef NO_LUA_SUPPORT typedef struct lua_handler { @@ -85,11 +91,9 @@ typedef struct bozo_consts_t { /* this structure encapsulates all the bozo flags and control vars */ typedef struct bozohttpd_t { char *rootdir; /* root directory */ - char *username; /* username to switch to */ int numeric; /* avoid gethostby*() */ char *virtbase; /* virtual directory base */ int unknown_slash; /* unknown vhosts go to normal slashdir */ - int untrustedref; /* make sure referrer = me unless url = / */ int logstderr; /* log to stderr (even if not tty) */ int background; /* drop into daemon mode */ int foreground; /* keep daemon mode in foreground */ @@ -104,6 +108,7 @@ typedef struct bozohttpd_t { const char *index_html; /* our home page */ const char *public_html; /* ~user/public_html page */ int enable_users; /* enable public_html */ + int enable_cgi_users; /* use the cgi handler */ int *sock; /* bound sockets */ int nsock; /* number of above */ struct pollfd *fds; /* current poll fd set */ @@ -146,6 +151,9 @@ typedef struct bozo_httpreq_t { char *hr_query; char *hr_host; /* HTTP/1.1 Host: or virtual hostname, possibly including a port number */ +#ifndef NO_USER_SUPPORT + char *hr_user; /* username if we hit userdir request */ +#endif /* !NO_USER_SUPPORT */ const char *hr_proto; const char *hr_content_type; const char *hr_content_length; @@ -165,8 +173,9 @@ typedef struct bozo_httpreq_t { /*const*/ char *hr_authuser; /*const*/ char *hr_authpass; #endif - SIMPLEQ_HEAD(, bozoheaders) hr_headers; - int hr_nheaders; + struct qheaders hr_headers; + struct qheaders hr_replheaders; + int hr_nheaders; } bozo_httpreq_t; /* helper to access the "active" host name from a httpd/request pair */ @@ -176,8 +185,8 @@ typedef struct bozo_httpreq_t { /* structure to hold string based (name, value) pairs with preferences */ typedef struct bozoprefs_t { - unsigned size; /* size of the two arrays */ - unsigned c; /* # of entries in arrays */ + size_t size; /* size of the two arrays */ + size_t count; /* # of entries in arrays */ char **name; /* names of each entry */ char **value; /* values for the name entries */ } bozoprefs_t; @@ -210,34 +219,42 @@ void debug__(bozohttpd_t *, int, const c #define debug(x) #endif /* NO_DEBUG */ -void bozo_warn(bozohttpd_t *, const char *, ...) - BOZO_PRINTFLIKE(2, 3); -void bozo_err(bozohttpd_t *, int, const char *, ...) - BOZO_PRINTFLIKE(3, 4) - BOZO_DEAD; int bozo_http_error(bozohttpd_t *, int, bozo_httpreq_t *, const char *); int bozo_check_special_files(bozo_httpreq_t *, const char *); char *bozo_http_date(char *, size_t); -void bozo_print_header(bozo_httpreq_t *, struct stat *, const char *, const char *); -char *bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url); +void bozo_print_header(bozo_httpreq_t *, struct stat *, const char *, + const char *); +char *bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url, int absolute); char *bozo_escape_html(bozohttpd_t *httpd, const char *url); -char *bozodgetln(bozohttpd_t *, int, ssize_t *, ssize_t (*)(bozohttpd_t *, int, void *, size_t)); +/* these are similar to libc functions, no underscore here */ +void bozowarn(bozohttpd_t *, const char *, ...) + BOZO_PRINTFLIKE(2, 3); +void bozoerr(bozohttpd_t *, int, const char *, ...) + BOZO_PRINTFLIKE(3, 4) + BOZO_DEAD; +void bozoasprintf(bozohttpd_t *, char **, const char *, ...) + BOZO_PRINTFLIKE(3, 4); +char *bozodgetln(bozohttpd_t *, int, ssize_t *, ssize_t (*)(bozohttpd_t *, + int, void *, size_t)); char *bozostrnsep(char **, const char *, ssize_t *); - void *bozomalloc(bozohttpd_t *, size_t); void *bozorealloc(bozohttpd_t *, void *, size_t); -char *bozostrdup(bozohttpd_t *, const char *); +char *bozostrdup(bozohttpd_t *, bozo_httpreq_t *, const char *); + +#define bozo_noop do { /* nothing */ } while (/*CONSTCOND*/0) /* ssl-bozo.c */ #ifdef NO_SSL_SUPPORT -#define bozo_ssl_set_opts(w, x, y) do { /* nothing */ } while (0) -#define bozo_ssl_init(x) do { /* nothing */ } while (0) +#define bozo_ssl_set_opts(w, x, y) bozo_noop +#define bozo_ssl_set_ciphers(w, x, y) bozo_noop +#define bozo_ssl_init(x) bozo_noop #define bozo_ssl_accept(x) (0) -#define bozo_ssl_destroy(x) do { /* nothing */ } while (0) +#define bozo_ssl_destroy(x) bozo_noop #else void bozo_ssl_set_opts(bozohttpd_t *, const char *, const char *); +void bozo_ssl_set_ciphers(bozohttpd_t *, const char *); void bozo_ssl_init(bozohttpd_t *); int bozo_ssl_accept(bozohttpd_t *); void bozo_ssl_destroy(bozohttpd_t *); @@ -255,13 +272,13 @@ void bozo_auth_check_401(bozo_httpreq_t void bozo_auth_cgi_setenv(bozo_httpreq_t *, char ***); int bozo_auth_cgi_count(bozo_httpreq_t *); #else -#define bozo_auth_init(x) do { /* nothing */ } while (0) +#define bozo_auth_init(x) bozo_noop #define bozo_auth_check(x, y) 0 -#define bozo_auth_cleanup(x) do { /* nothing */ } while (0) +#define bozo_auth_cleanup(x) bozo_noop #define bozo_auth_check_headers(y, z, a, b) 0 #define bozo_auth_check_special_files(x, y) 0 -#define bozo_auth_check_401(x, y) do { /* nothing */ } while (0) -#define bozo_auth_cgi_setenv(x, y) do { /* nothing */ } while (0) +#define bozo_auth_check_401(x, y) bozo_noop +#define bozo_auth_cgi_setenv(x, y) bozo_noop #define bozo_auth_cgi_count(x) 0 #endif /* DO_HTPASSWD */ @@ -288,9 +305,9 @@ int bozo_process_lua(bozo_httpreq_t *); /* daemon-bozo.c */ #ifdef NO_DAEMON_MODE -#define bozo_daemon_init(x) do { /* nothing */ } while (0) +#define bozo_daemon_init(x) bozo_noop #define bozo_daemon_fork(x) 0 -#define bozo_daemon_closefds(x) do { /* nothing */ } while (0) +#define bozo_daemon_closefds(x) bozo_noop #else void bozo_daemon_init(bozohttpd_t *); int bozo_daemon_fork(bozohttpd_t *); @@ -300,9 +317,11 @@ void bozo_daemon_closefds(bozohttpd_t *) /* tilde-luzah-bozo.c */ #ifdef NO_USER_SUPPORT -#define bozo_user_transform(a, c) 0 +#define bozo_user_transform(x) 0 +#define bozo_user_free(x) 0 #else -int bozo_user_transform(bozo_httpreq_t *, int *); +int bozo_user_transform(bozo_httpreq_t *); +#define bozo_user_free(x) free(x) #endif /* NO_USER_SUPPORT */ @@ -320,7 +339,8 @@ const char *bozo_content_encoding(bozo_h bozo_content_map_t *bozo_match_content_map(bozohttpd_t *, const char *, int); bozo_content_map_t *bozo_get_content_map(bozohttpd_t *, const char *); #ifndef NO_DYNAMIC_CONTENT -void bozo_add_content_map_mime(bozohttpd_t *, const char *, const char *, const char *, const char *); +void bozo_add_content_map_mime(bozohttpd_t *, const char *, const char *, + const char *, const char *); #endif /* I/O */ @@ -331,15 +351,19 @@ int bozo_flush(bozohttpd_t *, FILE *); /* misc */ int bozo_init_httpd(bozohttpd_t *); -int bozo_init_prefs(bozoprefs_t *); +int bozo_init_prefs(bozohttpd_t *, bozoprefs_t *); int bozo_set_defaults(bozohttpd_t *, bozoprefs_t *); int bozo_setup(bozohttpd_t *, bozoprefs_t *, const char *, const char *); bozo_httpreq_t *bozo_read_request(bozohttpd_t *); void bozo_process_request(bozo_httpreq_t *); void bozo_clean_request(bozo_httpreq_t *); +bozoheaders_t *addmerge_reqheader(bozo_httpreq_t *, const char *, + const char *, ssize_t); +bozoheaders_t *addmerge_replheader(bozo_httpreq_t *, const char *, + const char *, ssize_t); /* variables */ -int bozo_set_pref(bozoprefs_t *, const char *, const char *); +int bozo_set_pref(bozohttpd_t *, bozoprefs_t *, const char *, const char *); char *bozo_get_pref(bozoprefs_t *, const char *); #endif /* BOZOHTTOPD_H_ */ Index: cgi-bozo.c =================================================================== RCS file: /cvsroot/src/libexec/httpd/cgi-bozo.c,v retrieving revision 1.25.2.2 diff -p -u -u -r1.25.2.2 cgi-bozo.c --- cgi-bozo.c 9 May 2015 08:50:42 -0000 1.25.2.2 +++ cgi-bozo.c 15 Apr 2016 08:15:12 -0000 @@ -75,22 +75,23 @@ content_cgihandler(bozohttpd_t *httpd, b } static int -parse_header(bozohttpd_t *httpd, const char *str, ssize_t len, char **hdr_str, - char **hdr_val) +parse_header(bozo_httpreq_t *request, const char *str, ssize_t len, + char **hdr_str, char **hdr_val) { + struct bozohttpd_t *httpd = request->hr_httpd; char *name, *value; /* if the string passed is zero-length bail out */ if (*str == '\0') return -1; - value = bozostrdup(httpd, str); + value = bozostrdup(httpd, request, str); /* locate the ':' separator in the header/value */ name = bozostrnsep(&value, ":", &len); if (NULL == name || -1 == len) { - free(name); + free(value); return -1; } @@ -127,7 +128,7 @@ finish_cgi_output(bozohttpd_t *httpd, bo (str = bozodgetln(httpd, in, &len, bozo_read)) != NULL) { char *hdr_name, *hdr_value; - if (parse_header(httpd, str, len, &hdr_name, &hdr_value)) + if (parse_header(request, str, len, &hdr_name, &hdr_value)) break; /* @@ -194,7 +195,7 @@ finish_cgi_output(bozohttpd_t *httpd, bo rbytes -= wbytes; bp += wbytes; } else - bozo_err(httpd, 1, + bozoerr(httpd, 1, "cgi output write failed: %s", strerror(errno)); } @@ -214,7 +215,7 @@ append_index_html(bozohttpd_t *httpd, ch void bozo_cgi_setbin(bozohttpd_t *httpd, const char *path) { - httpd->cgibin = strdup(path); + httpd->cgibin = bozostrdup(httpd, NULL, path); debug((httpd, DEBUG_OBESE, "cgibin (cgi-bin directory) is %s", httpd->cgibin)); } @@ -247,8 +248,7 @@ bozo_process_cgi(bozo_httpreq_t *request char date[40]; bozoheaders_t *headp; const char *type, *clen, *info, *cgihandler; - char *query, *s, *t, *path, *env, *file, *url; - char command[MAXPATHLEN]; + char *query, *s, *t, *path, *env, *command, *file, *url; char **envp, **curenvp, *argv[4]; char *uri; size_t len; @@ -260,31 +260,36 @@ bozo_process_cgi(bozo_httpreq_t *request if (!httpd->cgibin && !httpd->process_cgi) return 0; +#ifndef NO_USER_SUPPORT + if (request->hr_user && !httpd->enable_cgi_users) + return 0; +#endif /* !NO_USER_SUPPORT */ + if (request->hr_oldfile && strcmp(request->hr_oldfile, "/") != 0) uri = request->hr_oldfile; else uri = request->hr_file; if (uri[0] == '/') - file = bozostrdup(httpd, uri); + file = bozostrdup(httpd, request, uri); else - asprintf(&file, "/%s", uri); - if (file == NULL) - return 0; + bozoasprintf(httpd, &file, "/%s", uri); if (request->hr_query && strlen(request->hr_query)) - query = bozostrdup(httpd, request->hr_query); + query = bozostrdup(httpd, request, request->hr_query); else query = NULL; - asprintf(&url, "%s%s%s", file, query ? "?" : "", query ? query : ""); - if (url == NULL) - goto out; + bozoasprintf(httpd, &url, "%s%s%s", + file, + query ? "?" : "", + query ? query : ""); debug((httpd, DEBUG_NORMAL, "bozo_process_cgi: url `%s'", url)); path = NULL; envp = NULL; cgihandler = NULL; + command = NULL; info = NULL; len = strlen(url); @@ -309,15 +314,14 @@ bozo_process_cgi(bozo_httpreq_t *request ix = 0; if (cgihandler) { - snprintf(command, sizeof(command), "%s", file + 1); - path = bozostrdup(httpd, cgihandler); + command = file + 1; + path = bozostrdup(httpd, request, cgihandler); argv[ix++] = path; /* argv[] = [ path, command, query, NULL ] */ } else { - snprintf(command, sizeof(command), "%s", - file + CGIBIN_PREFIX_LEN + 1); + command = file + CGIBIN_PREFIX_LEN + 1; if ((s = strchr(command, '/')) != NULL) { - info = bozostrdup(httpd, s); + info = bozostrdup(httpd, request, s); *s = '\0'; } path = bozomalloc(httpd, @@ -414,21 +418,18 @@ bozo_process_cgi(bozo_httpreq_t *request bozo_setenv(httpd, "REMOTE_ADDR", request->hr_remoteaddr, curenvp++); /* - * XXX Apache does this when invoking content handlers, and PHP - * XXX 5.3 requires it as a "security" measure. + * Apache does this when invoking content handlers, and PHP + * 5.3 requires it as a "security" measure. */ if (cgihandler) bozo_setenv(httpd, "REDIRECT_STATUS", "200", curenvp++); bozo_auth_cgi_setenv(request, &curenvp); - free(file); - free(url); - debug((httpd, DEBUG_FAT, "bozo_process_cgi: going exec %s, %s %s %s", path, argv[0], strornull(argv[1]), strornull(argv[2]))); if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sv) == -1) - bozo_err(httpd, 1, "child socketpair failed: %s", + bozoerr(httpd, 1, "child socketpair failed: %s", strerror(errno)); /* @@ -439,7 +440,7 @@ bozo_process_cgi(bozo_httpreq_t *request */ switch (fork()) { case -1: /* eep, failure */ - bozo_err(httpd, 1, "child fork failed: %s", strerror(errno)); + bozoerr(httpd, 1, "child fork failed: %s", strerror(errno)); /*NOTREACHED*/ case 0: close(sv[0]); @@ -451,19 +452,23 @@ bozo_process_cgi(bozo_httpreq_t *request bozo_daemon_closefds(httpd); if (-1 == execve(path, argv, envp)) - bozo_err(httpd, 1, "child exec failed: %s: %s", + bozoerr(httpd, 1, "child exec failed: %s: %s", path, strerror(errno)); /* NOT REACHED */ - bozo_err(httpd, 1, "child execve returned?!"); + bozoerr(httpd, 1, "child execve returned?!"); } + free(query); + free(file); + free(url); + close(sv[1]); /* parent: read from stdin (bozo_read()) write to sv[0] */ /* child: read from sv[0] (bozo_write()) write to stdout */ pid = fork(); if (pid == -1) - bozo_err(httpd, 1, "io child fork failed: %s", strerror(errno)); + bozoerr(httpd, 1, "io child fork failed: %s", strerror(errno)); else if (pid == 0) { /* child reader/writer */ close(STDIN_FILENO); @@ -487,7 +492,7 @@ bozo_process_cgi(bozo_httpreq_t *request rbytes -= wbytes; bp += wbytes; } else - bozo_err(httpd, 1, "write failed: %s", + bozoerr(httpd, 1, "write failed: %s", strerror(errno)); } } @@ -504,7 +509,8 @@ bozo_process_cgi(bozo_httpreq_t *request #ifndef NO_DYNAMIC_CONTENT /* cgi maps are simple ".postfix /path/to/prog" */ void -bozo_add_content_map_cgi(bozohttpd_t *httpd, const char *arg, const char *cgihandler) +bozo_add_content_map_cgi(bozohttpd_t *httpd, const char *arg, + const char *cgihandler) { bozo_content_map_t *map; Index: content-bozo.c =================================================================== RCS file: /cvsroot/src/libexec/httpd/content-bozo.c,v retrieving revision 1.10.2.2 diff -p -u -u -r1.10.2.2 content-bozo.c --- content-bozo.c 9 May 2015 08:50:42 -0000 1.10.2.2 +++ content-bozo.c 15 Apr 2016 08:15:12 -0000 @@ -258,7 +258,7 @@ bozo_get_content_map(bozohttpd_t *httpd, httpd->dynamic_content_map, (httpd->dynamic_content_map_size + 1) * sizeof *map); if (httpd->dynamic_content_map == NULL) - bozo_err(httpd, 1, "out of memory allocating content map"); + bozoerr(httpd, 1, "out of memory allocating content map"); map = &httpd->dynamic_content_map[httpd->dynamic_content_map_size]; map->name = map->type = map->encoding = map->encoding11 = map->cgihandler = NULL; Index: daemon-bozo.c =================================================================== RCS file: /cvsroot/src/libexec/httpd/daemon-bozo.c,v retrieving revision 1.16 diff -p -u -u -r1.16 daemon-bozo.c --- daemon-bozo.c 2 Jan 2014 08:21:38 -0000 1.16 +++ daemon-bozo.c 15 Apr 2016 08:15:12 -0000 @@ -105,10 +105,10 @@ create_pidfile(bozohttpd_t *httpd) return; if (atexit(remove_pidfile) == -1) - bozo_err(httpd, 1, "Failed to install pidfile handler"); + bozoerr(httpd, 1, "Failed to install pidfile handler"); if ((file = fopen(httpd->pidfile, "w")) == NULL) - bozo_err(httpd, 1, "Failed to create pidfile '%s'", + bozoerr(httpd, 1, "Failed to create pidfile '%s'", httpd->pidfile); (void)fprintf(file, "%d\n", getpid()); (void)fclose(file); @@ -138,7 +138,7 @@ bozo_daemon_init(bozohttpd_t *httpd) h.ai_flags = AI_PASSIVE; e = getaddrinfo(httpd->bindaddress, portnum, &h, &r0); if (e) - bozo_err(httpd, 1, "getaddrinfo([%s]:%s): %s", + bozoerr(httpd, 1, "getaddrinfo([%s]:%s): %s", httpd->bindaddress ? httpd->bindaddress : "*", portnum, gai_strerror(e)); for (r = r0; r != NULL; r = r->ai_next) @@ -151,7 +151,7 @@ bozo_daemon_init(bozohttpd_t *httpd) continue; if (setsockopt(httpd->sock[i], SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) - bozo_warn(httpd, "setsockopt SO_REUSEADDR: %s", + bozowarn(httpd, "setsockopt SO_REUSEADDR: %s", strerror(errno)); if (bind(httpd->sock[i], r->ai_addr, r->ai_addrlen) == -1) continue; @@ -163,7 +163,7 @@ bozo_daemon_init(bozohttpd_t *httpd) i++; } if (i == 0) - bozo_err(httpd, 1, "could not find any addresses to bind"); + bozoerr(httpd, 1, "could not find any addresses to bind"); httpd->nsock = i; freeaddrinfo(r0); @@ -172,7 +172,7 @@ bozo_daemon_init(bozohttpd_t *httpd) create_pidfile(httpd); - bozo_warn(httpd, "started in daemon mode as `%s' port `%s' root `%s'", + bozowarn(httpd, "started in daemon mode as `%s' port `%s' root `%s'", httpd->virthostname, portnum, httpd->slashdir); signal(SIGHUP, controlled_exit); @@ -209,13 +209,13 @@ daemon_poll_err(bozohttpd_t *httpd, int if ((httpd->fds[idx].revents & (POLLNVAL|POLLERR|POLLHUP)) == 0) return 0; - bozo_warn(httpd, "poll on fd %d pid %d revents %d: %s", + bozowarn(httpd, "poll on fd %d pid %d revents %d: %s", httpd->fds[idx].fd, getpid(), httpd->fds[idx].revents, strerror(errno)); - bozo_warn(httpd, "nsock = %d", httpd->nsock); + bozowarn(httpd, "nsock = %d", httpd->nsock); close(httpd->sock[idx]); httpd->nsock--; - bozo_warn(httpd, "nsock now = %d", httpd->nsock); + bozowarn(httpd, "nsock now = %d", httpd->nsock); /* no sockets left */ if (httpd->nsock == 0) exit(0); @@ -271,7 +271,7 @@ again: /* fail on programmer errors */ if (errno == EFAULT || errno == EINVAL) - bozo_err(httpd, 1, "poll: %s", + bozoerr(httpd, 1, "poll: %s", strerror(errno)); /* sleep on some temporary kernel failures */ @@ -294,7 +294,7 @@ again: if (fd == -1) { if (errno == EFAULT || errno == EINVAL) - bozo_err(httpd, 1, "accept: %s", + bozoerr(httpd, 1, "accept: %s", strerror(errno)); if (errno == ENOMEM || @@ -317,7 +317,7 @@ again: switch (fork()) { case -1: /* eep, failure */ - bozo_warn(httpd, "fork() failed, sleeping for " + bozowarn(httpd, "fork() failed, sleeping for " "10 seconds: %s", strerror(errno)); close(fd); sleep(10); Index: dir-index-bozo.c =================================================================== RCS file: /cvsroot/src/libexec/httpd/dir-index-bozo.c,v retrieving revision 1.19.4.1 diff -p -u -u -r1.19.4.1 dir-index-bozo.c --- dir-index-bozo.c 12 Jan 2015 10:02:29 -0000 1.19.4.1 +++ dir-index-bozo.c 15 Apr 2016 08:15:12 -0000 @@ -57,7 +57,7 @@ directory_hr(bozohttpd_t *httpd) * output a directory index. return 1 if it actually did something.. */ int -bozo_dir_index(bozo_httpreq_t *request, const char *dirname, int isindex) +bozo_dir_index(bozo_httpreq_t *request, const char *dirpath, int isindex) { bozohttpd_t *httpd = request->hr_httpd; struct stat sb; @@ -66,23 +66,23 @@ bozo_dir_index(bozo_httpreq_t *request, DIR *dp; char buf[MAXPATHLEN]; char spacebuf[48]; - char *file = NULL; + char *file = NULL, *printname = NULL; int l, k, j, i; if (!isindex || !httpd->dir_indexing) return 0; - if (strlen(dirname) <= strlen(httpd->index_html)) - dirname = "."; + if (strlen(dirpath) <= strlen(httpd->index_html)) + dirpath = "."; else { - file = bozostrdup(httpd, dirname); + file = bozostrdup(httpd, request, dirpath); file[strlen(file) - strlen(httpd->index_html)] = '\0'; - dirname = file; + dirpath = file; } - debug((httpd, DEBUG_FAT, "bozo_dir_index: dirname ``%s''", dirname)); - if (stat(dirname, &sb) < 0 || - (dp = opendir(dirname)) == NULL) { + debug((httpd, DEBUG_FAT, "bozo_dir_index: dirpath ``%s''", dirpath)); + if (stat(dirpath, &sb) < 0 || + (dp = opendir(dirpath)) == NULL) { if (errno == EPERM) (void)bozo_http_error(httpd, 403, request, "no permission to open directory"); @@ -108,11 +108,21 @@ bozo_dir_index(bozo_httpreq_t *request, goto done; } +#ifndef NO_USER_SUPPORT + if (request->hr_user) { + bozoasprintf(httpd, &printname, "~%s/%s", + request->hr_user, request->hr_file); + } else + printname = bozostrdup(httpd, request, request->hr_file); +#else + printname = bozostrdup(httpd, request, request->hr_file); +#endif /* !NO_USER_SUPPORT */ + bozo_printf(httpd, "Index of %s\r\n", - request->hr_file); + printname); bozo_printf(httpd, "

Index of %s

\r\n", - request->hr_file); + printname); bozo_printf(httpd, "
\r\n");
 #define NAMELEN 40
 #define LMODLEN 19
@@ -123,7 +133,7 @@ bozo_dir_index(bozo_httpreq_t *request, 
 	directory_hr(httpd);
 	bozo_printf(httpd, "
");
 
-	for (j = k = scandir(dirname, &de, NULL, alphasort), deo = de;
+	for (j = k = scandir(dirpath, &de, NULL, alphasort), deo = de;
 	    j--; de++) {
 		int nostat = 0;
 		char *name = (*de)->d_name;
@@ -134,13 +144,13 @@ bozo_dir_index(bozo_httpreq_t *request, 
 		     httpd->hide_dots && name[0] == '.'))
 			continue;
 
-		snprintf(buf, sizeof buf, "%s/%s", dirname, name);
+		snprintf(buf, sizeof buf, "%s/%s", dirpath, name);
 		if (stat(buf, &sb))
 			nostat = 1;
 
 		l = 0;
 
-		urlname = bozo_escape_rfc3986(httpd, name);
+		urlname = bozo_escape_rfc3986(httpd, name, 0);
 		htmlname = bozo_escape_html(httpd, name);
 		if (htmlname == NULL)
 			htmlname = name;
@@ -206,6 +216,7 @@ bozo_dir_index(bozo_httpreq_t *request, 
 
 done:
 	free(file);
+	free(printname);
 	return 1;
 }
 #endif /* NO_DIRINDEX_SUPPORT */
Index: lua-bozo.c
===================================================================
RCS file: /cvsroot/src/libexec/httpd/lua-bozo.c,v
retrieving revision 1.10.2.1
diff -p -u -u -r1.10.2.1 lua-bozo.c
--- lua-bozo.c	12 Jan 2015 10:02:29 -0000	1.10.2.1
+++ lua-bozo.c	15 Apr 2016 08:15:13 -0000
@@ -123,7 +123,7 @@ lua_register_handler(lua_State *L)
 
 	handler = bozomalloc(httpd, sizeof(lua_handler_t));
 
-	handler->name = bozostrdup(httpd, lua_tostring(L, 1));
+	handler->name = bozostrdup(httpd, NULL, lua_tostring(L, 1));
 	handler->ref = luaL_ref(L, LUA_REGISTRYINDEX);
 	SIMPLEQ_INSERT_TAIL(&map->handlers, handler, h_next);
 	httpd->process_lua = 1;
@@ -184,19 +184,19 @@ bozo_add_lua_map(bozohttpd_t *httpd, con
 	lua_state_map_t *map;
 
 	map = bozomalloc(httpd, sizeof(lua_state_map_t));
-	map->prefix = bozostrdup(httpd, prefix);
+	map->prefix = bozostrdup(httpd, NULL, prefix);
 	if (*script == '/')
-		map->script = bozostrdup(httpd, script);
+		map->script = bozostrdup(httpd, NULL, script);
 	else {
 		char cwd[MAXPATHLEN], *path;
 
 		getcwd(cwd, sizeof(cwd) - 1);
-		asprintf(&path, "%s/%s", cwd, script);
+		bozoasprintf(httpd, &path, "%s/%s", cwd, script);
 		map->script = path;
 	}
 	map->L = luaL_newstate();
 	if (map->L == NULL)
-		bozo_err(httpd, 1, "can't create Lua state");
+		bozoerr(httpd, 1, "can't create Lua state");
 	SIMPLEQ_INIT(&map->handlers);
 
 #if LUA_VERSION_NUM >= 502
@@ -225,10 +225,10 @@ bozo_add_lua_map(bozohttpd_t *httpd, con
 	lua_settable(map->L, LUA_REGISTRYINDEX);
 
 	if (luaL_loadfile(map->L, script))
-		bozo_err(httpd, 1, "failed to load script %s: %s", script,
+		bozoerr(httpd, 1, "failed to load script %s: %s", script,
 		    lua_tostring(map->L, -1));
 	if (lua_pcall(map->L, 0, 0, 0))
-		bozo_err(httpd, 1, "failed to execute script %s: %s", script,
+		bozoerr(httpd, 1, "failed to execute script %s: %s", script,
 		    lua_tostring(map->L, -1));
 	SIMPLEQ_INSERT_TAIL(&httpd->lua_states, map, s_next);
 }
@@ -311,44 +311,41 @@ bozo_process_lua(bozo_httpreq_t *request
 	if (!httpd->process_lua)
 		return 0;
 
+	info = NULL;
+	query = NULL;
+	prefix = NULL;
 	uri = request->hr_oldfile ? request->hr_oldfile : request->hr_file;
 
 	if (*uri == '/') {
-		file = bozostrdup(httpd, uri);
-		prefix = bozostrdup(httpd, &uri[1]);
+		file = bozostrdup(httpd, request, uri);
+		if (file == NULL)
+			goto out;
+		prefix = bozostrdup(httpd, request, &uri[1]);
 	} else {
-		prefix = bozostrdup(httpd, uri);
-		asprintf(&file, "/%s", uri);
-	}
-	if (file == NULL) {
-		free(prefix);
-		return 0;
+		if (asprintf(&file, "/%s", uri) < 0)
+			goto out;
+		prefix = bozostrdup(httpd, request, uri);
 	}
+	if (prefix == NULL)
+		goto out;
 
-	if (request->hr_query && strlen(request->hr_query))
-		query = bozostrdup(httpd, request->hr_query);
-	else
-		query = NULL;
+	if (request->hr_query && request->hr_query[0])
+		query = bozostrdup(httpd, request, request->hr_query);
 
 	p = strchr(prefix, '/');
-	if (p == NULL){
-		free(prefix);
-		return 0;
-	}
+	if (p == NULL)
+		goto out;
 	*p++ = '\0';
 	handler = p;
-	if (!*handler) {
-		free(prefix);
-		return 0;
-	}
+	if (!*handler)
+		goto out;
 	p = strchr(handler, '/');
 	if (p != NULL)
 		*p++ = '\0';
 
-	info = NULL;
 	command = file + 1;
 	if ((s = strchr(command, '/')) != NULL) {
-		info = bozostrdup(httpd, s);
+		info = bozostrdup(httpd, request, s);
 		*s = '\0';
 	}
 
Index: main.c
===================================================================
RCS file: /cvsroot/src/libexec/httpd/main.c,v
retrieving revision 1.8
diff -p -u -u -r1.8 main.c
--- main.c	16 Jul 2014 07:41:43 -0000	1.8
+++ main.c	15 Apr 2016 08:15:13 -0000
@@ -58,65 +58,67 @@
 BOZO_DEAD static void
 usage(bozohttpd_t *httpd, char *progname)
 {
-	bozo_warn(httpd, "usage: %s [options] slashdir [virtualhostname]",
+	bozowarn(httpd, "usage: %s [options] slashdir [virtualhostname]",
 			progname);
-	bozo_warn(httpd, "options:");
+	bozowarn(httpd, "options:");
 #ifndef NO_DEBUG
-	bozo_warn(httpd, "   -d\t\t\tenable debug support");
+	bozowarn(httpd, "   -d\t\t\tenable debug support");
+#endif
+	bozowarn(httpd, "   -s\t\t\talways log to stderr");
+#ifndef NO_DYNAMIC_CONTENT
+	bozowarn(httpd, "   -M arg t c c11\tadd this mime extenstion");
 #endif
-	bozo_warn(httpd, "   -s\t\t\talways log to stderr");
 #ifndef NO_USER_SUPPORT
-	bozo_warn(httpd, "   -u\t\t\tenable ~user/public_html support");
-	bozo_warn(httpd, "   -p dir\t\tchange `public_html' directory name]");
+	bozowarn(httpd, "   -u\t\t\tenable ~user/public_html support");
+	bozowarn(httpd, "   -p dir\t\tchange `public_html' directory name");
+#ifndef NO_CGIBIN_SUPPORT
+	bozowarn(httpd, "   -E\t\t\tenable CGI support for user dirs");
 #endif
-#ifndef NO_DYNAMIC_CONTENT
-	bozo_warn(httpd, "   -M arg t c c11\tadd this mime extenstion");
 #endif
 #ifndef NO_CGIBIN_SUPPORT
 #ifndef NO_DYNAMIC_CONTENT
-	bozo_warn(httpd, "   -C arg prog\t\tadd this CGI handler");
+	bozowarn(httpd, "   -C arg prog\t\tadd this CGI handler");
 #endif
-	bozo_warn(httpd,
+	bozowarn(httpd,
 		"   -c cgibin\t\tenable cgi-bin support in this directory");
 #endif
 #ifndef NO_LUA_SUPPORT
-	bozo_warn(httpd, "   -L arg script\tadd this Lua script");
+	bozowarn(httpd, "   -L arg script\tadd this Lua script");
 #endif
-	bozo_warn(httpd, "   -I port\t\tbind or use on this port");
+	bozowarn(httpd, "   -I port\t\tbind or use on this port");
 #ifndef NO_DAEMON_MODE
-	bozo_warn(httpd, "   -b\t\t\tbackground and go into daemon mode");
-	bozo_warn(httpd, "   -f\t\t\tkeep daemon mode in the foreground");
-	bozo_warn(httpd,
+	bozowarn(httpd, "   -b\t\t\tbackground and go into daemon mode");
+	bozowarn(httpd, "   -f\t\t\tkeep daemon mode in the foreground");
+	bozowarn(httpd,
 		"   -i address\t\tbind on this address (daemon mode only)");
-	bozo_warn(httpd, "   -P pidfile\t\tpath to the pid file to create");
+	bozowarn(httpd, "   -P pidfile\t\tpath to the pid file to create");
 #endif
-	bozo_warn(httpd, "   -S version\t\tset server version string");
-	bozo_warn(httpd, "   -t dir\t\tchroot to `dir'");
-	bozo_warn(httpd, "   -U username\t\tchange user to `user'");
-	bozo_warn(httpd,
+	bozowarn(httpd, "   -S version\t\tset server version string");
+	bozowarn(httpd, "   -t dir\t\tchroot to `dir'");
+	bozowarn(httpd, "   -U username\t\tchange user to `user'");
+	bozowarn(httpd,
 		"   -e\t\t\tdon't clean the environment (-t and -U only)");
-	bozo_warn(httpd,
+	bozowarn(httpd,
 		"   -v virtualroot\tenable virtual host support "
 		"in this directory");
-	bozo_warn(httpd,
-		"   -r\t\t\tmake sure sub-pages come from "
-		"this host via referrer");
 #ifndef NO_DIRINDEX_SUPPORT
-	bozo_warn(httpd,
+	bozowarn(httpd,
 		"   -X\t\t\tenable automatic directory index support");
-	bozo_warn(httpd,
+	bozowarn(httpd,
 		"   -H\t\t\thide files starting with a period (.)"
 		" in index mode");
 #endif
-	bozo_warn(httpd,
+	bozowarn(httpd,
 		"   -x index\t\tchange default `index.html' file name");
 #ifndef NO_SSL_SUPPORT
-	bozo_warn(httpd,
+	bozowarn(httpd,
+		"   -z ciphers\t\tspecify SSL ciphers");
+	bozowarn(httpd,
 		"   -Z cert privkey\tspecify path to server certificate"
 			" and private key file\n"
 		"\t\t\tin pem format and enable bozohttpd in SSL mode");
 #endif /* NO_SSL_SUPPORT */
-	bozo_err(httpd, 1, "%s failed to start", progname);
+	bozoerr(httpd, 1, "%s failed to start", progname);
 }
 
 int
@@ -126,6 +128,7 @@ main(int argc, char **argv)
 	bozohttpd_t	 httpd;
 	bozoprefs_t	 prefs;
 	char		*progname;
+	const char	*val;
 	int		 c;
 
 	(void) memset(&httpd, 0x0, sizeof(httpd));
@@ -140,13 +143,17 @@ main(int argc, char **argv)
 
 	bozo_set_defaults(&httpd, &prefs);
 
+	/*
+	 * -r option was removed, do not reuse it for a while
+	 */
+
 	while ((c = getopt(argc, argv,
-	    "C:HI:L:M:P:S:U:VXZ:bc:defhi:np:rst:uv:x:z:")) != -1) {
-		switch(c) {
+	    "C:EHI:L:M:P:S:U:VXZ:bc:defhi:np:st:uv:x:z:")) != -1) {
+		switch (c) {
 
 		case 'L':
 #ifdef NO_LUA_SUPPORT
-			bozo_err(&httpd, 1,
+			bozoerr(&httpd, 1,
 				"Lua support is not enabled");
 			/* NOTREACHED */
 #else
@@ -159,7 +166,7 @@ main(int argc, char **argv)
 #endif /* NO_LUA_SUPPORT */
 		case 'M':
 #ifdef NO_DYNAMIC_CONTENT
-			bozo_err(&httpd, 1,
+			bozoerr(&httpd, 1,
 				"dynamic mime content support is not enabled");
 			/* NOTREACHED */
 #else
@@ -173,23 +180,20 @@ main(int argc, char **argv)
 #endif /* NO_DYNAMIC_CONTENT */
 
 		case 'n':
-			bozo_set_pref(&prefs, "numeric", "true");
-			break;
-
-		case 'r':
-			bozo_set_pref(&prefs, "trusted referal", "true");
+			bozo_set_pref(&httpd, &prefs, "numeric", "true");
 			break;
 
 		case 's':
-			bozo_set_pref(&prefs, "log to stderr", "true");
+			bozo_set_pref(&httpd, &prefs, "log to stderr", "true");
 			break;
 
 		case 'S':
-			bozo_set_pref(&prefs, "server software", optarg);
+			bozo_set_pref(&httpd, &prefs, "server software",
+				      optarg);
 			break;
 		case 'Z':
 #ifdef NO_SSL_SUPPORT
-			bozo_err(&httpd, 1, "ssl support is not enabled");
+			bozoerr(&httpd, 1, "ssl support is not enabled");
 			/* NOT REACHED */
 #else
 			/* make sure there's two arguments */
@@ -198,24 +202,34 @@ main(int argc, char **argv)
 			bozo_ssl_set_opts(&httpd, optarg, argv[optind++]);
 			break;
 #endif /* NO_SSL_SUPPORT */
+
+		case 'z':
+#ifdef NO_SSL_SUPPORT
+			bozoerr(&httpd, 1, "ssl support is not enabled");
+			/* NOT REACHED */
+#else
+			bozo_ssl_set_ciphers(&httpd, optarg);
+			break;
+#endif /* NO_SSL_SUPPORT */
+
 		case 'U':
-			bozo_set_pref(&prefs, "username", optarg);
+			bozo_set_pref(&httpd, &prefs, "username", optarg);
 			break;
 
 		case 'V':
-			bozo_set_pref(&prefs, "unknown slash", "true");
+			bozo_set_pref(&httpd, &prefs, "unknown slash", "true");
 			break;
 
 		case 'v':
-			bozo_set_pref(&prefs, "virtual base", optarg);
+			bozo_set_pref(&httpd, &prefs, "virtual base", optarg);
 			break;
 
 		case 'x':
-			bozo_set_pref(&prefs, "index.html", optarg);
+			bozo_set_pref(&httpd, &prefs, "index.html", optarg);
 			break;
 
 		case 'I':
-			bozo_set_pref(&prefs, "port number", optarg);
+			bozo_set_pref(&httpd, &prefs, "port number", optarg);
 			break;
 
 #ifdef NO_DAEMON_MODE
@@ -224,7 +238,7 @@ main(int argc, char **argv)
 		case 'f':
 		case 'i':
 		case 'P':
-			bozo_err(&httpd, 1, "Daemon mode is not enabled");
+			bozoerr(&httpd, 1, "Daemon mode is not enabled");
 			/* NOTREACHED */
 #else
 		case 'b':
@@ -233,34 +247,33 @@ main(int argc, char **argv)
 			 * background == 2 (aka, -b -b) means to
 			 * only process 1 per kid
 			 */
-			if (bozo_get_pref(&prefs, "background") == NULL) {
-				bozo_set_pref(&prefs, "background", "1");
-			} else {
-				bozo_set_pref(&prefs, "background", "2");
-			}
+			val = bozo_get_pref(&prefs, "background") == NULL ?
+			    "1" : "2";
+			bozo_set_pref(&httpd, &prefs, "background", val);
 			break;
 
 		case 'e':
-			bozo_set_pref(&prefs, "dirty environment", "true");
+			bozo_set_pref(&httpd, &prefs, "dirty environment",
+				      "true");
 			break;
 
 		case 'f':
-			bozo_set_pref(&prefs, "foreground", "true");
+			bozo_set_pref(&httpd, &prefs, "foreground", "true");
 			break;
 
 		case 'i':
-			bozo_set_pref(&prefs, "bind address", optarg);
+			bozo_set_pref(&httpd, &prefs, "bind address", optarg);
 			break;
 
 		case 'P':
-			bozo_set_pref(&prefs, "pid file", optarg);
+			bozo_set_pref(&httpd, &prefs, "pid file", optarg);
 			break;
 #endif /* NO_DAEMON_MODE */
 
 #ifdef NO_CGIBIN_SUPPORT
 		case 'c':
 		case 'C':
-			bozo_err(&httpd, 1, "CGI is not enabled");
+			bozoerr(&httpd, 1, "CGI is not enabled");
 			/* NOTREACHED */
 #else
 		case 'c':
@@ -269,7 +282,7 @@ main(int argc, char **argv)
 
 		case 'C':
 #  ifdef NO_DYNAMIC_CONTENT
-			bozo_err(&httpd, 1,
+			bozoerr(&httpd, 1,
 				"dynamic CGI handler support is not enabled");
 			/* NOTREACHED */
 #  else
@@ -286,42 +299,54 @@ main(int argc, char **argv)
 			httpd.debug++;
 #ifdef NO_DEBUG
 			if (httpd.debug == 1)
-				bozo_warn(&httpd, "Debugging is not enabled");
+				bozowarn(&httpd, "Debugging is not enabled");
 #endif /* NO_DEBUG */
 			break;
 
 		case 't':
-			bozo_set_pref(&prefs, "chroot dir", optarg);
+			bozo_set_pref(&httpd, &prefs, "chroot dir", optarg);
 			break;
 
 #ifdef NO_USER_SUPPORT
 		case 'p':
 		case 'u':
-			bozo_err(&httpd, 1, "User support is not enabled");
+		case 'E':
+			bozoerr(&httpd, 1, "User support is not enabled");
 			/* NOTREACHED */
 #else
 		case 'p':
-			bozo_set_pref(&prefs, "public_html", optarg);
+			bozo_set_pref(&httpd, &prefs, "public_html", optarg);
 			break;
 
 		case 'u':
-			bozo_set_pref(&prefs, "enable users", "true");
+			bozo_set_pref(&httpd, &prefs, "enable users", "true");
+			break;
+#ifndef NO_CGIBIN_SUPPORT
+		case 'E':
+			bozo_set_pref(&httpd, &prefs, "enable user cgibin",
+				      "true");
 			break;
+#else
+		case 'E':
+			bozoerr(&httpd, 1, "CGI is not enabled");
+			/* NOTREACHED */
+#endif /* NO_CGIBIN_SPPORT */
 #endif /* NO_USER_SUPPORT */
 
 #ifdef NO_DIRINDEX_SUPPORT
 		case 'H':
 		case 'X':
-			bozo_err(&httpd, 1,
+			bozoerr(&httpd, 1,
 				"directory indexing is not enabled");
 			/* NOTREACHED */
 #else
 		case 'H':
-			bozo_set_pref(&prefs, "hide dots", "true");
+			bozo_set_pref(&httpd, &prefs, "hide dots", "true");
 			break;
 
 		case 'X':
-			bozo_set_pref(&prefs, "directory indexing", "true");
+			bozo_set_pref(&httpd, &prefs, "directory indexing",
+				      "true");
 			break;
 
 #endif /* NO_DIRINDEX_SUPPORT */
Index: printenv.lua
===================================================================
RCS file: /cvsroot/src/libexec/httpd/printenv.lua,v
retrieving revision 1.2
diff -p -u -u -r1.2 printenv.lua
--- printenv.lua	2 Jan 2014 08:21:38 -0000	1.2
+++ printenv.lua	15 Apr 2016 08:15:13 -0000
@@ -8,6 +8,10 @@
 -- the same value on each invocation.  You can not keep state between
 -- two calls.
 
+-- You can test this example by running the following command:
+-- /usr/libexec/httpd -b -f -I 8080 -L test printenv.lua .
+-- and then navigate to: http://127.0.0.1:8080/test/printenv
+
 local httpd = require 'httpd'
 
 function printenv(env, headers, query)
@@ -15,12 +19,14 @@ function printenv(env, headers, query)
 	-- we get the "environment" in the env table, the values are more
 	-- or less the same as the variable for a CGI program
 
-	if count == nil then
-		count = 1
-	end
-
-	-- output a header
-	print([[
+	-- output headers using httpd.write()
+	-- httpd.write() will not append newlines
+	httpd.write("HTTP/1.1 200 Ok\r\n")
+	httpd.write("Content-Type: text/html\r\n\r\n")
+
+	-- output html using httpd.print()
+	-- you can also use print() and io.write() but they will not work with SSL
+	httpd.print([[
 		
 			
 				Bozotic Lua Environment
@@ -29,54 +35,58 @@ function printenv(env, headers, query)
 				

Bozotic Lua Environment

]]) - print('module version: ' .. httpd._VERSION .. '
') + httpd.print('module version: ' .. httpd._VERSION .. '
') - print('

Server Environment

') + httpd.print('

Server Environment

') -- print the list of "environment" variables for k, v in pairs(env) do - print(k .. '=' .. v .. '
') + httpd.print(k .. '=' .. v .. '
') end - print('

Request Headers

') + httpd.print('

Request Headers

') for k, v in pairs(headers) do - print(k .. '=' .. v .. '
') + httpd.print(k .. '=' .. v .. '
') end if query ~= nil then - print('

Query Variables

') + httpd.print('

Query Variables

') for k, v in pairs(query) do - print(k .. '=' .. v .. '
') + httpd.print(k .. '=' .. v .. '
') end end - print('

Form Test

') + httpd.print('

Form Test

') - print([[ -
+ httpd.print([[ +
]]) -- output a footer - print([[ + httpd.print([[ ]]) end function form(env, header, query) + + httpd.write("HTTP/1.1 200 Ok\r\n") + httpd.write("Content-Type: text/html\r\n\r\n") + if query ~= nil then - print('

Form Variables

') + httpd.print('

Form Variables

') if env.CONTENT_TYPE ~= nil then - print('Content-type: ' .. env.CONTENT_TYPE .. '
') + httpd.print('Content-type: ' .. env.CONTENT_TYPE .. '
') end for k, v in pairs(query) do - print(k .. '=' .. v .. '
') + httpd.print(k .. '=' .. v .. '
') end else - print('No values') + httpd.print('No values') end end Index: ssl-bozo.c =================================================================== RCS file: /cvsroot/src/libexec/httpd/ssl-bozo.c,v retrieving revision 1.18 diff -p -u -u -r1.18 ssl-bozo.c --- ssl-bozo.c 17 Jul 2014 06:27:52 -0000 1.18 +++ ssl-bozo.c 15 Apr 2016 08:15:13 -0000 @@ -48,6 +48,25 @@ #define USE_ARG(x) /*LINTED*/(void)&(x) #endif +#ifndef BOZO_SSL_CIPHERS +#define BOZO_SSL_CIPHERS \ + "AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:" \ + "AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:" \ + "AES:" \ + "-SHA:" \ + "!aNULL:!eNULL:" \ + "!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" \ + "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:" \ + "!KRB5-DES-CBC3-SHA" +#endif + +#ifndef BOZO_SSL_OPTIONS +#define BOZO_SSL_OPTIONS \ + (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1) +#endif + + /* this structure encapsulates the ssl info */ + /* this structure encapsulates the ssl info */ typedef struct sslinfo_t { SSL_CTX *ssl_context; @@ -55,6 +74,7 @@ typedef struct sslinfo_t { SSL *bozossl; char *certificate_file; char *privatekey_file; + char *ciphers; } sslinfo_t; /* @@ -83,7 +103,7 @@ bozo_clear_ssl_queue(bozohttpd_t *httpd) } /* - * bozo_ssl_warn works just like bozo_warn, plus the SSL error queue + * bozo_ssl_warn works just like bozowarn, plus the SSL error queue */ BOZO_PRINTFLIKE(2, 3) static void bozo_ssl_warn(bozohttpd_t *httpd, const char *fmt, ...) @@ -103,7 +123,7 @@ bozo_ssl_warn(bozohttpd_t *httpd, const /* - * bozo_ssl_err works just like bozo_err, plus the SSL error queue + * bozo_ssl_err works just like bozoerr, plus the SSL error queue */ BOZO_PRINTFLIKE(3, 4) BOZO_DEAD static void bozo_ssl_err(bozohttpd_t *httpd, int code, const char *fmt, ...) @@ -187,6 +207,7 @@ void bozo_ssl_init(bozohttpd_t *httpd) { sslinfo_t *sslinfo = httpd->sslinfo; + long options; if (sslinfo == NULL || !sslinfo->certificate_file) return; @@ -200,6 +221,18 @@ bozo_ssl_init(bozohttpd_t *httpd) bozo_ssl_err(httpd, EXIT_FAILURE, "SSL context creation failed"); + options = SSL_CTX_set_options(sslinfo->ssl_context, + BOZO_SSL_OPTIONS); + if ((options & BOZO_SSL_OPTIONS) != BOZO_SSL_OPTIONS) + bozo_ssl_err(httpd, EXIT_FAILURE, + "Error setting ssl options requested %#lx, got %#lx", + BOZO_SSL_OPTIONS, options); + + if (!SSL_CTX_set_cipher_list(sslinfo->ssl_context, + sslinfo->ciphers ? sslinfo->ciphers : BOZO_SSL_CIPHERS)) + bozo_ssl_err(httpd, EXIT_FAILURE, + "Error setting cipher list '%s'", sslinfo->ciphers); + if (1 != SSL_CTX_use_certificate_chain_file(sslinfo->ssl_context, sslinfo->certificate_file)) bozo_ssl_err(httpd, EXIT_FAILURE, @@ -231,7 +264,7 @@ bozo_ssl_accept(bozohttpd_t *httpd) sslinfo->bozossl = SSL_new(sslinfo->ssl_context); if (sslinfo->bozossl == NULL) - bozo_err(httpd, 1, "SSL_new failed"); + bozoerr(httpd, 1, "SSL_new failed"); SSL_set_rfd(sslinfo->bozossl, 0); SSL_set_wfd(sslinfo->bozossl, 1); @@ -251,24 +284,40 @@ bozo_ssl_destroy(bozohttpd_t *httpd) SSL_free(sslinfo->bozossl); } +static sslinfo_t * +bozo_get_sslinfo(bozohttpd_t *httpd) +{ + sslinfo_t *sslinfo; + if (httpd->sslinfo) + return httpd->sslinfo; + sslinfo = bozomalloc(httpd, sizeof(*sslinfo)); + if (sslinfo == NULL) + bozoerr(httpd, 1, "sslinfo allocation failed"); + memset(sslinfo, 0, sizeof(*sslinfo)); + return httpd->sslinfo = sslinfo; +} + void bozo_ssl_set_opts(bozohttpd_t *httpd, const char *cert, const char *priv) { - sslinfo_t *sslinfo = httpd->sslinfo; + sslinfo_t *sslinfo = bozo_get_sslinfo(httpd); - if (sslinfo == NULL) { - sslinfo = bozomalloc(httpd, sizeof(*sslinfo)); - if (sslinfo == NULL) - bozo_err(httpd, 1, "sslinfo allocation failed"); - httpd->sslinfo = sslinfo; - } - sslinfo->certificate_file = strdup(cert); - sslinfo->privatekey_file = strdup(priv); + sslinfo->certificate_file = bozostrdup(httpd, NULL, cert); + sslinfo->privatekey_file = bozostrdup(httpd, NULL, priv); debug((httpd, DEBUG_NORMAL, "using cert/priv files: %s & %s", - sslinfo->certificate_file, - sslinfo->privatekey_file)); + sslinfo->certificate_file, + sslinfo->privatekey_file)); if (!httpd->bindport) - httpd->bindport = strdup("https"); + httpd->bindport = bozostrdup(httpd, NULL, "https"); +} + +void +bozo_ssl_set_ciphers(bozohttpd_t *httpd, const char *ciphers) +{ + sslinfo_t *sslinfo = bozo_get_sslinfo(httpd); + + sslinfo->ciphers = bozostrdup(httpd, NULL, ciphers); + debug((httpd, DEBUG_NORMAL, "using ciphers: %s", sslinfo->ciphers)); } #endif /* NO_SSL_SUPPORT */ Index: tilde-luzah-bozo.c =================================================================== RCS file: /cvsroot/src/libexec/httpd/tilde-luzah-bozo.c,v retrieving revision 1.10 diff -p -u -u -r1.10 tilde-luzah-bozo.c --- tilde-luzah-bozo.c 2 Jan 2014 08:21:38 -0000 1.10 +++ tilde-luzah-bozo.c 15 Apr 2016 08:15:13 -0000 @@ -36,6 +36,7 @@ #include +#include #include #include #include @@ -55,27 +56,40 @@ * enabled. */ int -bozo_user_transform(bozo_httpreq_t *request, int *isindex) +bozo_user_transform(bozo_httpreq_t *request) { bozohttpd_t *httpd = request->hr_httpd; - char c, *s, *file = NULL; + char *s, *file = NULL, *user; struct passwd *pw; - *isindex = 0; + /* find username */ + user = strchr(request->hr_file + 1, '~'); - if ((s = strchr(request->hr_file + 2, '/')) != NULL) { + /* this shouldn't happen, but "better paranoid than sorry" */ + assert(user != NULL); + + user++; + + if ((s = strchr(user, '/')) != NULL) { *s++ = '\0'; - c = s[strlen(s)-1]; - *isindex = (c == '/' || c == '\0'); } debug((httpd, DEBUG_OBESE, "looking for user %s", - request->hr_file + 2)); - pw = getpwnam(request->hr_file + 2); + user)); + pw = getpwnam(user); + request->hr_user = bozostrdup(httpd, request, user); + /* fix this up immediately */ - if (s) + if (s) { s[-1] = '/'; + /* omit additional slashes at the beginning */ + while (*s == '/') + s++; + } + if (pw == NULL) { + free(request->hr_user); + request->hr_user = NULL; (void)bozo_http_error(httpd, 404, request, "no such user"); return 0; } @@ -85,40 +99,25 @@ bozo_user_transform(bozo_httpreq_t *requ pw->pw_uid, pw->pw_gid)); if (chdir(pw->pw_dir) < 0) { - bozo_warn(httpd, "chdir1 error: %s: %s", pw->pw_dir, + bozowarn(httpd, "chdir1 error: %s: %s", pw->pw_dir, strerror(errno)); (void)bozo_http_error(httpd, 404, request, "can't chdir to homedir"); return 0; } if (chdir(httpd->public_html) < 0) { - bozo_warn(httpd, "chdir2 error: %s: %s", httpd->public_html, + bozowarn(httpd, "chdir2 error: %s: %s", httpd->public_html, strerror(errno)); (void)bozo_http_error(httpd, 404, request, "can't chdir to public_html"); return 0; } if (s == NULL || *s == '\0') { - file = bozostrdup(httpd, httpd->index_html); + file = bozostrdup(httpd, request, "/"); } else { - file = bozomalloc(httpd, strlen(s) + - (*isindex ? strlen(httpd->index_html) + 1 : 1)); - strcpy(file, s); - if (*isindex) - strcat(file, httpd->index_html); - } - - /* see transform_request() */ - if (*file == '/' || strcmp(file, "..") == 0 || - strstr(file, "/..") || strstr(file, "../")) { - (void)bozo_http_error(httpd, 403, request, "illegal request"); - free(file); - return 0; - } - - if (bozo_auth_check(request, file)) { - free(file); - return 0; + file = bozomalloc(httpd, strlen(s) + 2); + strcpy(file, "/"); + strcat(file, s); } free(request->hr_file); Index: testsuite/Makefile =================================================================== RCS file: /cvsroot/src/libexec/httpd/testsuite/Makefile,v retrieving revision 1.4 diff -p -u -u -r1.4 Makefile --- testsuite/Makefile 23 May 2009 02:26:03 -0000 1.4 +++ testsuite/Makefile 15 Apr 2016 08:15:13 -0000 @@ -6,7 +6,7 @@ BIGFILETESTS= partial4000 partial8000 BOZOHTTPD?= ../bozohttpd BOZOHTTPD?= ../debug/bozohttpd-debug WGET?= wget - +DATA?= $(.CURDIR)/data all: clean: @@ -19,14 +19,14 @@ check: check-simple check-bigfile check-simple: .for a in $(SIMPLETESTS) echo "Running test $a" - $(BOZOHTTPD) ./data < $(.CURDIR)/$a.in > tmp.$a.out || true + $(BOZOHTTPD) "$(DATA)" < $(.CURDIR)/$a.in > tmp.$a.out || true $(.CURDIR)/html_cmp $(.CURDIR)/$a.out tmp.$a.out .endfor check-bigfile: .for a in $(BIGFILETESTS) echo "Running test $a" - $(.CURDIR)/test-bigfile "$a" "${BOZOHTTPD}" "${WGET}" "./data" + $(.CURDIR)/test-bigfile "$a" "${BOZOHTTPD}" "${WGET}" "$(DATA)" .endfor .include Index: testsuite/test-bigfile =================================================================== RCS file: /cvsroot/src/libexec/httpd/testsuite/test-bigfile,v retrieving revision 1.1.1.1 diff -p -u -u -r1.1.1.1 test-bigfile --- testsuite/test-bigfile 23 May 2009 02:21:19 -0000 1.1.1.1 +++ testsuite/test-bigfile 15 Apr 2016 08:15:13 -0000 @@ -8,7 +8,7 @@ datadir="$4" bozotestport=11111 # copy beginning file -cp ./data/bigfile.${test} ./bigfile +cp ${datadir}/bigfile.${test} ./bigfile # fire up bozohttpd ${bozohttpd} -b -b -I ${bozotestport} -n -s -f ${datadir} & @@ -18,7 +18,7 @@ ${wget} -c http://localhost:${bozotestpo kill -9 $bozopid -if cmp ./bigfile ./data/bigfile; then +if cmp ./bigfile ${datadir}/bigfile; then rm -f ./bigfile exit 0 else