You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Dirk-Willem van Gulik <di...@jrc.it> on 1998/06/23 16:26:41 UTC

getpeername() and strong source blocking

In the context of hardening apache against certain DoS attacks,

Is there any way of finding out _who_ is next in the listen queue prior
to accepting it ? And what is a good way of skipping such. i.e. a way
to improve the following fragment;

{
	listen(fd,x)
	while(1) {
		select(..)
(1)
		s=accept(fd,peer,..)
		if ( peer-ip != one-of-my-friends )
(2)			close(s); shutdown(s,1);
			continue;
		
		find-a-child or fork
		    pass of
			and do somethng usefull...
		..
		};
	}


I.e. I would like to do something selective as early is (1) and
really would not like to do the accept/close/shutdown kinda
thing (2) when it aint no friend.

Any ideas ? (I am currently doing the above to lock certain
badly behaving IP addresses out; and was trying to tie this up
for some-one which caused me to look again.).

Dw.


RE: getpeername() and strong source blocking

Posted by Andy Finkenstadt <an...@simutronics.com>.
Good point!  I had already hardened my server to SYN attacks so adding the
source route would have no additional impact.
Andy


-----Original Message-----
From:	new-httpd-owner@apache.org [mailto:new-httpd-owner@apache.org] On
Behalf Of Marc Slemko
Sent:	Tuesday, June 23, 1998 11:28 AM
To:	new-httpd@apache.org
Subject:	Re: getpeername() and strong source blocking

Someone else suggested adding a route to the loopback interface, which
works but it can cause major problems on systems that are ill-defended
against SYN attacks because that is just what it can look like; each SYN
that arrives will cause a socket to be in SYN_SENT until it eventually
times out.



Re: getpeername() and strong source blocking

Posted by Dirk-Willem van Gulik <di...@jrc.it>.

On Tue, 23 Jun 1998, Marc Slemko wrote:

> No, not for any reasonably current version.
> 
> Essentially what you want to do is just link against the libtcpd, call
> hosts_ctl in Apache right after the connection is accepted but before it
> is handled, then close it and abort if you get a denied result.  It is
> only half a dozen lines or so.
> 
> On Tue, 23 Jun 1998, Dirk-Willem van Gulik wrote:
> 
> > 
> > > > Any ideas ? (I am currently doing the above to lock certain
> > > > badly behaving IP addresses out; and was trying to tie this up
> > > > for some-one which caused me to look again.).
> > > 
> > > If you are looking for a solution within Apache, that is about the best
> > > you can get.  it is how I did my tcpwrapper hook a while ago.
> > > 
> > Is that one still floating round ?
> > 
> > Dw 
> > 
> 
> 


Re: getpeername() and strong source blocking

Posted by Marc Slemko <ma...@worldgate.com>.
No, not for any reasonably current version.

Essentially what you want to do is just link against the libtcpd, call
hosts_ctl in Apache right after the connection is accepted but before it
is handled, then close it and abort if you get a denied result.  It is
only half a dozen lines or so.

On Tue, 23 Jun 1998, Dirk-Willem van Gulik wrote:

> 
> > > Any ideas ? (I am currently doing the above to lock certain
> > > badly behaving IP addresses out; and was trying to tie this up
> > > for some-one which caused me to look again.).
> > 
> > If you are looking for a solution within Apache, that is about the best
> > you can get.  it is how I did my tcpwrapper hook a while ago.
> > 
> Is that one still floating round ?
> 
> Dw 
> 


Re: getpeername() and strong source blocking

Posted by Dirk-Willem van Gulik <di...@jrc.it>.
> > Any ideas ? (I am currently doing the above to lock certain
> > badly behaving IP addresses out; and was trying to tie this up
> > for some-one which caused me to look again.).
> 
> If you are looking for a solution within Apache, that is about the best
> you can get.  it is how I did my tcpwrapper hook a while ago.
> 
Is that one still floating round ?

Dw 


Re: getpeername() and strong source blocking

Posted by Marc Slemko <ma...@worldgate.com>.
On Tue, 23 Jun 1998, Dirk-Willem van Gulik wrote:

