httpd: fix several bugs triggering by realtive path in -h DIR.

function                                             old     new   delta
handle_incoming_and_exit                            2657    2659      +2
send_cgi_and_exit                                    869     862      -7
parse_conf                                          1647    1626     -21
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/2 up/down: 2/-28)             Total: -26 bytes
This commit is contained in:
Denis Vlasenko 2008-05-07 12:18:48 +00:00
parent b153ace939
commit 6bf05cf1ff
3 changed files with 83 additions and 70 deletions

View File

@ -938,6 +938,10 @@ int bb_ask_confirmation(void);
extern int bb_parse_mode(const char* s, mode_t* theMode); extern int bb_parse_mode(const char* s, mode_t* theMode);
/* Concatenate path and filename to new allocated buffer.
* Add "/" only as needed (no duplicate "//" are produced).
* If path is NULL, it is assumed to be "/".
* filename should not be NULL. */
char *concat_path_file(const char *path, const char *filename); char *concat_path_file(const char *path, const char *filename);
char *concat_subpath_file(const char *path, const char *filename); char *concat_subpath_file(const char *path, const char *filename);
const char *bb_basename(const char *name); const char *bb_basename(const char *name);

View File

@ -8,9 +8,11 @@
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/ */
/* concatenate path and file name to new allocation buffer, /* Concatenate path and filename to new allocated buffer.
* not adding '/' if path name already has '/' * Add '/' only as needed (no duplicate // are produced).
*/ * If path is NULL, it is assumed to be "/".
* filename should not be NULL.
*/
#include "libbb.h" #include "libbb.h"

View File

