Browse Source

uhttpd: add option to reject requests from RFC1918 IPs to public server IPs (DNS rebinding countermeasure)

SVN-Revision: 22589
Jo-Philipp Wich 15 years ago
parent
commit
3d99f03082

+ 1 - 1
package/uhttpd/Makefile

@@ -8,7 +8,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=uhttpd
-PKG_RELEASE:=12
+PKG_RELEASE:=13
 
 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
 PKG_BUILD_DEPENDS := libcyassl liblua

+ 5 - 0
package/uhttpd/files/uhttpd.config

@@ -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.

+ 1 - 0
package/uhttpd/files/uhttpd.init

@@ -75,6 +75,7 @@ start_instance()
 
 	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

+ 15 - 0
package/uhttpd/src/uhttpd-utils.c

@@ -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)
 {

+ 1 - 0
package/uhttpd/src/uhttpd-utils.h

@@ -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);
 

+ 14 - 1
package/uhttpd/src/uhttpd.c

@@ -524,7 +524,7 @@ int main (int argc, char **argv)
 #endif
 
 	while( (opt = getopt(argc, argv,
-		"fSDC:K:E:I:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0
+		"fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0
 	) {
 		switch(opt)
 		{
@@ -648,6 +648,10 @@ int main (int argc, char **argv)
 				conf.no_dirlists = 1;
 				break;
 
+			case 'R':
+				conf.rfc1918_filter = 1;
+				break;
+
 #ifdef HAVE_CGI
 			/* cgi prefix */
 			case 'x':
@@ -728,6 +732,7 @@ int main (int argc, char **argv)
 					"	-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"
@@ -932,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) )

+ 1 - 0
package/uhttpd/src/uhttpd.h

@@ -69,6 +69,7 @@ struct config {
 	int no_symlinks;
 	int no_dirlists;
 	int network_timeout;
+	int rfc1918_filter;
 #ifdef HAVE_CGI
 	char *cgi_prefix;
 #endif