> 
> In the context of hardening apache against certain DoS attacks,
> 
> Is there any way of finding out _who_ is next in the listen queue prior
> to accepting it ? And what is a good way of skipping such. i.e. a way

Not with sockets.  Your best bet for that is really to get an OS that lets
you do packet filtering.

Someone else suggested adding a route to the loopback interface, which
works but it can cause major problems on systems that are ill-defended
against SYN attacks because that is just what it can look like; each SYN
that arrives will cause a socket to be in SYN_SENT until it eventually
times out.

[...]
> Any ideas ? (I am currently doing the above to lock certain
> badly behaving IP addresses out; and was trying to tie this up
> for some-one which caused me to look again.).

If you are looking for a solution within Apache, that is about the best
you can get.  it is how I did my tcpwrapper hook a while ago.


Re: getpeername() and strong source blocking

Posted by Dirk-Willem van Gulik <di...@jrc.it>.
On Tue, 23 Jun 1998, Dean Gaudet wrote:

> With linux 2.1.x you can insert a BPF-style filter onto any socket from
> userland.  That, and other firewalling methods are the only things I can
> think of.

Makes sense. I was just playing with the attached; partly caused by some
trouble I had on one box; where endless serious of re-tries where done;
first I thoughd it was password guessing; it turned out to be a de-
installed MSIE4 gone saur.

Dw.

