From b65422cf652c3f04cf6edd4c6050186df25e844c Mon Sep 17 00:00:00 2001 From: Glenn L McGrath Date: Mon, 8 Sep 2003 10:59:27 +0000 Subject: [PATCH] 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. --- networking/httpd.c | 287 +++++++++++++++++++++++++++++++-------------- 1 file changed, 198 insertions(+), 89 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index 8c87e7e91..eb03f34c0 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -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 -- 2.25.1