last_patch_109 from Vladimir N. Oleynik

Busybox`s httpd have the defect (from born):
ip 1.2.3.1 have true comparing also with
1.2.3.10-1.2.3.19 and 1.2.3.100-1.2.3.199.
Last patch removed this bug and added feature:
allow/deny rule can support network/netmask
example: 1.2.3.0/255.255.255.128
  or
network/mask_bits
example: 1.2.3.0/25
now; old format
1
1.2
1.2.3
1.2.3.4
too support and converted to
1/8 1.2/16 1.2.3/24 1.2.3.4/32
automaticaly.

Also, current CVS have small problem: ignores
A:IP, (loses 'A', 'a' only work). Corrected.
This commit is contained in:
Glenn L McGrath 2003-09-08 10:59:27 +00:00
parent 350733abb8
commit b65422cf65

View File

@ -49,9 +49,9 @@
*
* httpd.conf has the following format:
*
* A:172.20. # Allow any address that begins with 172.20
* A:10.10. # Allow any address that begins with 10.10.
* A:10.20 # Allow any address that previous set and 10.200-209.X.X
* A:172.20. # Allow address from 172.20.0.0/16
* A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127
* A:10.0.0.0/255.255.255.128 # Allow any address that previous set
* A:127.0.0.1 # Allow local loopback connections
* D:* # Deny from other IP connections
* /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
@ -75,9 +75,8 @@
*
* Example:
* 1. Allow only specified addresses
* A:172.20. # Allow any address that begins with 172.20
* A:172.20 # Allow any address that begins with 172.20.
* A:10.10. # Allow any address that begins with 10.10.
* A:10.10 # Allow any address that previous set and 10.100-109.X.X
* A:127.0.0.1 # Allow local loopback connections
* D:* # Deny from other IP connections
*
@ -87,8 +86,7 @@
* A:* # (optional line added for clarity)
*
* If a sub directory contains a config file it is parsed and merged with
* any existing settings as if it was appended to the original configuration
* except that all previous IP config rules are discarded.
* any existing settings as if it was appended to the original configuration.
*
* subdir paths are relative to the containing subdir and thus cannot
* affect the parent rules.
@ -122,7 +120,7 @@
#include "busybox.h"
static const char httpdVersion[] = "busybox httpd/1.28 22-Jun-2003";
static const char httpdVersion[] = "busybox httpd/1.30 7-Sep-2003";
static const char default_path_httpd_conf[] = "/etc";
static const char httpd_conf[] = "httpd.conf";
static const char home[] = "./";
@ -221,6 +219,13 @@ typedef struct HT_ACCESS {
char before_colon[1]; /* really bigger, must last */
} Htaccess;
typedef struct HT_ACCESS_IP {
unsigned int ip;
unsigned int mask;
int allow_deny;
struct HT_ACCESS_IP *next;
} Htaccess_IP;
typedef struct
{
#ifdef CONFIG_FEATURE_HTTPD_CGI
@ -234,7 +239,10 @@ typedef struct
#endif
const char *configFile;
char rmt_ip[16]; /* for set env REMOTE_ADDR */
unsigned int rmt_ip;
#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
char rmt_ip_str[16]; /* for set env REMOTE_ADDR */
#endif
unsigned port; /* server initial port and for
set env REMOTE_PORT */
@ -242,7 +250,7 @@ typedef struct
off_t ContentLength; /* -1 - unknown */
time_t last_mod;
Htaccess *ip_a_d; /* config allow/deny lines */
Htaccess_IP *ip_a_d; /* config allow/deny lines */
int flg_deny_all;
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
Htaccess *auth; /* config user:password lines */
@ -353,7 +361,83 @@ static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
static const char Content_length[] = "Content-length:";
static int
scan_ip (const char **ep, unsigned int *ip, unsigned char endc)
{
const char *p = *ep;
int auto_mask = 8;
int j;
*ip = 0;
for (j = 0; j < 4; j++) {
unsigned int octet;
if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0)
return -auto_mask;
octet = 0;
while (*p >= '0' && *p <= '9') {
octet *= 10;
octet += *p - '0';
if (octet > 255)
return -auto_mask;
p++;
}
if (*p == '.')
p++;
if (*p != '/' && *p != 0)
auto_mask += 8;
*ip = ((*ip) << 8) | octet;
}
if (*p != 0) {
if (*p != endc)
return -auto_mask;
p++;
if(*p == 0)
return -auto_mask;
}
*ep = p;
return auto_mask;
}
static int
scan_ip_mask (const char *ipm, unsigned int *ip, unsigned int *mask)
{
int i;
unsigned int msk;
i = scan_ip(&ipm, ip, '/');
if(i < 0)
return i;
if(*ipm) {
const char *p = ipm;
i = 0;
while (*p) {
if (*p < '0' || *p > '9') {
if (*p == '.') {
i = scan_ip (&ipm, mask, 0);
return i != 32;
}
return -1;
}
i *= 10;
i += *p - '0';
p++;
}
}
if (i > 32 || i < 0)
return -1;
msk = 0x80000000;
*mask = 0;
while (i > 0) {
*mask |= msk;
msk >>= 1;
i--;
}
return 0;
}
#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
static void free_config_lines(Htaccess **pprev)
{
Htaccess *prev = *pprev;
@ -366,6 +450,7 @@ static void free_config_lines(Htaccess **pprev)
}
*pprev = NULL;
}
#endif
/* flag */
#define FIRST_PARSE 0
@ -411,18 +496,29 @@ static void parse_conf(const char *path, int flag)
char *c, *p;
/* free previous ip setup if present */
free_config_lines(&config->ip_a_d);
Htaccess_IP *pip = config->ip_a_d;
while( pip ) {
Htaccess_IP *cur_ipl = pip;
pip = cur_ipl->next;
free(cur_ipl);
}
config->ip_a_d = NULL;
config->flg_deny_all = 0;
#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
/* retain previous auth and mime config only for subdir parse */
if(flag != SUBDIR_PARSE) {
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
free_config_lines(&config->auth)
free_config_lines(&config->auth);
#endif
; /* appease compiler warnings if option is not set */
#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
free_config_lines(&config->mime_a);
#endif
}
#endif
if(flag == SUBDIR_PARSE || cf == NULL) {
cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
@ -477,7 +573,7 @@ static void parse_conf(const char *path, int flag)
if(*p0 == 'a')
*p0 = 'A';
else if(*p0 != 'D'
else if(*p0 != 'D' && *p0 != 'A'
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
&& *p0 != '/'
#endif
@ -486,7 +582,35 @@ static void parse_conf(const char *path, int flag)
#endif
)
continue;
if(*p0 == 'A' || *p0 == 'D') {
/* storing current config IP line */
pip = calloc(1, sizeof(Htaccess_IP));
if(pip) {
if(scan_ip_mask (c, &(pip->ip), &(pip->mask))) {
/* syntax IP{/mask} error detected, protect all */
*p0 = 'D';
pip->mask = 0;
}
pip->allow_deny = *p0;
if(*p0 == 'D') {
/* Deny:form_IP move top */
pip->next = config->ip_a_d;
config->ip_a_d = pip;
} else {
/* add to bottom A:form_IP config line */
Htaccess_IP *prev_IP = config->ip_a_d;
if(prev_IP == NULL) {
config->ip_a_d = pip;
} else {
while(prev_IP->next)
prev_IP = prev_IP->next;
prev_IP->next = pip;
}
}
}
continue;
}
#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
if(*p0 == '/') {
/* make full path from httpd root / curent_path / config_line_path */
@ -526,8 +650,9 @@ static void parse_conf(const char *path, int flag)
sprintf(p0, "%s:%s", p0, c);
}
#endif
/* storing current config line */
#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
/* storing current config line */
cur = calloc(1, sizeof(Htaccess) + strlen(p0));
if(cur) {
cf = strcpy(cur->before_colon, p0);
@ -538,24 +663,6 @@ static void parse_conf(const char *path, int flag)
if(*cf == '/')
free(p0);
#endif
if(*cf == 'A' || *cf == 'D') {
if(*cf == 'D') {
/* Deny:form_IP move top */
cur->next = config->ip_a_d;
config->ip_a_d = cur;
} else {
/* add to bottom A:form_IP config line */
Htaccess *prev_IP = config->ip_a_d;
if(prev_IP == NULL) {
config->ip_a_d = cur;
} else {
while(prev_IP->next)
prev_IP = prev_IP->next;
prev_IP->next = cur;
}
}
}
#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
else if(*cf == '.') {
/* config .mime line move top for overwrite previous */
@ -594,6 +701,7 @@ static void parse_conf(const char *path, int flag)
prev = cur;
}
}
#endif
#endif
}
}
@ -1100,10 +1208,10 @@ static int sendCgi(const char *url,
addEnv("SERVER", "PROTOCOL", "HTTP/1.0");
addEnv("GATEWAY_INTERFACE", "", "CGI/1.1");
#ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
addEnv("REMOTE", "ADDR", config->rmt_ip);
addEnv("REMOTE", "ADDR", config->rmt_ip_str);
addEnvPort("REMOTE");
#else
addEnv("REMOTE_ADDR", "", config->rmt_ip);
addEnv("REMOTE_ADDR", "", config->rmt_ip_str);
#endif
if(bodyLen) {
char sbl[32];
@ -1288,17 +1396,45 @@ static int sendFile(const char *url, char *buf)
return 0;
}
static int checkPermIP(void)
{
Htaccess_IP * cur;
/* This could stand some work */
for (cur = config->ip_a_d; cur; cur = cur->next) {
#ifdef DEBUG
if (config->debugHttpd) {
fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str);
fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
(unsigned char)(cur->ip >> 24),
(unsigned char)(cur->ip >> 16),
(unsigned char)(cur->ip >> 8),
cur->ip & 0xff,
(unsigned char)(cur->mask >> 24),
(unsigned char)(cur->mask >> 16),
(unsigned char)(cur->mask >> 8),
cur->mask & 0xff);
}
#endif
if((config->rmt_ip & cur->mask) == cur->ip)
return cur->allow_deny == 'A'; /* Allow/Deny */
}
/* if uncofigured, return 1 - access from all */
return !config->flg_deny_all;
}
/****************************************************************************
*
> $Function: checkPerm()
*
* $Description: Check the permission file for access.
* $Description: Check the permission file for access password protected.
*
* If config file isn't present, everything is allowed.
* Entries are of the form you can see example from header source
*
* $Parameters:
* (const char *) path . . . . The file path or NULL for ip addresses.
* (const char *) path . . . . The file path.
* (const char *) request . . . User information to validate.
*
* $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise.
@ -1312,25 +1448,19 @@ static int checkPerm(const char *path, const char *request)
const char *p;
const char *p0;
int ipaddr = path == NULL;
const char *prev = NULL;
/* This could stand some work */
for (cur = ipaddr ? config->ip_a_d : config->auth; cur; cur = cur->next) {
for (cur = config->auth; cur; cur = cur->next) {
p0 = cur->before_colon;
if(prev != NULL && strcmp(prev, p0) != 0)
continue; /* find next identical */
p = cur->after_colon;
#ifdef DEBUG
if (config->debugHttpd)
fprintf(stderr,"checkPerm: '%s' ? '%s'\n",
(ipaddr ? (*p ? p : "*") : p0), request);
fprintf(stderr,"checkPerm: '%s' ? '%s'\n", p0, request);
#endif
if(ipaddr) {
if(strncmp(p, request, strlen(p)) != 0)
continue;
return *p0 == 'A'; /* Allow/Deny */
} else {
{
int l = strlen(p0);
if(strncmp(p0, path, l) == 0 &&
@ -1372,33 +1502,9 @@ static int checkPerm(const char *path, const char *request)
}
} /* for */
if(ipaddr)
return !config->flg_deny_all;
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 */
return !config->flg_deny_all;
}
#define checkPerm(null, request) checkPermIP(request)
#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
@ -1518,14 +1624,14 @@ BAD_REQUEST:
#endif
test = url;
ip_allowed = checkPerm(NULL, config->rmt_ip);
ip_allowed = checkPermIP();
while(ip_allowed && (test = strchr( test + 1, '/' )) != NULL) {
/* have path1/path2 */
*test = '\0';
if( is_directory(url + 1, 1, &sb) ) {
/* may be having subdir config */
parse_conf(url + 1, SUBDIR_PARSE);
ip_allowed = checkPerm(NULL, config->rmt_ip);
ip_allowed = checkPermIP();
}
*test = '/';
}
@ -1679,7 +1785,6 @@ static int miniHttpd(int server)
int on;
struct sockaddr_in fromAddr;
unsigned int addr;
socklen_t fromAddrLen = sizeof(fromAddr);
int s = accept(server,
(struct sockaddr *)&fromAddr, &fromAddrLen);
@ -1688,19 +1793,22 @@ static int miniHttpd(int server)
continue;
}
config->accepted_socket = s;
addr = ntohl(fromAddr.sin_addr.s_addr);
sprintf(config->rmt_ip, "%u.%u.%u.%u",
(unsigned char)(addr >> 24),
(unsigned char)(addr >> 16),
(unsigned char)(addr >> 8),
addr & 0xff);
config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr);
#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
(unsigned char)(config->rmt_ip >> 24),
(unsigned char)(config->rmt_ip >> 16),
(unsigned char)(config->rmt_ip >> 8),
config->rmt_ip & 0xff);
config->port = ntohs(fromAddr.sin_port);
#ifdef DEBUG
if (config->debugHttpd) {
bb_error_msg("connection from IP=%s, port %u\n",
config->rmt_ip, config->port);
config->rmt_ip_str, config->port);
}
#endif
#endif /* CONFIG_FEATURE_HTTPD_CGI */
/* set the KEEPALIVE option to cull dead connections */
on = 1;
setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on));
@ -1729,15 +1837,16 @@ static int miniHttpd(void)
{
struct sockaddr_in fromAddrLen;
socklen_t sinlen = sizeof (struct sockaddr_in);
unsigned int addr;
getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen);
addr = ntohl(fromAddrLen.sin_addr.s_addr);
sprintf(config->rmt_ip, "%u.%u.%u.%u",
(unsigned char)(addr >> 24),
(unsigned char)(addr >> 16),
(unsigned char)(addr >> 8),
addr & 0xff);
config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr);
#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
(unsigned char)(config->rmt_ip >> 24),
(unsigned char)(config->rmt_ip >> 16),
(unsigned char)(config->rmt_ip >> 8),
config->rmt_ip & 0xff);
#endif
config->port = ntohs(fromAddrLen.sin_port);
handleIncoming();
return 0;
@ -1750,12 +1859,12 @@ static void sighup_handler(int sig)
/* set and reset */
struct sigaction sa;
parse_conf(default_path_httpd_conf,
sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
sa.sa_handler = sighup_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGHUP, &sa, NULL);
parse_conf(default_path_httpd_conf,
sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
}
#endif