version 1.25.2.2.2.8, 2019/06/15 15:57:32 |
version 1.25.2.3, 2016/04/10 10:33:11 |
|
|
/* $eterna: cgi-bozo.c,v 1.40 2011/11/18 09:21:15 mrg Exp $ */ |
/* $eterna: cgi-bozo.c,v 1.40 2011/11/18 09:21:15 mrg Exp $ */ |
|
|
/* |
/* |
* Copyright (c) 1997-2019 Matthew R. Green |
* Copyright (c) 1997-2015 Matthew R. Green |
* All rights reserved. |
* All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
|
|
#include <string.h> |
#include <string.h> |
#include <syslog.h> |
#include <syslog.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <assert.h> |
|
|
|
#include <netinet/in.h> |
#include <netinet/in.h> |
|
|
|
|
*/ |
*/ |
static const char * |
static const char * |
content_cgihandler(bozohttpd_t *httpd, bozo_httpreq_t *request, |
content_cgihandler(bozohttpd_t *httpd, bozo_httpreq_t *request, |
const char *file) |
const char *file) |
{ |
{ |
bozo_content_map_t *map; |
bozo_content_map_t *map; |
|
|
Line 104 parse_header(bozo_httpreq_t *request, co |
|
Line 103 parse_header(bozo_httpreq_t *request, co |
|
*hdr_val = value; |
*hdr_val = value; |
|
|
return 0; |
return 0; |
} |
} |
|
|
/* |
/* |
* handle parsing a CGI header output, transposing a Status: header |
* handle parsing a CGI header output, transposing a Status: header |
Line 124 finish_cgi_output(bozohttpd_t *httpd, bo |
|
Line 123 finish_cgi_output(bozohttpd_t *httpd, bo |
|
/* much of this code is like bozo_read_request()'s header loop. */ |
/* much of this code is like bozo_read_request()'s header loop. */ |
SIMPLEQ_INIT(&headers); |
SIMPLEQ_INIT(&headers); |
write_header = nph == 0; |
write_header = nph == 0; |
|
/* was read(2) here - XXX - agc */ |
while (nph == 0 && |
while (nph == 0 && |
(str = bozodgetln(httpd, in, &len, bozo_read)) != NULL) { |
(str = bozodgetln(httpd, in, &len, bozo_read)) != NULL) { |
char *hdr_name, *hdr_value; |
char *hdr_name, *hdr_value; |
Line 144 finish_cgi_output(bozohttpd_t *httpd, bo |
|
Line 144 finish_cgi_output(bozohttpd_t *httpd, bo |
|
*/ |
*/ |
if (strcasecmp(hdr_name, "status") == 0) { |
if (strcasecmp(hdr_name, "status") == 0) { |
debug((httpd, DEBUG_OBESE, |
debug((httpd, DEBUG_OBESE, |
"%s: writing HTTP header " |
"bozo_process_cgi: writing HTTP header " |
"from status %s ..", __func__, hdr_value)); |
"from status %s ..", hdr_value)); |
bozo_printf(httpd, "%s %s\r\n", request->hr_proto, |
bozo_printf(httpd, "%s %s\r\n", request->hr_proto, |
hdr_value); |
hdr_value); |
bozo_flush(httpd, stdout); |
bozo_flush(httpd, stdout); |
write_header = 0; |
write_header = 0; |
free(hdr_name); |
free(hdr_name); |
Line 163 finish_cgi_output(bozohttpd_t *httpd, bo |
|
Line 163 finish_cgi_output(bozohttpd_t *httpd, bo |
|
|
|
if (write_header) { |
if (write_header) { |
debug((httpd, DEBUG_OBESE, |
debug((httpd, DEBUG_OBESE, |
"%s: writing HTTP header ..", __func__)); |
"bozo_process_cgi: writing HTTP header ..")); |
bozo_printf(httpd, |
bozo_printf(httpd, |
"%s 200 OK\r\n", request->hr_proto); |
"%s 200 OK\r\n", request->hr_proto); |
bozo_flush(httpd, stdout); |
bozo_flush(httpd, stdout); |
Line 171 finish_cgi_output(bozohttpd_t *httpd, bo |
|
Line 171 finish_cgi_output(bozohttpd_t *httpd, bo |
|
|
|
if (nheaders) { |
if (nheaders) { |
debug((httpd, DEBUG_OBESE, |
debug((httpd, DEBUG_OBESE, |
"%s: writing delayed HTTP headers ..", __func__)); |
"bozo_process_cgi: writing delayed HTTP headers ..")); |
SIMPLEQ_FOREACH_SAFE(hdr, &headers, h_next, nhdr) { |
SIMPLEQ_FOREACH_SAFE(hdr, &headers, h_next, nhdr) { |
bozo_printf(httpd, "%s: %s\r\n", hdr->h_header, |
bozo_printf(httpd, "%s: %s\r\n", hdr->h_header, |
hdr->h_value); |
hdr->h_value); |
free(hdr->h_header); |
free(hdr->h_header); |
free(hdr); |
free(hdr); |
} |
} |
Line 190 finish_cgi_output(bozohttpd_t *httpd, bo |
|
Line 190 finish_cgi_output(bozohttpd_t *httpd, bo |
|
|
|
while (rbytes) { |
while (rbytes) { |
wbytes = bozo_write(httpd, STDOUT_FILENO, buf, |
wbytes = bozo_write(httpd, STDOUT_FILENO, buf, |
(size_t)rbytes); |
(size_t)rbytes); |
if (wbytes > 0) { |
if (wbytes > 0) { |
rbytes -= wbytes; |
rbytes -= wbytes; |
bp += wbytes; |
bp += wbytes; |
Line 212 append_index_html(bozohttpd_t *httpd, ch |
|
Line 212 append_index_html(bozohttpd_t *httpd, ch |
|
"append_index_html: url adjusted to `%s'", *url)); |
"append_index_html: url adjusted to `%s'", *url)); |
} |
} |
|
|
/* This function parse search-string according to section 4.4 of RFC3875 */ |
|
static char ** |
|
parse_search_string(bozo_httpreq_t *request, const char *query, size_t *args_len) |
|
{ |
|
struct bozohttpd_t *httpd = request->hr_httpd; |
|
size_t i; |
|
char *s, *str, **args; |
|
|
|
*args_len = 0; |
|
|
|
/* URI MUST not contain any unencoded '=' - RFC3875, section 4.4 */ |
|
if (strchr(query, '=')) |
|
return NULL; |
|
|
|
str = bozostrdup(httpd, request, query); |
|
|
|
/* |
|
* there's no more arguments than '+' chars in the query string as it's |
|
* the separator |
|
*/ |
|
*args_len = 1; |
|
/* count '+' in str */ |
|
for (s = str; (s = strchr(s, '+')) != NULL; (*args_len)++) |
|
s++; |
|
|
|
args = bozomalloc(httpd, sizeof(*args) * (*args_len + 1)); |
|
|
|
args[0] = str; |
|
args[*args_len] = NULL; |
|
for (s = str, i = 1; (s = strchr(s, '+')) != NULL; i++) { |
|
*s = '\0'; |
|
s++; |
|
args[i] = s; |
|
} |
|
|
|
/* |
|
* check if search-strings are valid: |
|
* |
|
* RFC3875, section 4.4: |
|
* |
|
* search-string = search-word *( "+" search-word ) |
|
* search-word = 1*schar |
|
* schar = unreserved | escaped | xreserved |
|
* xreserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "," | |
|
* "$" |
|
* |
|
* section 2.3: |
|
* |
|
* hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | "a" | |
|
* "b" | "c" | "d" | "e" | "f" |
|
* escaped = "%" hex hex |
|
* unreserved = alpha | digit | mark |
|
* mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" |
|
* |
|
* section 2.2: |
|
* |
|
* alpha = lowalpha | hialpha |
|
* lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | |
|
* "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | |
|
* "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | |
|
* "y" | "z" |
|
* hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | |
|
* "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | |
|
* "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | |
|
* "Y" | "Z" |
|
* digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | |
|
* "8" | "9" |
|
*/ |
|
#define UNRESERVED_CHAR "-_.!~*'()" |
|
#define XRESERVED_CHAR ";/?:@&=,$" |
|
|
|
for (i = 0; i < *args_len; i++) { |
|
s = args[i]; |
|
/* search-word MUST have at least one schar */ |
|
if (*s == '\0') |
|
goto parse_err; |
|
while (*s) { |
|
/* check if it's unreserved */ |
|
if (isalpha((int)*s) || isdigit((int)*s) || |
|
strchr(UNRESERVED_CHAR, *s)) { |
|
s++; |
|
continue; |
|
} |
|
|
|
/* check if it's escaped */ |
|
if (*s == '%') { |
|
if (s[1] == '\0' || s[2] == '\0') |
|
goto parse_err; |
|
if (!isxdigit((int)s[1]) || |
|
!isxdigit((int)s[2])) |
|
goto parse_err; |
|
s += 3; |
|
continue; |
|
} |
|
|
|
/* check if it's xreserved */ |
|
|
|
if (strchr(XRESERVED_CHAR, *s)) { |
|
s++; |
|
continue; |
|
} |
|
|
|
goto parse_err; |
|
} |
|
} |
|
|
|
/* decode percent encoding */ |
|
for (i = 0; i < *args_len; i++) { |
|
if (bozo_decode_url_percent(request, args[i])) |
|
goto parse_err; |
|
} |
|
|
|
/* allocate each arg separately */ |
|
for (i = 0; i < *args_len; i++) |
|
args[i] = bozostrdup(httpd, request, args[i]); |
|
free(str); |
|
|
|
return args; |
|
|
|
parse_err: |
|
|
|
free(str); |
|
free(args); |
|
*args_len = 0; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
void |
void |
bozo_cgi_setbin(bozohttpd_t *httpd, const char *path) |
bozo_cgi_setbin(bozohttpd_t *httpd, const char *path) |
{ |
{ |
httpd->cgibin = bozostrdup(httpd, NULL, path); |
httpd->cgibin = bozostrdup(httpd, NULL, path); |
debug((httpd, DEBUG_OBESE, "cgibin (cgi-bin directory) is %s", |
debug((httpd, DEBUG_OBESE, "cgibin (cgi-bin directory) is %s", |
httpd->cgibin)); |
httpd->cgibin)); |
} |
} |
|
|
/* help build up the environ pointer */ |
/* help build up the environ pointer */ |
Line 378 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 249 bozo_process_cgi(bozo_httpreq_t *request |
|
bozoheaders_t *headp; |
bozoheaders_t *headp; |
const char *type, *clen, *info, *cgihandler; |
const char *type, *clen, *info, *cgihandler; |
char *query, *s, *t, *path, *env, *command, *file, *url; |
char *query, *s, *t, *path, *env, *command, *file, *url; |
char **envp, **curenvp, **argv, **search_string_argv = NULL; |
char **envp, **curenvp, *argv[4]; |
char **lastenvp; |
|
char *uri; |
char *uri; |
size_t i, len, search_string_argc = 0; |
size_t len; |
ssize_t rbytes; |
ssize_t rbytes; |
pid_t pid; |
pid_t pid; |
int envpsize, ix, nph; |
int envpsize, ix, nph; |
Line 414 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 284 bozo_process_cgi(bozo_httpreq_t *request |
|
file, |
file, |
query ? "?" : "", |
query ? "?" : "", |
query ? query : ""); |
query ? query : ""); |
debug((httpd, DEBUG_NORMAL, "%s: url `%s'", __func__, url)); |
debug((httpd, DEBUG_NORMAL, "bozo_process_cgi: url `%s'", url)); |
|
|
path = NULL; |
path = NULL; |
envp = NULL; |
envp = NULL; |
Line 432 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 302 bozo_process_cgi(bozo_httpreq_t *request |
|
cgihandler = content_cgihandler(httpd, request, file + 1); |
cgihandler = content_cgihandler(httpd, request, file + 1); |
if (cgihandler == NULL) { |
if (cgihandler == NULL) { |
debug((httpd, DEBUG_FAT, |
debug((httpd, DEBUG_FAT, |
"%s: no handler, returning", __func__)); |
"bozo_process_cgi: no handler, returning")); |
goto out; |
goto out; |
} |
} |
if (len == 0 || file[len - 1] == '/') |
if (len == 0 || file[len - 1] == '/') |
append_index_html(httpd, &file); |
append_index_html(httpd, &file); |
debug((httpd, DEBUG_NORMAL, "%s: cgihandler `%s'", |
debug((httpd, DEBUG_NORMAL, "bozo_process_cgi: cgihandler `%s'", |
__func__, cgihandler)); |
cgihandler)); |
} else if (len - 1 == CGIBIN_PREFIX_LEN) /* url is "/cgi-bin/" */ |
} else if (len - 1 == CGIBIN_PREFIX_LEN) /* url is "/cgi-bin/" */ |
append_index_html(httpd, &file); |
append_index_html(httpd, &file); |
|
|
/* RFC3875 sect. 4.4. - search-string support */ |
|
if (query != NULL) { |
|
search_string_argv = parse_search_string(request, query, |
|
&search_string_argc); |
|
} |
|
|
|
debug((httpd, DEBUG_NORMAL, "parse_search_string args no: %zu", |
|
search_string_argc)); |
|
for (i = 0; i < search_string_argc; i++) { |
|
debug((httpd, DEBUG_FAT, |
|
"search_string[%zu]: `%s'", i, search_string_argv[i])); |
|
} |
|
|
|
argv = bozomalloc(httpd, sizeof(*argv) * (3 + search_string_argc)); |
|
|
|
ix = 0; |
ix = 0; |
if (cgihandler) { |
if (cgihandler) { |
command = file + 1; |
command = file + 1; |
path = bozostrdup(httpd, request, cgihandler); |
path = bozostrdup(httpd, request, cgihandler); |
|
argv[ix++] = path; |
|
/* argv[] = [ path, command, query, NULL ] */ |
} else { |
} else { |
command = file + CGIBIN_PREFIX_LEN + 1; |
command = file + CGIBIN_PREFIX_LEN + 1; |
if ((s = strchr(command, '/')) != NULL) { |
if ((s = strchr(command, '/')) != NULL) { |
Line 472 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 329 bozo_process_cgi(bozo_httpreq_t *request |
|
strcpy(path, httpd->cgibin); |
strcpy(path, httpd->cgibin); |
strcat(path, "/"); |
strcat(path, "/"); |
strcat(path, command); |
strcat(path, command); |
|
/* argv[] = [ command, query, NULL ] */ |
} |
} |
|
argv[ix++] = command; |
argv[ix++] = path; |
argv[ix++] = query; |
|
|
/* copy search-string args */ |
|
for (i = 0; i < search_string_argc; i++) |
|
argv[ix++] = search_string_argv[i]; |
|
|
|
argv[ix++] = NULL; |
argv[ix++] = NULL; |
|
|
nph = strncmp(command, "nph-", 4) == 0; |
nph = strncmp(command, "nph-", 4) == 0; |
|
|
type = request->hr_content_type; |
type = request->hr_content_type; |
Line 493 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 347 bozo_process_cgi(bozo_httpreq_t *request |
|
(clen && *clen ? 1 : 0) + |
(clen && *clen ? 1 : 0) + |
(request->hr_remotehost && *request->hr_remotehost ? 1 : 0) + |
(request->hr_remotehost && *request->hr_remotehost ? 1 : 0) + |
(request->hr_remoteaddr && *request->hr_remoteaddr ? 1 : 0) + |
(request->hr_remoteaddr && *request->hr_remoteaddr ? 1 : 0) + |
(cgihandler ? 1 : 0) + |
|
bozo_auth_cgi_count(request) + |
bozo_auth_cgi_count(request) + |
(request->hr_serverport && *request->hr_serverport ? 1 : 0); |
(request->hr_serverport && *request->hr_serverport ? 1 : 0); |
|
|
debug((httpd, DEBUG_FAT, |
debug((httpd, DEBUG_FAT, |
"%s: path `%s', cmd `%s', info `%s', " |
"bozo_process_cgi: path `%s', cmd `%s', info `%s', " |
"query `%s', nph `%d', envpsize `%d'", __func__, |
"query `%s', nph `%d', envpsize `%d'", |
path, command, strornull(info), |
path, command, strornull(info), |
strornull(query), nph, envpsize)); |
strornull(query), nph, envpsize)); |
|
|
Line 507 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 360 bozo_process_cgi(bozo_httpreq_t *request |
|
for (ix = 0; ix < envpsize; ix++) |
for (ix = 0; ix < envpsize; ix++) |
envp[ix] = NULL; |
envp[ix] = NULL; |
curenvp = envp; |
curenvp = envp; |
lastenvp = envp + envpsize; |
|
|
|
SIMPLEQ_FOREACH(headp, &request->hr_headers, h_next) { |
SIMPLEQ_FOREACH(headp, &request->hr_headers, h_next) { |
const char *s2; |
const char *s2; |
Line 518 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 370 bozo_process_cgi(bozo_httpreq_t *request |
|
strcpy(t, "HTTP_"); |
strcpy(t, "HTTP_"); |
t += strlen(t); |
t += strlen(t); |
for (s2 = headp->h_header; *s2; t++, s2++) |
for (s2 = headp->h_header; *s2; t++, s2++) |
if (islower((unsigned)*s2)) |
if (islower((u_int)*s2)) |
*t = toupper((unsigned)*s2); |
*t = toupper((u_int)*s2); |
else if (*s2 == '-') |
else if (*s2 == '-') |
*t = '_'; |
*t = '_'; |
else |
else |
Line 548 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 400 bozo_process_cgi(bozo_httpreq_t *request |
|
bozo_setenv(httpd, "REQUEST_URI", uri, curenvp++); |
bozo_setenv(httpd, "REQUEST_URI", uri, curenvp++); |
bozo_setenv(httpd, "DATE_GMT", bozo_http_date(date, sizeof(date)), |
bozo_setenv(httpd, "DATE_GMT", bozo_http_date(date, sizeof(date)), |
curenvp++); |
curenvp++); |
/* RFC3875 section 4.1.7 says that QUERY_STRING MUST be defined. */ |
|
if (query && *query) |
if (query && *query) |
bozo_setenv(httpd, "QUERY_STRING", query, curenvp++); |
bozo_setenv(httpd, "QUERY_STRING", query, curenvp++); |
else |
|
bozo_setenv(httpd, "QUERY_STRING", "", curenvp++); |
|
if (info && *info) |
if (info && *info) |
bozo_setenv(httpd, "PATH_INFO", info, curenvp++); |
bozo_setenv(httpd, "PATH_INFO", info, curenvp++); |
if (type && *type) |
if (type && *type) |
Line 576 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 425 bozo_process_cgi(bozo_httpreq_t *request |
|
bozo_setenv(httpd, "REDIRECT_STATUS", "200", curenvp++); |
bozo_setenv(httpd, "REDIRECT_STATUS", "200", curenvp++); |
bozo_auth_cgi_setenv(request, &curenvp); |
bozo_auth_cgi_setenv(request, &curenvp); |
|
|
debug((httpd, DEBUG_FAT, "%s: going exec %s with args:", __func__, |
debug((httpd, DEBUG_FAT, "bozo_process_cgi: going exec %s, %s %s %s", |
path)); |
path, argv[0], strornull(argv[1]), strornull(argv[2]))); |
|
|
for (i = 0; argv[i] != NULL; i++) { |
|
debug((httpd, DEBUG_FAT, "%s: argv[%zu] = `%s'", __func__, |
|
i, argv[i])); |
|
} |
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sv) == -1) |
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sv) == -1) |
bozoerr(httpd, 1, "child socketpair failed: %s", |
bozoerr(httpd, 1, "child socketpair failed: %s", |
strerror(errno)); |
strerror(errno)); |
|
|
*curenvp = 0; |
|
assert(lastenvp > curenvp); |
|
|
|
/* |
/* |
* We create 2 procs: one to become the CGI, one read from |
* We create 2 procs: one to become the CGI, one read from |
* the CGI and output to the network, and this parent will |
* the CGI and output to the network, and this parent will |
Line 610 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 451 bozo_process_cgi(bozo_httpreq_t *request |
|
closelog(); |
closelog(); |
bozo_daemon_closefds(httpd); |
bozo_daemon_closefds(httpd); |
|
|
if (-1 == execve(path, argv, envp)) { |
if (-1 == execve(path, argv, envp)) |
bozo_http_error(httpd, 404, request, |
|
"Cannot execute CGI"); |
|
bozoerr(httpd, 1, "child exec failed: %s: %s", |
bozoerr(httpd, 1, "child exec failed: %s: %s", |
path, strerror(errno)); |
path, strerror(errno)); |
} |
|
/* NOT REACHED */ |
/* NOT REACHED */ |
bozoerr(httpd, 1, "child execve returned?!"); |
bozoerr(httpd, 1, "child execve returned?!"); |
} |
} |
Line 623 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 461 bozo_process_cgi(bozo_httpreq_t *request |
|
free(query); |
free(query); |
free(file); |
free(file); |
free(url); |
free(url); |
for (i = 0; i < search_string_argc; i++) |
|
free(search_string_argv[i]); |
|
free(search_string_argv); |
|
|
|
close(sv[1]); |
close(sv[1]); |
|
|
Line 665 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 500 bozo_process_cgi(bozo_httpreq_t *request |
|
exit(0); |
exit(0); |
|
|
out: |
out: |
|
|
for (i = 0; i < search_string_argc; i++) |
|
free(search_string_argv[i]); |
|
free(search_string_argv); |
|
free(query); |
free(query); |
free(file); |
free(file); |
free(url); |
free(url); |