> Index: compat.h
> ===================================================================
> RCS file: /home/cvs//apache-1.3/src/include/compat.h,v
> retrieving revision 1.10
> diff -u -r1.10 compat.h
> --- compat.h	1998/05/28 22:09:46	1.10
> +++ compat.h	1998/06/23 19:07:44
> @@ -349,6 +349,7 @@
>  #define set_etag                       ap_set_etag
>  #define set_file_slot                  ap_set_file_slot
>  #define set_flag_slot                  ap_set_flag_slot
> +#define set_int_slot                   ap_set_int_slot
>  #define set_keepalive                  ap_set_keepalive
>  #define set_last_modified              ap_set_last_modified
>  #define set_module_config              ap_set_module_config
> Index: http_config.h
> ===================================================================
> RCS file: /home/cvs//apache-1.3/src/include/http_config.h,v
> retrieving revision 1.87
> diff -u -r1.87 http_config.h
> --- http_config.h	1998/05/27 14:01:31	1.87
> +++ http_config.h	1998/06/23 19:07:46
> @@ -295,6 +295,7 @@
>  API_EXPORT_NONSTD(const char *) ap_set_string_slot(cmd_parms *, char *, char *);
>  API_EXPORT_NONSTD(const char *) ap_set_string_slot_lower(cmd_parms *, char *, char *);
>  API_EXPORT_NONSTD(const char *) ap_set_flag_slot(cmd_parms *, char *, int);
> +API_EXPORT_NONSTD(const char *) ap_set_int_slot(cmd_parms *, char *, char *);
>  API_EXPORT_NONSTD(const char *) ap_set_file_slot(cmd_parms *, char *, char *);
>  
>  /* For modules which need to read config files, open logs, etc. ...
> Index: scoreboard.h
> ===================================================================
> RCS file: /home/cvs//apache-1.3/src/include/scoreboard.h,v
> retrieving revision 1.40
> diff -u -r1.40 scoreboard.h
> --- scoreboard.h	1998/05/03 17:31:10	1.40
> +++ scoreboard.h	1998/06/23 19:07:46
> @@ -133,6 +133,13 @@
>      char request[64];		/* We just want an idea... */
>      char vhost[32];		/* What virtual host is being accessed? */
>  #endif
> +
> +#ifdef IP_LOCKOUT
> +    struct {			/* list of IP addresses to block right */
> +	time_t last_seen;	/* away; just after the 'accept'. */
> +	struct sockaddr_in remote_addr;
> +	} ip_lockedout[ IP_LOCKOUT ];
> +#endif
>  } short_score;
>  
>  typedef struct {
> 
> Index: http_config.c
> ===================================================================
> RCS file: /home/cvs//apache-1.3/src/main/http_config.c,v
> retrieving revision 1.117
> diff -u -r1.117 http_config.c
> --- http_config.c	1998/06/13 15:22:52	1.117
> +++ http_config.c	1998/06/23 19:07:34
> @@ -1046,6 +1046,16 @@
>      return NULL;
>  }
>  
> +API_EXPORT_NONSTD(const char *) ap_set_int_slot(cmd_parms *cmd,
> +                                              char *struct_ptr, char * arg)
> +{
> +    /* This one's pretty generic too... integers... */
> +
> +    int offset = (int) (long) cmd->info;
> +    *(int *) (struct_ptr + offset) = atoi( arg );
> +    return NULL;
> +}  
> +
>  API_EXPORT_NONSTD(const char *) ap_set_file_slot(cmd_parms *cmd, char *struct_ptr, char *arg)
>  {
>      /* Prepend server_root to relative arg.
> Index: http_main.c
> ===================================================================
> RCS file: /home/cvs//apache-1.3/src/main/http_main.c,v
> retrieving revision 1.365
> diff -u -r1.365 http_main.c
> --- http_main.c	1998/06/16 03:37:27	1.365
> +++ http_main.c	1998/06/23 19:07:52
> @@ -3538,6 +3538,31 @@
>  
>  	SAFE_ACCEPT(accept_mutex_off());	/* unlock after "accept" */
>  
> +#ifdef IP_LOCKOUT
> +	/* we should really do this before accepting the connection
> +	 * with accept; but getpeername() only works this late in
> +	 * the process :-( so we have to do an accept to see who is
> +	 * on the other side of the socket. The other play would be
> +      * with some firewall filter dynamically configured.
> +	 */
> +	{ 
> +	int i,n;
> +        ap_sync_scoreboard_image();
> +     /* double loop, cost of no locking */
> +    	for (n = 0; n < max_daemons_limit; ++n) if 
> +	   (ap_scoreboard_image->servers[n].status != AP_DEAD ) 
> +	     for(i=0; i<IP_LOCKOUT; i++)
> +		/* could be done with a 4 byte int comparision and a proper
> +		 * set of assert() on the sizes.
> +		 */
> +		if (!memcmp(
> +			&(ap_scoreboard_image->servers[n].ip_lockedout[i].remote_addr),
> +			&sa_client,
> +			sizeof(struct sockaddr_in))) {
> +			close(csd); 
> +			shutdown(csd,1);
> +			continue;
> +			};
> +	};
> +#endif
> +
>  	/* We've got a socket, let's at least process one request off the
>  	 * socket before we accept a graceful restart request.
>  	 */

> from mod_bust.c 
> /* ====================================================================
>  * Copyright (c) 1995-1998 The Apache Group.  All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>  * are met:
>  *
>  * 1. Redistributions of source code must retain the above copyright
>  *    notice, this list of conditions and the following disclaimer. 
>  *
>  * 2. Redistributions in binary form must reproduce the above copyright
>  *    notice, this list of conditions and the following disclaimer in
>  *    the documentation and/or other materials provided with the
>  *    distribution.
>  *
>  * 3. All advertising materials mentioning features or use of this
>  *    software must display the following acknowledgment:
>  *    "This product includes software developed by the Apache Group
>  *    for use in the Apache HTTP server project (http://www.apache.org/)."
>  *
>  * 4. The names "Apache Server" and "Apache Group" must not be used to
>  *    endorse or promote products derived from this software without
>  *    prior written permission. For written permission, please contact
>  *    apache@apache.org.
>  *
>  * 5. Products derived from this software may not be called "Apache"
>  *    nor may "Apache" appear in their names without prior written
>  *    permission of the Apache Group.
>  *
>  * 6. Redistributions of any form whatsoever must retain the following
>  *    acknowledgment:
>  *    "This product includes software developed by the Apache Group
>  *    for use in the Apache HTTP server project (http://www.apache.org/)."
>  *
>  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
>  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
>  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
>  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
>  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
>  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
>  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
>  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
>  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
>  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
>  * OF THE POSSIBILITY OF SUCH DAMAGE.
>  * ====================================================================
>  *
>  * This software consists of voluntary contributions made by many
>  * individuals on behalf of the Apache Group and was originally based
>  * on public domain software written at the National Center for
>  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
>  * For more information on the Apache Group and the Apache HTTP server
>  * project, please see <http://www.apache.org/>.
>  *
>  * mod_bust.c	- simple attempt to stop endless streams of password
>  *		  guessing.
>  *
>  */
> 
> #include "httpd.h"
> #include "http_config.h"
> #include "http_core.h"
> #include "http_log.h"
> #include "http_protocol.h"
> #include "scoreboard.h"
> 
> typedef struct bust_list_rec {
>     struct sockaddr_in	remote_addr;
>     time_t 		last_seen;
>     int 		count;
>     struct bust_list_rec * nxt;
>     } bust_list_rec;
> 
> typedef struct bust_config_struct {
>     int maxbust;
>     int timeout;
>     int maxnum; 
>     struct bust_list_rec * list;
> } bust_config_rec;
> 
> static void *create_bust_config(pool *p, char *d)
> {
>     bust_config_rec *sec =
>       (bust_config_rec *) ap_pcalloc(p, sizeof(bust_config_rec));
> 
>     /* just to illustrate the default really */
>     sec->maxbust = 0;
>     sec->maxnum = 10;    
>     sec->timeout = 600;    
>     sec->list = NULL;
>     return sec;
> }      
> 
> static const command_rec bust_cmd[] =
> {
>     {"BustMaxTry", ap_set_int_slot,
>      (void *) XtOffsetOf(bust_config_rec, maxbust),
>      OR_AUTHCFG, TAKE1,
>      "Maximum number of times we allow a user to try a password."
>      "Set to '0' to switch off, or to a higher value above which we start to cut off access to the source IP."
>      "Default is off."},
>     {"BustMaxIP", ap_set_int_slot,
>      (void *) XtOffsetOf(bust_config_rec, maxnum),
>      OR_AUTHCFG, TAKE1,
>      "Maximum number of IP addresses we track. Per child."
>      "Default is 10."},
>     {"BustMaxTime", ap_set_int_slot,
>      (void *) XtOffsetOf(bust_config_rec, timeout),
>      OR_AUTHCFG, TAKE1,
>      "Timeout, in second, over which we count. Per child."
>      "Default is 600."},
>     {NULL}
> };
> 
> module MODULE_VAR_EXPORT bust_module;
> 
> /* add an IP address to the scoreboard, for all to see
>  * and for the accept/listen handler in the main body
>  * of the server to block
>  */
> 
> #include <net/if.h>
> #include <net/route.h>
> 
> static int bust_add_to_lobal_lockout_list(request_rec *rec, conn_rec * con ) {
> 
> 	/* would the server run as root, we could also do a route-add
> 	 * type of thing here, as suggested on the mailing list
> 	 */
> #ifdef TEST_NULL_ROUTE
> 	int s=socket(PF_ROUTE,SOCK_RAW,AF_INET);
> 	dx_route_add_LPC(addr,SYRC);
> 	close(s);shutdown(s,1);
> #endif
> 	/* Take the oldest/null one; replace it by the
> 	 * new address.
> 	 */
>         int o,i;
> 	time_t mage=0,now= rec->request_time;
>         if (!ap_exists_scoreboard_image()) 
> 		return; /* in inetd mode or something like that */
> 
> 	for(i=o=0; i<IP_LOCKOUT;i++) {
> 		time_t d;
> 		if  (ap_scoreboard_image->servers[con->child_num].ip_lockedout[i].last_seen == 0) {
> 			o = i;
> 			break;
> 			};
> 	 	d=difftime( ap_scoreboard_image->servers[con->child_num].ip_lockedout[i].last_seen, now);
> 		if ( d > mage ) {
> 			mage = d;
> 			o = i;		
> 			};
> 		};
> 
> 	ap_scoreboard_image->servers[con->child_num].ip_lockedout[i].last_seen = now;
> 	ap_scoreboard_image->servers[con->child_num].ip_lockedout[i].remote_addr = con->remote_addr;
> 	}
> 
> static int bust_count( request_rec * r) {
> 	struct bust_list_rec * p, * minp;
>     	conn_rec *con = r->connection;                
>    	bust_config_rec *bust =
>     		(bust_config_rec *) ap_get_module_config(r->per_dir_config, &bust_module);
> 	time_t maxd=0;
> 	int items=0;
> 
> 	if ( r->status != AUTH_REQUIRED )
> 		return DECLINED;
> 
> 	for( p= bust->list; p;) {
> 		time_t d = difftime(r->request_time, p->last_seen);
> 		p = p->nxt;
> 
> 		if (d > bust->timeout) {
> 			/* too old, remove from chain */
> 			void * pp = p;
> 			p=p->nxt;
> 			free(pp);
> 			continue;
> 			};
> 
> 		/* could be optimized by using 4 byte int comparision */
> 		if (!memcmp(&(p->remote_addr),&(con->remote_addr),sizeof(p->remote_addr))) {
> 			if (p->count >= bust->maxnum) 
> 				return bust_add_to_lobal_lockout_list( r, con );
> 
> 			p->count ++;
> 			p->last_seen = r->request_time;
> 			};
> 
> 		if (d > maxd) {
> 			minp = p;
> 			maxd = d;
> 			};
> 
> 		items++;
> 		};
> 
> 	if (p == NULL) {
> 		if ( items <= bust->maxnum) {
> 			/* add, rathern than replace */
> 			minp = malloc( sizeof( bust_list_rec ));
> 			minp->nxt = bust->list;
> 			bust->list = minp;
> 			};
> 		minp->nxt = minp;
> 
> 		minp->remote_addr = con->remote_addr;
> 		minp->count = 1;
> 		minp->last_seen = r->request_time;
> 		};
> 			
> 	return DECLINED;
> 	};
> 	
> module MODULE_VAR_EXPORT auth_module =
> {
>     STANDARD_MODULE_STUFF,
>     NULL,			/* initializer */
>     create_bust_config,		/* dir config creater */
>     NULL,			/* dir merger --- default is to override */
>     NULL,			/* server config */
>     NULL,			/* merge server config */
>     bust_cmd,			/* command table */
>     NULL,			/* handlers */
>     NULL,			/* filename translation */
>     NULL,			/* check_user_id */
>     NULL,			/* check auth */
>     NULL,			/* check access */
>     NULL,			/* type_checker */
>     NULL,			/* fixups */
>     bust_count,			/* logger */
>     NULL,			/* header parser */
>     NULL,			/* child_init */
>     NULL,			/* child_exit */
>     NULL			/* post read-request */
> };
> 


Re: getpeername() and strong source blocking

Posted by Dean Gaudet <dg...@arctic.org>.
With linux 2.1.x you can insert a BPF-style filter onto any socket from
userland.  That, and other firewalling methods are the only things I can
think of.

Dean

On Tue, 23 Jun 1998, Dirk-Willem van Gulik wrote:

> 
> In the context of hardening apache against certain DoS attacks,
> 
> Is there any way of finding out _who_ is next in the listen queue prior
> to accepting it ? And what is a good way of skipping such. i.e. a way
> to improve the following fragment;
> 
> {
> 	listen(fd,x)
> 	while(1) {
> 		select(..)
> (1)
> 		s=accept(fd,peer,..)
> 		if ( peer-ip != one-of-my-friends )
> (2)			close(s); shutdown(s,1);
> 			continue;
> 		
> 		find-a-child or fork
> 		    pass of
> 			and do somethng usefull...
> 		..
> 		};
> 	}
> 
> 
> I.e. I would like to do something selective as early is (1) and
> really would not like to do the accept/close/shutdown kinda
> thing (2) when it aint no friend.
> 
> Any ideas ? (I am currently doing the above to lock certain
> badly behaving IP addresses out; and was trying to tie this up
> for some-one which caused me to look again.).
> 
> Dw.
> 
> 


RE: getpeername() and strong source blocking

Posted by Andy Finkenstadt <an...@simutronics.com>.
My solution to this problem, back when I was running www.homes.com and had
badly behaving library and dialup sites tickling my web server, was to
insert a host route to the black hole at the operating system level.  It
prevents the TCP connection from even coming up on the port and Apache would
never see the connection to refuse it.  From memory, it was something like
"route add ip.ad.dr.ess 127.0.0.1 1" or some such.

This is obviously only an option when you can be pro-active and need to
exclude just a few sites, not accept things only from a few other sites.

Andy