[backfire] merge r22589
authorJo-Philipp Wich <jow@openwrt.org>
Wed, 11 Aug 2010 00:23:13 +0000 (00:23 +0000)
committerJo-Philipp Wich <jow@openwrt.org>
Wed, 11 Aug 2010 00:23:13 +0000 (00:23 +0000)
SVN-Revision: 22590

package/uhttpd/Makefile
package/uhttpd/files/uhttpd.config
package/uhttpd/files/uhttpd.init
package/uhttpd/src/uhttpd-cgi.c
package/uhttpd/src/uhttpd-file.c
package/uhttpd/src/uhttpd-utils.c
package/uhttpd/src/uhttpd-utils.h
package/uhttpd/src/uhttpd.c
package/uhttpd/src/uhttpd.h

index eb4238c90411230648fb4dbdf3ee0ad6a2aeb507..8a7b5295d9eedff3911676d705bba02fc5a8f860 100644 (file)
@@ -8,9 +8,10 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=uhttpd
-PKG_RELEASE:=11
+PKG_RELEASE:=13
 
 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+PKG_BUILD_DEPENDS := libcyassl liblua
 
 include $(INCLUDE_DIR)/package.mk
 
index acdd62ea4eb471aba1e9f5f9397ad1715ff008bb..534e8f8b29da31f1e33df1a2975250fbccd25742 100644 (file)
@@ -12,6 +12,11 @@ config uhttpd main
        # Server document root
        option home             /www
 
+       # Reject requests from RFC1918 IP addresses
+       # directed to the servers public IP(s).
+       # This is a DNS rebinding countermeasure.
+       option rfc1918_filter 1
+
        # Certificate and private key for HTTPS.
        # If no listen_https addresses are given,
        # the key options are ignored.
index 58f980c42f2dc92696f1ddfa6cc8da4b40a71cfe..b00b2e281b97898c784ef986d9c4b71f5ba0f2fc 100755 (executable)
@@ -17,6 +17,17 @@ append_arg() {
        [ -n "$val" -o -n "$def" ] && append UHTTPD_ARGS "$opt ${val:-$def}"
 }
 
