last_patch84 by Vodz.

This commit is contained in:
Glenn L McGrath 2003-05-13 16:20:11 +00:00
parent 49e74effbc
commit c9163fee91

View File

@ -42,7 +42,7 @@
* The server can also be invoked as a url arg decoder and html text encoder * The server can also be invoked as a url arg decoder and html text encoder
* as follows: * as follows:
* foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
* bar=`httpd -e "<Hello World>"` # encode as "%3CHello%20World%3E" * bar=`httpd -e "<Hello World>"` # encode as "&#60Hello&#32World&#62"
* *
* httpd.conf has the following format: * httpd.conf has the following format:
@ -56,51 +56,17 @@ D:* # Deny from other IP connections
/adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
.au:audio/basic # additional mime type for audio.au files .au:audio/basic # additional mime type for audio.au files
A shortes path and D:from[^*] automaticaly sorting to top.
All longest path can`t reset user:password if shorted protect setted.
A/D may be as a/d or allow/deny - first char case unsensitive parsed only. A/D may be as a/d or allow/deny - first char case unsensitive parsed only.
Each subdir can have config file. Each subdir can have config file.
You can set less IP allow from subdir config.
Password protection from subdir config can rewriten previous sets for
current or/and next subpathes.
For protect as user:pass current subdir and subpathes set from subdir config: For protect as user:pass current subdir and subpathes set from subdir config:
/:user:pass /:user:pass
if not, other subpathes for give effect must have path from httpd root /subpath:user2:pass2
/current_subdir_path_from_httpd_root/subpath:user:pass
The Deny/Allow IP logic:
1. Allow all:
The config don`t set D: lines
2. Allow from setted only:
see the begin format example
3. Set deny, allow from other:
D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
A:* # allow from other, this line not strongly require
A global and subdirs config merging logic:
allow rules reducing, deny lines strongled.
The algorithm combinations:
4. If current config have
A:from
D:*
subdir config A: lines skiping, D:from - moving top
5. If current config have
D:from
A:*
result config:
D:from current
D:from subdir
A:from subdir
and seting D:* if subdir config have this
If -c don`t setted, used httpd root config, else httpd root config skiped. If -c don`t setted, used httpd root config, else httpd root config skiped.
Exited with fault if can`t open start config.
For set wide open server, use -c /dev/null ;=)
*/ */
#include <stdio.h> #include <stdio.h>
@ -120,30 +86,29 @@ A:from subdir
#include "busybox.h" #include "busybox.h"
static const char httpdVersion[] = "busybox httpd/1.20 31-Jan-2003"; static const char httpdVersion[] = "busybox httpd/1.25 10-May-2003";
static const char default_patch_httpd_conf[] = "/etc"; static const char default_path_httpd_conf[] = "/etc";
static const char httpd_conf[] = "httpd.conf"; static const char httpd_conf[] = "httpd.conf";
static const char home[] = "/www"; static const char home[] = "/www";
// Note: xfuncs are not used because we want the server to keep running // Note: bussybox xfuncs are not used because we want the server to keep running
// if something bad happens due to a malformed user request. // if something bad happens due to a malformed user request.
// As a result, all memory allocation after daemonize // As a result, all memory allocation after daemonize
// is checked rigorously // is checked rigorously
//#define DEBUG 1 //#define DEBUG 1
/* Configure options, disabled by default as nonstandart httpd feature */ /* Configure options, disabled by default as custom httpd feature */
/* disabled as optional features */
//#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV //#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
//#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR //#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
//#define CONFIG_FEATURE_HTTPD_DECODE_URL_STR
/* disabled as not necessary feature */
//#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV //#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
//#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES //#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
//#define CONFIG_FEATURE_HTTPD_SETUID //#define CONFIG_FEATURE_HTTPD_SETUID
//#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP //#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
/* If seted this you can use this server from internet superserver only */ /* If set, use this server from internet superserver only */
//#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY //#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
/* You can use this server as standalone, require libbb.a for linking */ /* You can use this server as standalone, require libbb.a for linking */
@ -160,7 +125,6 @@ static const char home[] = "/www";
#undef CONFIG_FEATURE_HTTPD_BASIC_AUTH #undef CONFIG_FEATURE_HTTPD_BASIC_AUTH
#undef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV #undef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
#undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR #undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
#undef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
#undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV #undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
#undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES #undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
#undef CONFIG_FEATURE_HTTPD_CGI #undef CONFIG_FEATURE_HTTPD_CGI
@ -170,7 +134,6 @@ static const char home[] = "/www";
#define CONFIG_FEATURE_HTTPD_BASIC_AUTH #define CONFIG_FEATURE_HTTPD_BASIC_AUTH
#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV #define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR #define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
#define CONFIG_FEATURE_HTTPD_DECODE_URL_STR
#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV #define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES #define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
#define CONFIG_FEATURE_HTTPD_CGI #define CONFIG_FEATURE_HTTPD_CGI
@ -183,7 +146,7 @@ const char *bb_applet_name = "httpd";
void bb_show_usage(void) void bb_show_usage(void)
{ {
fprintf(stderr, "Usage: %s [-p <port>] [-c configFile] [-d/-e <string>] " fprintf(stderr, "Usage: %s [-p <port>] [-c configFile] [-d/-e <string>] "
"[-r realm] [-u user]\n", bb_applet_name); "[-r realm] [-u user] [-h homedir]\n", bb_applet_name);
exit(1); exit(1);
} }
#endif #endif
@ -232,6 +195,15 @@ typedef struct
const char *found_mime_type; const char *found_mime_type;
off_t ContentLength; /* -1 - unknown */ off_t ContentLength; /* -1 - unknown */
time_t last_mod; time_t last_mod;
Htaccess *ip_a_d; /* config allow/deny lines */
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
Htaccess *auth; /* config user:password lines */
#endif
#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Htaccess *mime_a; /* config mime types */
#endif
#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
int accepted_socket; int accepted_socket;
#define a_c_r config->accepted_socket #define a_c_r config->accepted_socket
@ -241,7 +213,6 @@ typedef struct
#define a_c_r 0 #define a_c_r 0
#define a_c_w 1 #define a_c_w 1
#endif #endif
Htaccess *Httpd_conf_parsed;
} HttpdConfig; } HttpdConfig;
static HttpdConfig *config; static HttpdConfig *config;
@ -308,15 +279,16 @@ static const HttpEnumString httpResponseNames[] = {
{ HTTP_OK, "OK" }, { HTTP_OK, "OK" },
{ HTTP_NOT_IMPLEMENTED, "Not Implemented", { HTTP_NOT_IMPLEMENTED, "Not Implemented",
"The requested method is not recognized by this server." }, "The requested method is not recognized by this server." },
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
{ HTTP_UNAUTHORIZED, "Unauthorized", "" }, { HTTP_UNAUTHORIZED, "Unauthorized", "" },
#endif
{ HTTP_NOT_FOUND, "Not Found", { HTTP_NOT_FOUND, "Not Found",
"The requested URL was not found on this server." }, "The requested URL was not found on this server." },
{ HTTP_BAD_REQUEST, "Bad Request" , { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
"Unsupported method." },
{ HTTP_FORBIDDEN, "Forbidden", "" }, { HTTP_FORBIDDEN, "Forbidden", "" },
{ HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error" { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
"Internal Server Error" }, "Internal Server Error" },
#if 0 #if 0 /* not implemented */
{ HTTP_CREATED, "Created" }, { HTTP_CREATED, "Created" },
{ HTTP_ACCEPTED, "Accepted" }, { HTTP_ACCEPTED, "Accepted" },
{ HTTP_NO_CONTENT, "No Content" }, { HTTP_NO_CONTENT, "No Content" },
@ -334,157 +306,106 @@ static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
static const char Content_length[] = "Content-length:"; static const char Content_length[] = "Content-length:";
/*
* sotring to: static void free_config_lines(Htaccess **pprev)
* .ext:mime/type
* /path:user:pass
* /path/subdir:user:pass
* D:from
* A:from
* D:*
*/
static int conf_sort(const void *p1, const void *p2)
{ {
const Htaccess *cl1 = *(const Htaccess **)p1; Htaccess *prev = *pprev;
const Htaccess *cl2 = *(const Htaccess **)p2;
char c1 = cl1->before_colon[0];
char c2 = cl2->before_colon[0];
int test;
#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES while( prev ) {
/* .ext line up before other lines for simlify algorithm */ Htaccess *cur = prev;
test = c2 == '.';
if(c1 == '.')
return -(!test);
if(test)
return test;
#endif
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH prev = cur->next;
test = c1 == '/'; free(cur);
/* /path line up before A/D lines for simlify algorithm */
if(test) {
if(c2 != '/')
return -test;
/* a shortes path with user:pass must be first */
return strlen(cl1->before_colon) - strlen(cl2->before_colon);
} else if(c2 == '/')
return !test;
#endif
/* D:from must move top */
test = c2 == 'D' && cl2->after_colon[0] != 0;
if(c1 == 'D' && cl1->after_colon[0] != 0) {
return -(!test);
} }
if(test) *pprev = NULL;
return test;
/* next lines - A:from */
test = c2 == 'A' && cl2->after_colon[0] != 0;
if(c1 == 'A' && cl1->after_colon[0] != 0) {
return -(!test);
}
if(test)
return test;
/* end lines - D:* */
test = c2 == 'D' && cl2->after_colon[0] == 0;
if(c1 == 'D' && cl1->after_colon[0] == 0) {
return -(!test);
}
#ifdef DEBUG
if(!test)
bb_error_msg_and_die("sort: can`t found compares!");
#endif
return test;
} }
static void add_config_line(Htaccess **pprev, Htaccess *cur)
{
if(*pprev == NULL) {
*pprev = cur;
} else {
Htaccess *prev;
for(prev = *pprev; prev->next; prev = prev->next)
;
prev->next = cur;
}
}
/* flag */ /* flag */
#define FIRST_PARSE 0 #define FIRST_PARSE 0
#define SUBDIR_PARSE 1 #define SUBDIR_PARSE 1
#define SIGNALED_PARSE 2 #define SIGNALED_PARSE 2
#define FIND_FROM_HTTPD_ROOT 3
static void parse_conf(const char *path, int flag) static void parse_conf(const char *path, int flag)
{ {
#define bc cur->before_colon[0]
#define ac cur->after_colon[0]
FILE *f; FILE *f;
Htaccess *prev;
Htaccess *cur; Htaccess *cur;
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
Htaccess *prev;
#endif
const char *cf = config->configFile; const char *cf = config->configFile;
char buf[80]; char buf[80];
char *p0 = NULL; char *p0 = NULL;
int deny_all = 0; /* default A:* */ char *c, *p;
int n = 0; /* count config lines */
/* free previous setuped */
free_config_lines(&config->ip_a_d);
if(flag != SUBDIR_PARSE) {
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
free_config_lines(&config->auth)
#endif
; /* syntax confuse */
#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
free_config_lines(&config->mime_a);
#endif
}
if(flag == SUBDIR_PARSE || cf == NULL) { if(flag == SUBDIR_PARSE || cf == NULL) {
cf = p0 = alloca(strlen(path) + sizeof(httpd_conf) + 2); cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
if(p0 == NULL) { if(cf == NULL) {
if(flag == FIRST_PARSE) if(flag == FIRST_PARSE)
bb_error_msg_and_die(bb_msg_memory_exhausted); bb_error_msg_and_die(bb_msg_memory_exhausted);
return; return;
} }
sprintf(p0, "%s/%s", path, httpd_conf); sprintf((char *)cf, "%s/%s", path, httpd_conf);
} }
while((f = fopen(cf, "r")) == NULL) { while((f = fopen(cf, "r")) == NULL) {
if(flag != FIRST_PARSE) if(flag != FIRST_PARSE) {
return; /* subdir config not found */ /* config file not found */
if(p0 == NULL) /* if -c option gived */ return;
}
if(config->configFile) /* if -c option given */
bb_perror_msg_and_die("%s", cf); bb_perror_msg_and_die("%s", cf);
p0 = NULL; flag = FIND_FROM_HTTPD_ROOT;
cf = httpd_conf; /* set -c ./httpd_conf */ cf = httpd_conf;
}
prev = config->Httpd_conf_parsed;
if(flag != SUBDIR_PARSE) {
/* free previous setuped */
while( prev ) {
cur = prev;
prev = cur->next;
free(cur);
}
config->Httpd_conf_parsed = prev; /* eq NULL */
} else {
/* parse previous IP logic for merge */
for(cur = prev; cur; cur = cur->next) {
if(bc == 'D' && ac == 0)
deny_all++;
n++;
/* find last for set prev->next of merging */
if(cur != prev)
prev = prev->next;
}
} }
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
prev = config->auth;
#endif
/* This could stand some work */ /* This could stand some work */
while ( (p0 = fgets(buf, 80, f)) != NULL) { while ( (p0 = fgets(buf, 80, f)) != NULL) {
char *p; c = NULL;
char *colon; for(p = p0; *p0 != 0 && *p0 != '#'; p0++) {
if(!isspace(*p0)) {
for(p = colon = p0; *p; p++) { *p++ = *p0;
if(*p == '#') { if(*p0 == ':' && c == NULL)
c = p;
}
}
*p = 0; *p = 0;
break;
}
if(isspace(*p)) {
if(p != p0) {
*p = 0;
break;
}
p0++;
}
else if(*p == ':' && colon <= p0)
colon = p;
}
/* test for empty or strange line */ /* test for empty or strange line */
if (colon <= p0 || colon[1] == 0) if (c == NULL || *c == 0)
continue; continue;
if(colon[1] == '*') if(*c == '*')
colon[1] = 0; /* Allow all */ *c = 0; /* Allow all */
p0 = buf;
if(*p0 == 'a') if(*p0 == 'a')
*p0 = 'A'; *p0 = 'A';
if(*p0 == 'd') if(*p0 == 'd')
@ -498,70 +419,109 @@ static void parse_conf(const char *path, int flag)
#endif #endif
) )
continue; continue;
if(*p0 == 'A' && *c == 0) {
/* skip default A:* */
continue;
}
p0 = buf;
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
if(*p0 == '/' && colon[1] == 0) { if(*p0 == '/') {
if(*c == 0) {
/* skip /path:* */ /* skip /path:* */
continue; continue;
} }
/* make full path from httpd root / curent_path / config_line_path */
cf = flag == SUBDIR_PARSE ? path : "";
p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
if(p0 == NULL)
continue;
c[-1] = 0;
sprintf(p0, "/%s%s", cf, buf);
/* another call bb_simplify_path */
cf = p = p0;
do {
if (*p == '/') {
if (*cf == '/') { /* skip duplicate (or initial) slash */
continue;
} else if (*cf == '.') {
if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
continue;
} else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
++cf;
if (p > p0) {
while (*--p != '/'); /* omit previous dir */
}
continue;
}
}
}
*++p = *cf;
} while (*++cf);
if ((p == p0) || (*p != '/')) { /* not a trailing slash */
++p; /* so keep last character */
}
*p = 0;
sprintf(p0, "%s:%s", p0, c);
}
#endif
/* storing current config line */
cur = calloc(1, sizeof(Htaccess) + strlen(p0));
if(cur) {
cf = strcpy(cur->before_colon, p0);
c = strchr(cf, ':');
*c++ = 0;
cur->after_colon = c;
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
if(*cf == '/')
free(p0);
#endif
if(*cf == 'A' || *cf == 'D')
add_config_line(&config->ip_a_d, cur);
#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
else if(*cf == '.')
add_config_line(&config->mime_a, cur);
#endif #endif
if(*p0 == 'A' || *p0 == 'D') { #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
if(colon[1] == 0) { else if(prev == NULL) {
if(*p0 == 'A' || deny_all != 0)
continue; /* skip default A:* or double D:* */
}
if(deny_all != 0 && *p0 == 'A')
continue; // if previous setted rule D:* skip all subdir A:
}
/* storing current config line */
cur = calloc(1, sizeof(Htaccess) + (p-p0));
if(cur) {
if(*(colon-1) == '/' && (colon-1) != p0)
colon[-1] = 0; // remove last / from /path/
cur->after_colon = strcpy(cur->before_colon, p0);
cur->after_colon += (colon-p0);
*cur->after_colon++ = 0;
if(prev == NULL) {
/* first line */ /* first line */
config->Httpd_conf_parsed = prev = cur; config->auth = prev = cur;
} else { } else {
/* sort path, if current lenght eq or bigger then move up */
Htaccess *prev_hti = config->auth;
int l = strlen(cf);
Htaccess *hti;
for(hti = prev_hti; hti; hti = hti->next) {
if(l >= strlen(hti->before_colon)) {
/* insert before hti */
cur->next = hti;
if(prev_hti != hti) {
prev_hti->next = cur;
break;
} else {
/* insert as top */
config->auth = cur;
break;
}
}
if(prev_hti != hti)
prev_hti = prev_hti->next;
}
if(!hti) { /* not inserted, add to bottom */
prev->next = cur; prev->next = cur;
prev = cur; prev = cur;
} }
n++; }
#endif
} }
} }
fclose(f); fclose(f);
if(n > 1) {
/* sorting conf lines */
Htaccess ** pcur; /* array for qsort */
prev = config->Httpd_conf_parsed;
pcur = alloca((n + 1) * sizeof(Htaccess *));
if(pcur == NULL) {
if(flag == FIRST_PARSE)
bb_error_msg_and_die(bb_msg_memory_exhausted);
return;
}
n = 0;
for(cur = prev; cur; cur = cur->next)
pcur[n++] = cur;
pcur[n] = NULL;
qsort(pcur, n, sizeof(Htaccess *), conf_sort);
/* storing sorted config */
config->Httpd_conf_parsed = *pcur;
for(cur = *pcur; cur; cur = cur->next) {
#ifdef DEBUG
bb_error_msg("%s: %s:%s", cf, cur->before_colon, cur->after_colon);
#endif
cur->next = *++pcur;
}
}
} }
#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
@ -602,7 +562,6 @@ static char *encodeString(const char *string)
} }
#endif /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */ #endif /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */
#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
/**************************************************************************** /****************************************************************************
* *
> $Function: decodeString() > $Function: decodeString()
@ -615,20 +574,21 @@ static char *encodeString(const char *string)
* *
* $Parameters: * $Parameters:
* (char *) string . . . The first string to decode. * (char *) string . . . The first string to decode.
* (int) flag . . . 1 if require decode '+' as ' ' for CGI
* *
* $Return: (char *) . . . . A pointer to the decoded string (same as input). * $Return: (char *) . . . . A pointer to the decoded string (same as input).
* *
* $Errors: None * $Errors: None
* *
****************************************************************************/ ****************************************************************************/
static char *decodeString(char *string) static char *decodeString(char *string, int flag_plus_to_space)
{ {
/* note that decoded string is always shorter than original */ /* note that decoded string is always shorter than original */
char *orig = string; char *orig = string;
char *ptr = string; char *ptr = string;
while (*ptr) while (*ptr)
{ {
if (*ptr == '+') { *string++ = ' '; ptr++; } if (*ptr == '+' && flag_plus_to_space) { *string++ = ' '; ptr++; }
else if (*ptr != '%') *string++ = *ptr++; else if (*ptr != '%') *string++ = *ptr++;
else { else {
unsigned int value; unsigned int value;
@ -640,7 +600,6 @@ static char *decodeString(char *string)
*string = '\0'; *string = '\0';
return orig; return orig;
} }
#endif /* CONFIG_FEATURE_HTTPD_DECODE_URL_STR */
#ifdef CONFIG_FEATURE_HTTPD_CGI #ifdef CONFIG_FEATURE_HTTPD_CGI
@ -730,7 +689,7 @@ static void addEnvCgi(const char *pargs)
args = strchr(value, '&'); args = strchr(value, '&');
if (args) if (args)
*args++ = 0; *args++ = 0;
addEnv("CGI", name, decodeString(value)); addEnv("CGI", name, decodeString(value, 1));
} }
free(memargs); free(memargs);
} }
@ -758,27 +717,38 @@ static void addEnvCgi(const char *pargs)
****************************************************************************/ ****************************************************************************/
static void decodeBase64(char *Data) static void decodeBase64(char *Data)
{ {
int i = 0;
static const char base64ToBin[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const unsigned char *in = Data; const unsigned char *in = Data;
// The decoded size will be at most 3/4 the size of the encoded // The decoded size will be at most 3/4 the size of the encoded
unsigned long ch = 0; unsigned long ch = 0;
int i = 0;
while (*in) { while (*in) {
unsigned char conv = 0; int t = *in++;
while (*in) { switch(t) {
const char *p64; case '+':
t = 62;
p64 = strchr(base64ToBin, *in++);
if(p64 == NULL)
continue;
conv = (p64 - base64ToBin);
break; break;
case '/':
t = 63;
break;
case '=':
t = 0;
break;
case 'A' ... 'Z':
t = t - 'A';
break;
case 'a' ... 'z':
t = t - 'a' + 26;
break;
case '0' ... '9':
t = t - '0' + 52;
break;
default:
continue;
} }
ch = (ch << 6) | conv; ch = (ch << 6) | t;
i++; i++;
if (i == 4) { if (i == 4) {
*Data++ = (char) (ch >> 16); *Data++ = (char) (ch >> 16);
@ -1203,7 +1173,7 @@ static int sendFile(const char *url, char *buf)
if (suffix) { if (suffix) {
Htaccess * cur; Htaccess * cur;
for (cur = config->Httpd_conf_parsed; cur; cur = cur->next) { for (cur = config->mime_a; cur; cur = cur->next) {
if(strcmp(cur->before_colon, suffix) == 0) { if(strcmp(cur->before_colon, suffix) == 0) {
config->found_mime_type = cur->after_colon; config->found_mime_type = cur->after_colon;
break; break;
@ -1255,53 +1225,32 @@ static int sendFile(const char *url, char *buf)
* *
****************************************************************************/ ****************************************************************************/
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
static int checkPerm(const char *path, const char *request) static int checkPerm(const char *path, const char *request)
{ {
Htaccess * cur; Htaccess * cur;
const char *p; const char *p;
const char *p0;
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
int ipaddr = path == NULL; int ipaddr = path == NULL;
const char *prev = NULL; const char *prev = NULL;
#else
# define ipaddr 1
#endif
/* This could stand some work */ /* This could stand some work */
for (cur = config->Httpd_conf_parsed; cur; cur = cur->next) { for (cur = ipaddr ? config->ip_a_d : config->auth; cur; cur = cur->next) {
const char *p0 = cur->before_colon; p0 = cur->before_colon;
if(*p0 == 'A' || *p0 == 'D') {
if(!ipaddr)
continue;
} else {
if(ipaddr)
continue;
#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
if(*p0 == '.')
continue;
#endif
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
if(prev != NULL && strcmp(prev, p0) != 0) if(prev != NULL && strcmp(prev, p0) != 0)
continue; /* find next identical */ continue; /* find next identical */
#endif
}
p = cur->after_colon; p = cur->after_colon;
#ifdef DEBUG #ifdef DEBUG
if (config->debugHttpd) if (config->debugHttpd)
fprintf(stderr,"checkPerm: '%s' ? '%s'\n", fprintf(stderr,"checkPerm: '%s' ? '%s'\n",
(ipaddr ? p : p0), request); (ipaddr ? (*p ? p : "*") : p0), request);
#endif #endif
if(ipaddr) { if(ipaddr) {
if(strncmp(p, request, strlen(p)) != 0) if(strncmp(p, request, strlen(p)) != 0)
continue; continue;
return *p0 == 'A'; /* Allow/Deny */ return *p0 == 'A'; /* Allow/Deny */
} else {
}
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
else {
int l = strlen(p0); int l = strlen(p0);
if(strncmp(p0, path, l) == 0 && if(strncmp(p0, path, l) == 0 &&
@ -1313,16 +1262,34 @@ static int checkPerm(const char *path, const char *request)
prev = p0; prev = p0;
} }
} }
#endif
} /* for */ } /* for */
#ifndef CONFIG_FEATURE_HTTPD_BASIC_AUTH return prev == NULL;
}
#else /* ifndef CONFIG_FEATURE_HTTPD_BASIC_AUTH */
static int checkPermIP(const char *request)
{
Htaccess * cur;
const char *p;
/* This could stand some work */
for (cur = config->ip_a_d; cur; cur = cur->next) {
p = cur->after_colon;
#ifdef DEBUG
if (config->debugHttpd)
fprintf(stderr, "checkPerm: '%s' ? '%s'\n",
(*p ? p : "*"), request);
#endif
if(strncmp(p, request, strlen(p)) == 0)
return *cur->before_colon == 'A'; /* Allow/Deny */
}
/* if uncofigured, return 1 - access from all */ /* if uncofigured, return 1 - access from all */
return 1; return 1;
#else
return prev == NULL;
#endif
} }
#define checkPerm(null, request) checkPermIP(request)
#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
/**************************************************************************** /****************************************************************************
@ -1347,6 +1314,7 @@ static void handleIncoming(void)
#endif #endif
char *test; char *test;
struct stat sb; struct stat sb;
int ip_allowed;
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
int credentials = -1; /* if not requred this is Ok */ int credentials = -1; /* if not requred this is Ok */
@ -1382,6 +1350,7 @@ BAD_REQUEST:
*purl = ' '; *purl = ' ';
count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank); count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
decodeString(buf, 0);
if (count < 1 || buf[0] != '/') { if (count < 1 || buf[0] != '/') {
/* Garbled request/URL */ /* Garbled request/URL */
goto BAD_REQUEST; goto BAD_REQUEST;
@ -1394,13 +1363,11 @@ BAD_REQUEST:
strcpy(url, buf); strcpy(url, buf);
/* extract url args if present */ /* extract url args if present */
urlArgs = strchr(url, '?'); urlArgs = strchr(url, '?');
if (urlArgs) { if (urlArgs)
*urlArgs++ = 0; /* next code can set '/' to this pointer, *urlArgs++ = 0;
but CGI script can`t be a directory */
}
/* algorithm stolen from libbb bb_simplify_path(), /* algorithm stolen from libbb bb_simplify_path(),
but don`t strdup and reducing trailing slash */ but don`t strdup and reducing trailing slash and protect out root */
purl = test = url; purl = test = url;
do { do {
@ -1441,12 +1408,14 @@ BAD_REQUEST:
#endif #endif
test = url; test = url;
while((test = strchr( test + 1, '/' )) != NULL) { ip_allowed = checkPerm(NULL, config->rmt_ip);
while(ip_allowed && (test = strchr( test + 1, '/' )) != NULL) {
/* have path1/path2 */ /* have path1/path2 */
*test = '\0'; *test = '\0';
if( is_directory(url + 1, 1, &sb) ) { if( is_directory(url + 1, 1, &sb) ) {
/* may be having subdir config */ /* may be having subdir config */
parse_conf(url + 1, SUBDIR_PARSE); parse_conf(url + 1, SUBDIR_PARSE);
ip_allowed = checkPerm(NULL, config->rmt_ip);
} }
*test = '/'; *test = '/';
} }
@ -1490,8 +1459,7 @@ BAD_REQUEST:
} /* while extra header reading */ } /* while extra header reading */
if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
checkPerm(NULL, config->rmt_ip) == 0) {
/* protect listing [/path]/httpd_conf or IP deny */ /* protect listing [/path]/httpd_conf or IP deny */
#ifdef CONFIG_FEATURE_HTTPD_CGI #ifdef CONFIG_FEATURE_HTTPD_CGI
FORBIDDEN: /* protect listing /cgi-bin */ FORBIDDEN: /* protect listing /cgi-bin */
@ -1525,8 +1493,8 @@ FORBIDDEN: /* protect listing /cgi-bin */
} }
if (strncmp(test, "cgi-bin", 7) == 0) { if (strncmp(test, "cgi-bin", 7) == 0) {
if(test[7] == 0 || (test[7] == '/' && test[8] == 0)) if(test[7] == '/' && test[8] == 0)
goto FORBIDDEN; // protect listing cgi-bin goto FORBIDDEN; // protect listing cgi-bin/
sendCgi(url, prequest, urlArgs, body, length, cookie); sendCgi(url, prequest, urlArgs, body, length, cookie);
} else { } else {
if (prequest != request_GET) if (prequest != request_GET)
@ -1587,7 +1555,6 @@ FORBIDDEN: /* protect listing /cgi-bin */
static int miniHttpd(int server) static int miniHttpd(int server)
{ {
fd_set readfd, portfd; fd_set readfd, portfd;
int nfound;
FD_ZERO(&portfd); FD_ZERO(&portfd);
FD_SET(server, &portfd); FD_SET(server, &portfd);
@ -1597,16 +1564,7 @@ static int miniHttpd(int server)
readfd = portfd; readfd = portfd;
/* Now wait INDEFINATELY on the set of sockets! */ /* Now wait INDEFINATELY on the set of sockets! */
nfound = select(server + 1, &readfd, 0, 0, 0); if (select(server + 1, &readfd, 0, 0, 0) > 0) {
switch (nfound) {
case 0:
/* select timeout error! */
break ;
case -1:
/* select error */
break;
default:
if (FD_ISSET(server, &readfd)) { if (FD_ISSET(server, &readfd)) {
int on; int on;
struct sockaddr_in fromAddr; struct sockaddr_in fromAddr;
@ -1639,6 +1597,10 @@ static int miniHttpd(int server)
if (config->debugHttpd || fork() == 0) { if (config->debugHttpd || fork() == 0) {
/* This is the spawned thread */ /* This is the spawned thread */
#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
/* protect reload config, may be confuse checking */
signal(SIGHUP, SIG_IGN);
#endif
handleIncoming(); handleIncoming();
if(!config->debugHttpd) if(!config->debugHttpd)
exit(0); exit(0);
@ -1682,7 +1644,7 @@ static void sighup_handler(int sig)
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; sa.sa_flags = SA_RESTART;
sigaction(SIGHUP, &sa, NULL); sigaction(SIGHUP, &sa, NULL);
parse_conf(default_patch_httpd_conf, parse_conf(default_path_httpd_conf,
sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE); sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
} }
#endif #endif
@ -1716,16 +1678,13 @@ int httpd_main(int argc, char *argv[])
/* check if user supplied a port number */ /* check if user supplied a port number */
for (;;) { for (;;) {
int c = getopt( argc, argv, "c:h:" int c = getopt( argc, argv, "c:d:h:"
#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
"p:v" "p:v"
#endif #endif
#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
"e:" "e:"
#endif #endif
#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
"d:"
#endif
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
"r:" "r:"
#endif #endif
@ -1751,11 +1710,9 @@ int httpd_main(int argc, char *argv[])
bb_error_msg_and_die("invalid %s for -p", optarg); bb_error_msg_and_die("invalid %s for -p", optarg);
break; break;
#endif #endif
#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
case 'd': case 'd':
printf("%s", decodeString(optarg)); printf("%s", decodeString(optarg, 1));
return 0; return 0;
#endif
#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
case 'e': case 'e':
printf("%s", encodeString(optarg)); printf("%s", encodeString(optarg));
@ -1803,7 +1760,7 @@ int httpd_main(int argc, char *argv[])
#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
sighup_handler(0); sighup_handler(0);
#else #else
parse_conf(default_patch_httpd_conf, FIRST_PARSE); parse_conf(default_path_httpd_conf, FIRST_PARSE);
#endif #endif
#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY