[package] uhttpd: add option to reject requests from RFC1918 IPs to public server...
[openwrt/svn-archive/archive.git] / package / uhttpd / src / uhttpd-utils.c
index c1e08b069586b2ee5484d05a97b61cde4c4da325..4a1423c715d0db9e9b6f7d1ff44f34250574b336 100644 (file)
@@ -59,6 +59,21 @@ int sa_port(void *sa)
        return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
 }
 
+int sa_rfc1918(void *sa)
+{
+       struct sockaddr_in *v4 = (struct sockaddr_in *)sa;
+       unsigned long a = htonl(v4->sin_addr.s_addr);
+
+       if( v4->sin_family == AF_INET )
+       {
+               return ((a >= 0x0A000000) && (a <= 0x0AFFFFFF)) ||
+                      ((a >= 0xAC100000) && (a <= 0xAC1FFFFF)) ||
+                      ((a >= 0xC0A80000) && (a <= 0xC0A8FFFF));
+       }
+
+       return 0;
+}
+
 /* Simple strstr() like function that takes len arguments for both haystack and needle. */
 char *strfind(char *haystack, int hslen, const char *needle, int ndlen)
 {
@@ -88,6 +103,25 @@ char *strfind(char *haystack, int hslen, const char *needle, int ndlen)
        return NULL;
 }
 
+/* interruptable select() */
+int select_intr(int n, fd_set *r, fd_set *w, fd_set *e, struct timeval *t)
+{
+       int rv;
+       sigset_t ssn, sso;
+
+       /* unblock SIGCHLD */
+       sigemptyset(&ssn);
+       sigaddset(&ssn, SIGCHLD);
+       sigprocmask(SIG_UNBLOCK, &ssn, &sso);
+
+       rv = select(n, r, w, e, t);
+
+       /* restore signal mask */
+       sigprocmask(SIG_SETMASK, &sso, NULL);
+
+       return rv;
+}
+
 
 int uh_tcp_send(struct client *cl, const char *buf, int len)
 {
@@ -97,8 +131,8 @@ int uh_tcp_send(struct client *cl, const char *buf, int len)
        FD_ZERO(&writer);
        FD_SET(cl->socket, &writer);
 
-       timeout.tv_sec = 0;
-       timeout.tv_usec = 500000;
+       timeout.tv_sec = cl->server->conf->network_timeout;
+       timeout.tv_usec = 0;
 
        if( select(cl->socket + 1, NULL, &writer, NULL, &timeout) > 0 )
        {
@@ -357,6 +391,79 @@ int uh_b64decode(char *buf, int blen, const unsigned char *src, int slen)
        return len;
 }
 
+static char * canonpath(const char *path, char *path_resolved)
+{
+       char path_copy[PATH_MAX];
+       char *path_cpy = path_copy;
+       char *path_res = path_resolved;
+
+       struct stat s;
+
+
+       /* relative -> absolute */
+       if( *path != '/' )
+       {
+               getcwd(path_copy, PATH_MAX);
+               strncat(path_copy, "/", PATH_MAX - strlen(path_copy));
+               strncat(path_copy, path, PATH_MAX - strlen(path_copy));
+       }
+       else
+       {
+               strncpy(path_copy, path, PATH_MAX);
+       }
+
+       /* normalize */
+       while( (*path_cpy != '\0') && (path_cpy < (path_copy + PATH_MAX - 2)) )
+       {
+               if( *path_cpy == '/' )
+               {
+                       /* skip repeating / */
+                       if( path_cpy[1] == '/' )
+                       {
+                               path_cpy++;
+                               continue;
+                       }
+
+                       /* /./ or /../ */
+                       else if( path_cpy[1] == '.' )
+                       {
+                               /* skip /./ */
+                               if( (path_cpy[2] == '/') || (path_cpy[2] == '\0') )
+                               {
+                                       path_cpy += 2;
+                                       continue;
+                               }
+
+                               /* collapse /x/../ */
+                               else if( (path_cpy[2] == '.') &&
+                                        ((path_cpy[3] == '/') || (path_cpy[3] == '\0'))
+                               ) {
+                                       while( (path_res > path_resolved) && (*--path_res != '/') )
+                                               ;
+
+                                       path_cpy += 3;
+                                       continue;
+                               }
+                       }
+               }
+
+               *path_res++ = *path_cpy++;
+       }
+
+       /* remove trailing slash if not root / */
+       if( (path_res > (path_resolved+1)) && (path_res[-1] == '/') )
+               path_res--;
+       else if( path_res == path_resolved )
+               *path_res++ = '/';
+
+       *path_res = '\0';
+
+       /* test access */
+       if( !stat(path_resolved, &s) && (s.st_mode & S_IROTH) )
+               return path_resolved;
+
+       return NULL;
+}
 
 struct path_info * uh_path_lookup(struct client *cl, const char *url)
 {
@@ -368,9 +475,13 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url)
        char *docroot = cl->server->conf->docroot;
        char *pathptr = NULL;
 
+       int no_sym = cl->server->conf->no_symlinks;
        int i = 0;
        struct stat s;
 
+       /* back out early if url is undefined */
+       if ( url == NULL )
+               return NULL;
 
        memset(path_phys, 0, sizeof(path_phys));
        memset(path_info, 0, sizeof(path_info));
@@ -413,8 +524,9 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url)
                        memset(path_info, 0, sizeof(path_info));
                        memcpy(path_info, buffer, min(i + 1, sizeof(path_info) - 1));
 
-                       if( realpath(path_info, path_phys) )
-                       {
+                       if( no_sym ? realpath(path_info, path_phys)
+                                  : canonpath(path_info, path_phys)
+                       ) {
                                memset(path_info, 0, sizeof(path_info));
                                memcpy(path_info, &buffer[i],
                                        min(strlen(buffer) - i, sizeof(path_info) - 1));
@@ -456,18 +568,31 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url)
                        memcpy(buffer, path_phys, sizeof(buffer));
                        pathptr = &buffer[strlen(buffer)];
 
-                       for( i = 0; i < array_size(uh_index_files); i++ )
+                       if( cl->server->conf->index_file )
                        {
-                               strncat(buffer, uh_index_files[i], sizeof(buffer));
+                               strncat(buffer, cl->server->conf->index_file, sizeof(buffer));
 
                                if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
                                {
                                        memcpy(path_phys, buffer, sizeof(path_phys));
                                        memcpy(&p.stat, &s, sizeof(p.stat));
-                                       break;
                                }
+                       }
+                       else
+                       {
+                               for( i = 0; i < array_size(uh_index_files); i++ )
+                               {
+                                       strncat(buffer, uh_index_files[i], sizeof(buffer));
 
-                               *pathptr = 0;
+                                       if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
+                                       {
+                                               memcpy(path_phys, buffer, sizeof(path_phys));
+                                               memcpy(&p.stat, &s, sizeof(p.stat));
+                                               break;
+                                       }
+
+                                       *pathptr = 0;
+                               }
                        }
 
                        p.root = docroot;
@@ -528,10 +653,14 @@ struct auth_realm * uh_auth_add(char *path, char *user, char *pass)
                                min(strlen(pass), sizeof(new->pass) - 1));
                }
 
-               uh_realm_count++;
+               if( new->pass[0] )
+               {
+                       uh_realm_count++;
+                       return new;
+               }
        }
 
-       return new;
+       return NULL;
 }
 
 int uh_auth_check(