+append_bool() {
+       local cfg="$1"
+       local var="$2"
+       local opt="$3"
+       local def="$4"
+       local val
+
+       config_get_bool val "$cfg" "$var" "$def"
+       [ "$val" = 1 ] && append UHTTPD_ARGS "$opt"
+}
+
 generate_keys() {
        local cfg="$1"
        local key="$2"
@@ -59,6 +70,12 @@ start_instance()
        append_arg "$cfg" lua_handler "-L"
        append_arg "$cfg" script_timeout "-t"
        append_arg "$cfg" network_timeout "-T"
+       append_arg "$cfg" error_page "-E"
+       append_arg "$cfg" index_page "-I"
+
+       append_bool "$cfg" no_symlinks "-S" 0
+       append_bool "$cfg" no_dirlists "-D" 0
+       append_bool "$cfg" rfc1918_filter "-R" 0
 
        config_get http "$cfg" listen_http
        for listen in $http; do
index 855a72f5697dbff4001b22a59a5eab3475acf44e..08612491624d0c8c1303152b6e7b4732153caa3c 100644 (file)
@@ -234,6 +234,17 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                                if( pi->info )
                                        setenv("PATH_INFO", pi->info, 1);
 
+                               /* REDIRECT_STATUS, php-cgi wants it */
+                               switch( req->redirect_status )
+                               {
+                                       case 404:
+                                               setenv("REDIRECT_STATUS", "404", 1);
+                                               break;
+
+                                       default:
+                                               setenv("REDIRECT_STATUS", "200", 1);
+                                               break;
+                               }
 
                                /* http version */
                                if( req->version > 1.0 )
index 81f66a34b0250c81890152587ab828b569fb89f6..850a14175ca33c0f3b44e10419015e1c9dfef8ea 100644 (file)
@@ -29,7 +29,7 @@
 static const char * uh_file_mime_lookup(const char *path)
 {
        struct mimetype *m = &uh_mime_types[0];
-       char *e;
+       const char *e;
 
        while( m->extn )
        {
@@ -275,7 +275,9 @@ static void uh_file_dirlist(struct client *cl, struct http_request *req, struct
                        strncat(filename, files[i]->d_name,
                                sizeof(filename) - strlen(files[i]->d_name));
 
-                       if( !stat(filename, &s) && (s.st_mode & S_IFDIR) )
+                       if( !stat(filename, &s) &&
+                           (s.st_mode & S_IFDIR) && (s.st_mode & S_IXOTH)
+                       )
                                uh_http_sendf(cl, req,
                                        "<li><strong><a href='%s%s'>%s</a>/</strong><br />"
                                        "<small>modified: %s<br />directory - %.02f kbyte"
@@ -293,7 +295,9 @@ static void uh_file_dirlist(struct client *cl, struct http_request *req, struct
                        strncat(filename, files[i]->d_name,
                                sizeof(filename) - strlen(files[i]->d_name));
 
-                       if( !stat(filename, &s) && !(s.st_mode & S_IFDIR) )
+                       if( !stat(filename, &s) &&
+                           !(s.st_mode & S_IFDIR) && (s.st_mode & S_IROTH)
+                       )
                                uh_http_sendf(cl, req,
                                        "<li><strong><a href='%s%s'>%s</a></strong><br />"
                                        "<small>modified: %s<br />%s - %.02f kbyte<br />"
@@ -369,7 +373,7 @@ void uh_file_request(struct client *cl, struct http_request *req, struct path_in
        }
 
        /* directory */
-       else if( pi->stat.st_mode & S_IFDIR )
+       else if( (pi->stat.st_mode & S_IFDIR) && !cl->server->conf->no_dirlists )
        {
                /* write status */
                uh_file_response_200(cl, req, NULL);
index caa6b12bc684b64fa3eff9e32482cd6e1c82ad47..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)
 {
@@ -464,6 +479,9 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url)
        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));
@@ -550,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;
index a6448b63bc1d1e9055ed67edd7c6b8ff8c71ed96..1b18265417910ad402e125dd5e924a4c54aac0ea 100644 (file)
@@ -49,6 +49,7 @@ struct path_info {
 const char * sa_straddr(void *sa);
 const char * sa_strport(void *sa);
 int sa_port(void *sa);
+int sa_rfc1918(void *sa);
 
 char *strfind(char *haystack, int hslen, const char *needle, int ndlen);
 
index 2f77a32a965c794f1c5006239af0aa6b9065daea..be882470ad0b0988605a8e695c3e44488b3f1165 100644 (file)
@@ -47,7 +47,7 @@ static void uh_sigchld(int sig)
        while( waitpid(-1, NULL, WNOHANG) > 0 ) { }
 }
 
-static void uh_config_parse(const char *path)
+static void uh_config_parse(struct config *conf)
 {
        FILE *c;
        char line[512];
@@ -55,7 +55,10 @@ static void uh_config_parse(const char *path)
        char *pass = NULL;
        char *eol  = NULL;
 
-       if( (c = fopen(path ? path : "/etc/httpd.conf", "r")) != NULL )
+       const char *path = conf->file ? conf->file : "/etc/httpd.conf";
+
+
+       if( (c = fopen(path, "r")) != NULL )
        {
                memset(line, 0, sizeof(line));
 
@@ -74,9 +77,23 @@ static void uh_config_parse(const char *path)
                                                "Notice: No password set for user %s, ignoring "
                                                "authentication on %s\n", user, line
                                        );
+                               }
+                       }
+                       else if( !strncmp(line, "I:", 2) )
+                       {
+                               if( !(user = strchr(line, ':')) || (*user++ = 0) ||
+                                   !(eol = strchr(user, '\n')) || (*eol++  = 0) )
+                                       continue;
 
-                                       break;
-                               } 
+                               conf->index_file = strdup(user);
+                       }
+                       else if( !strncmp(line, "E404:", 5) )
+                       {
+                               if( !(user = strchr(line, ':')) || (*user++ = 0) ||
+                                   !(eol = strchr(user, '\n')) || (*eol++  = 0) )
+                                               continue;
+
+                               conf->error_handler = strdup(user);
                        }
                }
 
@@ -302,6 +319,7 @@ static struct http_request * uh_http_header_parse(struct client *cl, char *buffe
                }
 
                /* valid enough */
+               req.redirect_status = 200;
                return &req;
        }
 
@@ -505,8 +523,9 @@ int main (int argc, char **argv)
        }
 #endif
 
