last_patch_109 from Vladimir N. Oleynik
authorGlenn L McGrath <bug1@ihug.co.nz>
Mon, 8 Sep 2003 10:59:27 +0000 (10:59 -0000)
committerGlenn L McGrath <bug1@ihug.co.nz>
Mon, 8 Sep 2003 10:59:27 +0000 (10:59 -0000)
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

index 8c87e7e9188e50659f21d39482dd57c3c5fb22f6..eb03f34c0d8d27f5c5b2df1e8d7156404822b6b8 100644 (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.
 #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