#define range_len (G.range_len )
#else
enum {
- range_start = 0,
+ range_start = -1,
range_end = MAXINT(off_t) - 1,
range_len = MAXINT(off_t),
};
#define INIT_G() do { \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
+ IF_FEATURE_HTTPD_RANGES(range_start = -1;) \
bind_addr_or_port = "80"; \
index_page = index_html; \
file_size = -1; \
/* the line is not recognized */
config_error:
bb_error_msg("config error '%s' in '%s'", buf, filename);
- } /* while (fgets) */
+ } /* while (fgets) */
- fclose(f);
+ fclose(f);
}
#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
*
* Parameters:
* const char *url The requested URL (with leading /).
+ * const char *orig_uri The original URI before rewriting (if any)
* int post_len Length of the POST body.
* const char *cookie For set HTTP_COOKIE.
* const char *content_type For set CONTENT_TYPE.
*/
static void send_cgi_and_exit(
const char *url,
+ const char *orig_uri,
const char *request,
int post_len,
const char *cookie,
const char *content_type) NORETURN;
static void send_cgi_and_exit(
const char *url,
+ const char *orig_uri,
const char *request,
int post_len,
const char *cookie,
setenv1("PATH_INFO", script); /* set to /PATH_INFO or "" */
setenv1("REQUEST_METHOD", request);
if (g_query) {
- putenv(xasprintf("%s=%s?%s", "REQUEST_URI", url, g_query));
+ putenv(xasprintf("%s=%s?%s", "REQUEST_URI", orig_uri, g_query));
} else {
- setenv1("REQUEST_URI", url);
+ setenv1("REQUEST_URI", orig_uri);
}
if (script != NULL)
*script = '\0'; /* cut off /PATH_INFO */
if (script != url) { /* paranoia */
*script = '\0';
if (chdir(url + 1) != 0) {
- bb_perror_msg("chdir(%s)", url + 1);
+ bb_perror_msg("can't change directory to '%s'", url + 1);
goto error_execing_cgi;
}
// not needed: *script = '/';
if (what == SEND_BODY /* err pages and ranges don't mix */
|| content_gzip /* we are sending compressed page: can't do ranges */ ///why?
) {
- range_start = 0;
+ range_start = -1;
}
range_len = MAXINT(off_t);
- if (range_start) {
- if (!range_end) {
+ if (range_start >= 0) {
+ if (!range_end || range_end > file_size - 1) {
range_end = file_size - 1;
}
if (range_end < range_start
|| lseek(fd, range_start, SEEK_SET) != range_start
) {
lseek(fd, 0, SEEK_SET);
- range_start = 0;
+ range_start = -1;
} else {
range_len = range_end - range_start + 1;
send_headers(HTTP_PARTIAL_CONTENT);
break; /* fall back to read/write loop */
goto fin;
}
- IF_FEATURE_HTTPD_RANGES(range_len -= sz;)
+ IF_FEATURE_HTTPD_RANGES(range_len -= count;)
if (count == 0 || range_len == 0)
log_and_exit();
}
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
-# if ENABLE_FEATURE_HTTPD_AUTH_MD5 && ENABLE_PAM
+# if ENABLE_PAM
struct pam_userinfo {
const char *name;
const char *pw;
case PAM_PROMPT_ECHO_OFF:
s = userinfo->pw;
break;
- case PAM_ERROR_MSG:
+ case PAM_ERROR_MSG:
case PAM_TEXT_INFO:
s = "";
break;
colon_after_user = strchr(user_and_passwd, ':');
if (!colon_after_user)
goto bad_input;
+
+ /* compare "user:" */
+ if (cur->after_colon[0] != '*'
+ && strncmp(cur->after_colon, user_and_passwd,
+ colon_after_user - user_and_passwd + 1) != 0
+ ) {
+ continue;
+ }
+ /* this cfg entry is '*' or matches username from peer */
+
passwd = strchr(cur->after_colon, ':');
if (!passwd)
goto bad_input;
struct pam_conv conv_info = { &pam_talker, (void *) &userinfo };
pam_handle_t *pamh;
- /* compare "user:" */
- if (cur->after_colon[0] != '*'
- && strncmp(cur->after_colon, user_and_passwd, colon_after_user - user_and_passwd + 1) != 0
- ) {
- continue;
- }
- /* this cfg entry is '*' or matches username from peer */
*colon_after_user = '\0';
userinfo.name = user_and_passwd;
userinfo.pw = colon_after_user + 1;
passwd = result->sp_pwdp;
}
# endif
+ /* In this case, passwd is ALWAYS encrypted:
+ * it came from /etc/passwd or /etc/shadow!
+ */
+ goto check_encrypted;
# endif /* ENABLE_PAM */
}
+ /* Else: passwd is from httpd.conf, it is either plaintext or encrypted */
- /* compare "user:" */
- if (cur->after_colon[0] != '*'
- && strncmp(cur->after_colon, user_and_passwd, colon_after_user - user_and_passwd + 1) != 0
- ) {
- continue;
- }
- /* this cfg entry is '*' or matches username from peer */
-
- /* encrypt pwd from peer and check match with local one */
- {
- char *encrypted = pw_encrypt(
- /* pwd: */ colon_after_user + 1,
+ if (passwd[0] == '$' && isdigit(passwd[1])) {
+ char *encrypted;
+# if !ENABLE_PAM
+ check_encrypted:
+# endif
+ /* encrypt pwd from peer and check match with local one */
+ encrypted = pw_encrypt(
+ /* pwd (from peer): */ colon_after_user + 1,
/* salt: */ passwd,
/* cleanup: */ 0
);
r = strcmp(encrypted, passwd);
free(encrypted);
- goto end_check_passwd;
+ } else {
+ /* local passwd is from httpd.conf and it's plaintext */
+ r = strcmp(colon_after_user + 1, passwd);
}
- bad_input: ;
+ goto end_check_passwd;
}
-
+ bad_input:
/* Comparing plaintext "user:pass" in one go */
r = strcmp(cur->after_colon, user_and_passwd);
end_check_passwd:
send_headers_and_exit(HTTP_BAD_REQUEST);
/* Determine type of request (GET/POST) */
- urlp = strpbrk(iobuf, " \t");
+ // rfc2616: method and URI is separated by exactly one space
+ //urlp = strpbrk(iobuf, " \t"); - no, tab isn't allowed
+ urlp = strchr(iobuf, ' ');
if (urlp == NULL)
send_headers_and_exit(HTTP_BAD_REQUEST);
*urlp++ = '\0';
if (strcasecmp(iobuf, request_GET) != 0)
send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
#endif
- urlp = skip_whitespace(urlp);
+ // rfc2616: method and URI is separated by exactly one space
+ //urlp = skip_whitespace(urlp); - should not be necessary
if (urlp[0] != '/')
send_headers_and_exit(HTTP_BAD_REQUEST);
s += sizeof("bytes=")-1;
range_start = BB_STRTOOFF(s, &s, 10);
if (s[0] != '-' || range_start < 0) {
- range_start = 0;
+ range_start = -1;
} else if (s[1]) {
range_end = BB_STRTOOFF(s+1, NULL, 10);
if (errno || range_end < range_start)
- range_start = 0;
+ range_start = -1;
}
}
}
/* protect listing "cgi-bin/" */
send_headers_and_exit(HTTP_FORBIDDEN);
}
- send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type);
+ send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type);
}
#endif
- if (urlp[-1] == '/')
+ if (urlp[-1] == '/') {
+ /* When index_page string is appended to <dir>/ URL, it overwrites
+ * the query string. If we fall back to call /cgi-bin/index.cgi,
+ * query string would be lost and not available to the CGI.
+ * Work around it by making a deep copy.
+ */
+ if (ENABLE_FEATURE_HTTPD_CGI)
+ g_query = xstrdup(g_query); /* ok for NULL too */
strcpy(urlp, index_page);
+ }
if (stat(tptr, &sb) == 0) {
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
char *suffix = strrchr(tptr, '.');
Htaccess *cur;
for (cur = script_i; cur; cur = cur->next) {
if (strcmp(cur->before_colon + 1, suffix) == 0) {
- send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type);
+ send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type);
}
}
}
/* It's a dir URL and there is no index.html
* Try cgi-bin/index.cgi */
if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
- urlp[0] = '\0';
- g_query = urlcopy;
- send_cgi_and_exit("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
+ urlp[0] = '\0'; /* remove index_page */
+ send_cgi_and_exit("/cgi-bin/index.cgi", urlcopy, prequest, length, cookie, content_type);
}
}
/* else fall through to send_file, it errors out if open fails: */