-       while( (opt = getopt(argc, argv, "fSC:K:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0 )
-       {
+       while( (opt = getopt(argc, argv,
+               "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0
+       ) {
                switch(opt)
                {
                        /* [addr:]port */
@@ -597,11 +616,42 @@ int main (int argc, char **argv)
                                }
                                break;
 
+                       /* error handler */
+                       case 'E':
+                               if( (strlen(optarg) == 0) || (optarg[0] != '/') )
+                               {
+                                       fprintf(stderr, "Error: Invalid error handler: %s\n",
+                                               optarg);
+                                       exit(1);
+                               }
+                               conf.error_handler = optarg;
+                               break;
+
+                       /* index file */
+                       case 'I':
+                               if( (strlen(optarg) == 0) || (optarg[0] == '/') )
+                               {
+                                       fprintf(stderr, "Error: Invalid index page: %s\n",
+                                               optarg);
+                                       exit(1);
+                               }
+                               conf.index_file = optarg;
+                               break;
+
                        /* don't follow symlinks */
                        case 'S':
                                conf.no_symlinks = 1;
                                break;
 
+                       /* don't list directories */
+                       case 'D':
+                               conf.no_dirlists = 1;
+                               break;
+
+                       case 'R':
+                               conf.rfc1918_filter = 1;
+                               break;
+
 #ifdef HAVE_CGI
                        /* cgi prefix */
                        case 'x':
@@ -678,7 +728,11 @@ int main (int argc, char **argv)
                                        "       -K file         ASN.1 server private key file\n"
 #endif
                                        "       -h directory    Specify the document root, default is '.'\n"
+                                       "       -E string       Use given virtual URL as 404 error handler\n"
+                                       "       -I string       Use given filename as index page for directories\n"
                                        "       -S              Do not follow symbolic links outside of the docroot\n"
+                                       "       -D              Do not allow directory listings, send 403 instead\n"
+                                       "       -R              Enable RFC1918 filter\n"
 #ifdef HAVE_LUA
                                        "       -l string       URL prefix for Lua handler, default is '/lua'\n"
                                        "       -L file         Lua handler script, omit to disable Lua\n"
@@ -727,7 +781,7 @@ int main (int argc, char **argv)
                conf.realm = "Protected Area";
 
        /* config file */
-       uh_config_parse(conf.file);
+       uh_config_parse(&conf);
 
        /* default network timeout */
        if( conf.network_timeout <= 0 )
@@ -883,6 +937,14 @@ int main (int argc, char **argv)
                                        /* parse message header */
                                        if( (req = uh_http_header_recv(cl)) != NULL )
                                        {
+                                               /* RFC1918 filtering required? */
+                                               if( conf.rfc1918_filter && sa_rfc1918(&cl->peeraddr) &&
+                                                   !sa_rfc1918(&cl->servaddr) )
+                                               {
+                                                       uh_http_sendhf(cl, 403, "Forbidden",
+                                                               "Rejected request from RFC1918 IP to public server address");
+                                               }
+                                               else
 #ifdef HAVE_LUA
                                                /* Lua request? */
                                                if( L && uh_path_match(conf.lua_prefix, req->url) )
@@ -913,8 +975,29 @@ int main (int argc, char **argv)
                                                /* 404 */
                                                else
                                                {
-                                                       uh_http_sendhf(cl, 404, "Not Found",
-                                                               "No such file or directory");
+                                                       /* Try to invoke an error handler */
+                                                       pin = uh_path_lookup(cl, conf.error_handler);
+
+                                                       if( pin && uh_auth_check(cl, req, pin) )
+                                                       {
+                                                               req->redirect_status = 404;
+
+#ifdef HAVE_CGI
+                                                               if( uh_path_match(conf.cgi_prefix, pin->name) )
+                                                               {
+                                                                       uh_cgi_request(cl, req, pin);
+                                                               }
+                                                               else
+#endif
+                                                               {
+                                                                       uh_file_request(cl, req, pin);
+                                                               }
+                                                       }
+                                                       else
+                                                       {
+                                                               uh_http_sendhf(cl, 404, "Not Found",
+                                                                       "No such file or directory");
+                                                       }
                                                }
                                        }
 
index 32e39700722c7c41488539ad1d197fa71a77f181..fd2176ebdd9c7a3fc5e4470698e8c6bb2c352b4f 100644 (file)
@@ -64,8 +64,12 @@ struct config {
        char docroot[PATH_MAX];
        char *realm;
        char *file;
+       char *index_file;
+       char *error_handler;
        int no_symlinks;
+       int no_dirlists;
        int network_timeout;
+       int rfc1918_filter;
 #ifdef HAVE_CGI
        char *cgi_prefix;
 #endif
@@ -124,6 +128,7 @@ struct auth_realm {
 struct http_request {
        int     method;
        float version;
+       int redirect_status;
        char *url;
        char *headers[UH_LIMIT_HEADERS];
        struct auth_realm *realm;