@ -613,7 +613,12 @@ static void parse_conf(const char *path, int flag)
/* then error page; find matching status */ /* then error page; find matching status */
for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
if (http_response_type[i] == status) { if (http_response_type[i] == status) {
http_error_page[i] = concat_path_file((*c == '/') ? NULL : home_httpd, c); // We chdir to home_httpd, thus no need to
// concat_path_file(home_httpd, c)
//if (c[0] == '/' || home_httpd[0] != '/')
http_error_page[i] = xstrdup(c);
//else
// http_error_page[i] = concat_path_file(home_httpd, c);
break; break;
} }
} }
@ -1009,7 +1014,7 @@ static void send_headers(int responseNum)
} }
#if ENABLE_FEATURE_HTTPD_ERROR_PAGES #if ENABLE_FEATURE_HTTPD_ERROR_PAGES
if (error_page && !access(error_page, R_OK)) { if (error_page && access(error_page, R_OK) == 0) {
strcat(iobuf, "\r\n"); strcat(iobuf, "\r\n");
len += 2; len += 2;
@ -1313,49 +1318,49 @@ static void send_cgi_and_exit(
{ {
struct fd_pair fromCgi; /* CGI -> httpd pipe */ struct fd_pair fromCgi; /* CGI -> httpd pipe */
struct fd_pair toCgi; /* httpd -> CGI pipe */ struct fd_pair toCgi; /* httpd -> CGI pipe */
char *fullpath;
char *script; char *script;
char *purl;
int pid; int pid;
/* Make a copy. NB: caller guarantees:
* url[0] == '/', url[1] != '/' */
url = xstrdup(url);
/* /*
* We are mucking with environment _first_ and then vfork/exec, * We are mucking with environment _first_ and then vfork/exec,
* this allows us to use vfork safely. Parent don't care about * this allows us to use vfork safely. Parent doesn't care about
* these environment changes anyway. * these environment changes anyway.
*/ */
/* /* Check for [dirs/]script.cgi/PATH_INFO */
* Find PATH_INFO. script = (char*)url;
*/
purl = xstrdup(url);
script = purl;
while ((script = strchr(script + 1, '/')) != NULL) { while ((script = strchr(script + 1, '/')) != NULL) {
/* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
struct stat sb; struct stat sb;
*script = '\0'; *script = '\0';
if (!is_directory(purl + 1, 1, &sb)) { if (!is_directory(url + 1, 1, &sb)) {
/* not directory, found script.cgi/PATH_INFO */ /* not directory, found script.cgi/PATH_INFO */
*script = '/'; *script = '/';
break; break;
} }
*script = '/'; /* is directory, find next '/' */ *script = '/'; /* is directory, find next '/' */
} }
setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */ setenv1("PATH_INFO", script); /* set to /PATH_INFO or "" */
setenv1("REQUEST_METHOD", request); setenv1("REQUEST_METHOD", request);
if (g_query) { if (g_query) {
putenv(xasprintf("%s=%s?%s", "REQUEST_URI", purl, g_query)); putenv(xasprintf("%s=%s?%s", "REQUEST_URI", url, g_query));
} else { } else {
setenv1("REQUEST_URI", purl); setenv1("REQUEST_URI", url);
} }
if (script != NULL) if (script != NULL)
*script = '\0'; /* cut off /PATH_INFO */ *script = '\0'; /* cut off /PATH_INFO */
/* SCRIPT_FILENAME required by PHP in CGI mode */ /* SCRIPT_FILENAME is required by PHP in CGI mode */
fullpath = concat_path_file(home_httpd, purl); if (home_httpd[0] == '/') {
setenv1("SCRIPT_FILENAME", fullpath); char *fullpath = concat_path_file(home_httpd, url);
setenv1("SCRIPT_FILENAME", fullpath);
}
/* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
setenv1("SCRIPT_NAME", purl); setenv1("SCRIPT_NAME", url);
/* http://hoohoo.ncsa.uiuc.edu/cgi/env.html: /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html:
* QUERY_STRING: The information which follows the ? in the URL * QUERY_STRING: The information which follows the ? in the URL
* which referenced this script. This is the query information. * which referenced this script. This is the query information.
@ -1413,6 +1418,8 @@ static void send_cgi_and_exit(
if (!pid) { if (!pid) {
/* Child process */ /* Child process */
char *argv[3];
xfunc_error_retval = 242; xfunc_error_retval = 242;
/* NB: close _first_, then move fds! */ /* NB: close _first_, then move fds! */
@ -1424,53 +1431,54 @@ static void send_cgi_and_exit(
* If CGI really wants that, it can always do dup itself. */ * If CGI really wants that, it can always do dup itself. */
/* dup2(1, 2); */ /* dup2(1, 2); */
script = strrchr(fullpath, '/'); /* Chdiring to script's dir */
//fullpath is a result of concat_path_file and always has '/' script = strrchr(url, '/');
//if (!script) if (script != url) { /* paranoia */
// goto error_execing_cgi; *script = '\0';
*script = '\0'; if (chdir(url + 1) != 0) {
/* chdiring to script's dir */ bb_perror_msg("chdir %s", url + 1);
if (chdir(script == fullpath ? "/" : fullpath) == 0) { goto error_execing_cgi;
char *argv[3]; }
// not needed: *script = '/';
}
script++;
*script++ = '/'; /* repair fullpath */ /* set argv[0] to name without path */
/* set argv[0] to name without path */ argv[0] = script;
argv[0] = script; argv[1] = NULL;
argv[1] = NULL;
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
{ {
char *suffix = strrchr(script, '.'); char *suffix = strrchr(script, '.');
if (suffix) { if (suffix) {
Htaccess *cur; Htaccess *cur;
for (cur = script_i; cur; cur = cur->next) { for (cur = script_i; cur; cur = cur->next) {
if (strcmp(cur->before_colon + 1, suffix) == 0) { if (strcmp(cur->before_colon + 1, suffix) == 0) {
/* found interpreter name */ /* found interpreter name */
fullpath = cur->after_colon; argv[0] = cur->after_colon;
argv[0] = cur->after_colon; argv[1] = script;
argv[1] = script; argv[2] = NULL;
argv[2] = NULL; break;
break;
}
} }
} }
} }
#endif
/* restore default signal dispositions for CGI process */
bb_signals(0
| (1 << SIGCHLD)
| (1 << SIGPIPE)
| (1 << SIGHUP)
, SIG_DFL);
execv(fullpath, argv);
if (verbose)
bb_perror_msg("exec %s", fullpath);
} else if (verbose) {
bb_perror_msg("chdir %s", fullpath);
} }
//error_execing_cgi: #endif
/* restore default signal dispositions for CGI process */
bb_signals(0
| (1 << SIGCHLD)
| (1 << SIGPIPE)
| (1 << SIGHUP)
, SIG_DFL);
/* _NOT_ execvp. We do not search PATH. argv[0] is a filename
* without any dir components and will only match a file
* in the current directory */
execv(argv[0], argv);
if (verbose)
bb_perror_msg("exec %s", argv[0]);
error_execing_cgi:
/* send to stdout /* send to stdout
* (we are CGI here, our stdout is pumped to the net) */ * (we are CGI here, our stdout is pumped to the net) */
send_headers_and_exit(HTTP_NOT_FOUND); send_headers_and_exit(HTTP_NOT_FOUND);
@ -1889,7 +1897,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
/* Canonicalize path */ /* Canonicalize path */
/* Algorithm stolen from libbb bb_simplify_path(), /* Algorithm stolen from libbb bb_simplify_path(),
* but don't strdup and reducing trailing slash and protect out root */ * but don't strdup, retain trailing slash, protect root */
urlp = tptr = urlcopy; urlp = tptr = urlcopy;
do { do {
if (*urlp == '/') { if (*urlp == '/') {
@ -1898,11 +1906,11 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
continue; continue;
} }
if (*tptr == '.') { if (*tptr == '.') {
/* skip extra '.' */ /* skip extra "/./" */
if (tptr[1] == '/' || !tptr[1]) { if (tptr[1] == '/' || !tptr[1]) {
continue; continue;
} }
/* '..': be careful */ /* "..": be careful */
if (tptr[1] == '.' && (tptr[2] == '/' || !tptr[2])) { if (tptr[1] == '.' && (tptr[2] == '/' || !tptr[2])) {
++tptr; ++tptr;
if (urlp == urlcopy) /* protect root */ if (urlp == urlcopy) /* protect root */
@ -1914,11 +1922,10 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
} }
*++urlp = *tptr; *++urlp = *tptr;
} while (*++tptr); } while (*++tptr);
*++urlp = '\0'; /* so keep last character */ *++urlp = '\0'; /* terminate after last character */
tptr = urlp; /* end ptr */
/* If URL is a directory, add '/' */ /* If URL is a directory, add '/' */
if (tptr[-1] != '/') { if (urlp[-1] != '/') {
if (is_directory(urlcopy + 1, 1, &sb)) { if (is_directory(urlcopy + 1, 1, &sb)) {
found_moved_temporarily = urlcopy; found_moved_temporarily = urlcopy;
} }
@ -2310,8 +2317,8 @@ int httpd_main(int argc ATTRIBUTE_UNUSED, char **argv)
/* -v counts, -i implies -f */ /* -v counts, -i implies -f */
opt_complementary = "vv:if"; opt_complementary = "vv:if";
/* We do not "absolutize" path given by -h (home) opt. /* We do not "absolutize" path given by -h (home) opt.
* If user gives relative path in -h, $SCRIPT_FILENAME can end up * If user gives relative path in -h,
* relative too. */ * $SCRIPT_FILENAME will not be set. */
opt = getopt32(argv, "c:d:h:" opt = getopt32(argv, "c:d:h:"
USE_FEATURE_HTTPD_ENCODE_URL_STR("e:") USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
USE_FEATURE_HTTPD_BASIC_AUTH("r:") USE_FEATURE_HTTPD_BASIC_AUTH("r:")