version 1.20.2.4, 2017/03/07 07:25:18 |
version 1.21, 2013/06/27 10:01:31 |
|
|
/* $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-2017 Matthew R. Green |
* Copyright (c) 1997-2011 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 |
Line 75 content_cgihandler(bozohttpd_t *httpd, b |
|
Line 75 content_cgihandler(bozohttpd_t *httpd, b |
|
} |
} |
|
|
static int |
static int |
parse_header(bozo_httpreq_t *request, const char *str, ssize_t len, |
parse_header(bozohttpd_t *httpd, const char *str, ssize_t len, char **hdr_str, |
char **hdr_str, char **hdr_val) |
char **hdr_val) |
{ |
{ |
struct bozohttpd_t *httpd = request->hr_httpd; |
|
char *name, *value; |
char *name, *value; |
|
|
/* if the string passed is zero-length bail out */ |
/* if the string passed is zero-length bail out */ |
if (*str == '\0') |
if (*str == '\0') |
return -1; |
return -1; |
|
|
value = bozostrdup(httpd, request, str); |
value = bozostrdup(httpd, str); |
|
|
/* locate the ':' separator in the header/value */ |
/* locate the ':' separator in the header/value */ |
name = bozostrnsep(&value, ":", &len); |
name = bozostrnsep(&value, ":", &len); |
|
|
if (NULL == name || -1 == len) { |
if (NULL == name || -1 == len) { |
free(value); |
free(name); |
return -1; |
return -1; |
} |
} |
|
|
Line 128 finish_cgi_output(bozohttpd_t *httpd, bo |
|
Line 127 finish_cgi_output(bozohttpd_t *httpd, bo |
|
(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; |
|
|
if (parse_header(request, str, len, &hdr_name, &hdr_value)) |
if (parse_header(httpd, str, len, &hdr_name, &hdr_value)) |
break; |
break; |
|
|
/* |
/* |
Line 195 finish_cgi_output(bozohttpd_t *httpd, bo |
|
Line 194 finish_cgi_output(bozohttpd_t *httpd, bo |
|
rbytes -= wbytes; |
rbytes -= wbytes; |
bp += wbytes; |
bp += wbytes; |
} else |
} else |
bozoerr(httpd, 1, |
bozo_err(httpd, 1, |
"cgi output write failed: %s", |
"cgi output write failed: %s", |
strerror(errno)); |
strerror(errno)); |
} |
} |
Line 212 append_index_html(bozohttpd_t *httpd, ch |
|
Line 211 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, '+')); (*args_len)++) |
|
s++; |
|
|
|
args = bozomalloc(httpd, sizeof(*args) * (*args_len + 1)); |
|
|
|
args[0] = str; |
|
args[*args_len] = NULL; |
|
for (s = str, i = 0; (s = strchr(s, '+'));) { |
|
*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); |
|
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 = strdup(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)); |
} |
} |
Line 380 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 248 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 *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 391 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 259 bozo_process_cgi(bozo_httpreq_t *request |
|
if (!httpd->cgibin && !httpd->process_cgi) |
if (!httpd->cgibin && !httpd->process_cgi) |
return 0; |
return 0; |
|
|
#ifndef NO_USER_SUPPORT |
uri = request->hr_oldfile ? request->hr_oldfile : request->hr_file; |
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] == '/') |
if (uri[0] == '/') |
file = bozostrdup(httpd, request, uri); |
file = bozostrdup(httpd, uri); |
else |
else |
bozoasprintf(httpd, &file, "/%s", uri); |
asprintf(&file, "/%s", uri); |
|
if (file == NULL) |
|
return 0; |
|
|
if (request->hr_query && strlen(request->hr_query)) |
if (request->hr_query && strlen(request->hr_query)) |
query = bozostrdup(httpd, request, request->hr_query); |
query = bozostrdup(httpd, request->hr_query); |
else |
else |
query = NULL; |
query = NULL; |
|
|
bozoasprintf(httpd, &url, "%s%s%s", |
asprintf(&url, "%s%s%s", file, query ? "?" : "", query ? query : ""); |
file, |
if (url == NULL) |
query ? "?" : "", |
goto out; |
query ? query : ""); |
|
debug((httpd, DEBUG_NORMAL, "bozo_process_cgi: url `%s'", url)); |
debug((httpd, DEBUG_NORMAL, "bozo_process_cgi: url `%s'", url)); |
|
|
path = NULL; |
path = NULL; |
Line 443 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 303 bozo_process_cgi(bozo_httpreq_t *request |
|
} 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, 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) { |
info = bozostrdup(httpd, request, s); |
info = bozostrdup(httpd, s); |
*s = '\0'; |
*s = '\0'; |
} |
} |
path = bozomalloc(httpd, |
path = bozomalloc(httpd, |
Line 473 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 320 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 547 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 391 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 568 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 409 bozo_process_cgi(bozo_httpreq_t *request |
|
bozo_setenv(httpd, "REMOTE_ADDR", request->hr_remoteaddr, |
bozo_setenv(httpd, "REMOTE_ADDR", request->hr_remoteaddr, |
curenvp++); |
curenvp++); |
/* |
/* |
* Apache does this when invoking content handlers, and PHP |
* XXX Apache does this when invoking content handlers, and PHP |
* 5.3 requires it as a "security" measure. |
* XXX 5.3 requires it as a "security" measure. |
*/ |
*/ |
if (cgihandler) |
if (cgihandler) |
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, "bozo_process_cgi: going exec %s with args:", |
free(file); |
path)); |
free(url); |
|
|
for (i = 0; argv[i] != NULL; i++) { |
debug((httpd, DEBUG_FAT, "bozo_process_cgi: going exec %s, %s %s %s", |
debug((httpd, DEBUG_FAT, "bozo_process_cgi: argv[%zu] = `%s'", |
path, argv[0], strornull(argv[1]), strornull(argv[2]))); |
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", |
bozo_err(httpd, 1, "child socketpair failed: %s", |
strerror(errno)); |
strerror(errno)); |
|
|
/* |
/* |
Line 595 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 434 bozo_process_cgi(bozo_httpreq_t *request |
|
*/ |
*/ |
switch (fork()) { |
switch (fork()) { |
case -1: /* eep, failure */ |
case -1: /* eep, failure */ |
bozoerr(httpd, 1, "child fork failed: %s", strerror(errno)); |
bozo_err(httpd, 1, "child fork failed: %s", strerror(errno)); |
/*NOTREACHED*/ |
/*NOTREACHED*/ |
case 0: |
case 0: |
close(sv[0]); |
close(sv[0]); |
Line 607 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 446 bozo_process_cgi(bozo_httpreq_t *request |
|
bozo_daemon_closefds(httpd); |
bozo_daemon_closefds(httpd); |
|
|
if (-1 == execve(path, argv, envp)) |
if (-1 == execve(path, argv, envp)) |
bozoerr(httpd, 1, "child exec failed: %s: %s", |
bozo_err(httpd, 1, "child exec failed: %s: %s", |
path, strerror(errno)); |
path, strerror(errno)); |
/* NOT REACHED */ |
/* NOT REACHED */ |
bozoerr(httpd, 1, "child execve returned?!"); |
bozo_err(httpd, 1, "child execve returned?!"); |
} |
} |
|
|
free(query); |
|
free(file); |
|
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]); |
|
|
/* parent: read from stdin (bozo_read()) write to sv[0] */ |
/* parent: read from stdin (bozo_read()) write to sv[0] */ |
/* child: read from sv[0] (bozo_write()) write to stdout */ |
/* child: read from sv[0] (bozo_write()) write to stdout */ |
pid = fork(); |
pid = fork(); |
if (pid == -1) |
if (pid == -1) |
bozoerr(httpd, 1, "io child fork failed: %s", strerror(errno)); |
bozo_err(httpd, 1, "io child fork failed: %s", strerror(errno)); |
else if (pid == 0) { |
else if (pid == 0) { |
/* child reader/writer */ |
/* child reader/writer */ |
close(STDIN_FILENO); |
close(STDIN_FILENO); |
Line 650 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 482 bozo_process_cgi(bozo_httpreq_t *request |
|
rbytes -= wbytes; |
rbytes -= wbytes; |
bp += wbytes; |
bp += wbytes; |
} else |
} else |
bozoerr(httpd, 1, "write failed: %s", |
bozo_err(httpd, 1, "write failed: %s", |
strerror(errno)); |
strerror(errno)); |
} |
} |
} |
} |
Line 658 bozo_process_cgi(bozo_httpreq_t *request |
|
Line 490 bozo_process_cgi(bozo_httpreq_t *request |
|
exit(0); |
exit(0); |
|
|
out: |
out: |
|
if (query) |
for (i = 0; i < search_string_argc; i++) |
free(query); |
free(search_string_argv[i]); |
if (file) |
free(search_string_argv); |
free(file); |
free(query); |
if (url) |
free(file); |
free(url); |
free(url); |
|
return 0; |
return 0; |
} |
} |
|
|
#ifndef NO_DYNAMIC_CONTENT |
#ifndef NO_DYNAMIC_CONTENT |
/* cgi maps are simple ".postfix /path/to/prog" */ |
/* cgi maps are simple ".postfix /path/to/prog" */ |
void |
void |
bozo_add_content_map_cgi(bozohttpd_t *httpd, const char *arg, |
bozo_add_content_map_cgi(bozohttpd_t *httpd, const char *arg, const char *cgihandler) |
const char *cgihandler) |
|
{ |
{ |
bozo_content_map_t *map; |
bozo_content_map_t *map; |
|
|
Line 683 bozo_add_content_map_cgi(bozohttpd_t *ht |
|
Line 513 bozo_add_content_map_cgi(bozohttpd_t *ht |
|
|
|
map = bozo_get_content_map(httpd, arg); |
map = bozo_get_content_map(httpd, arg); |
map->name = arg; |
map->name = arg; |
|
map->namelen = strlen(map->name); |
map->type = map->encoding = map->encoding11 = NULL; |
map->type = map->encoding = map->encoding11 = NULL; |
map->cgihandler = cgihandler; |
map->cgihandler = cgihandler; |
} |
} |