Subject: TCP RST nuke prevention.
To: None <tech-security@NetBSD.ORG>
From: Darren Reed <darrenr@cyber.com.au>
List: tech-security
Date: 06/04/1997 23:58:51
It was recently brought to my attention that IP Filter is quite fascist
about which packets it lets through when you use "keep state" with TCP,
particaularly when it comes to RST packets.  IP Filter makes sure that
every packet which goes through is within the current window...

However, it has been shown that SOME RST packets have an ACK of 0 but
a non-zero SEQ.  So I went back through the code, traced what happened
to RST packets (something I'm sure I'd done before - if only alluded
to it by experiment).  If you do the same, I expect you will find that
an RST is allowed to reset any connection, providing port numbers match,
irrespective of the SEQ/ACK numbers in the packet.

Quickly thinking, you might say "but what about when a box reboots ?"
An RST, like ICMP errors, is only ever returned in response to a packet
the kernel receives, and as such, should be able to place at least a
non-zero SEQ number in the RST.  RFC 793 goes through the requirements
for RST packets being sent - BUT does not say anything about how what
should be checked (nor does 1122).

To me, the current behaviour is a bug.  I developed the patch below for
FreeBSD, but it should slip into NetBSD bar the line numbers being wrong.

Is there something I'm missing which requires RST nukes to happen ?  If
not, is someone else willing to commit this patch or can I ?

Darren

*** /sys/netinet/tcp_input.c.orig       Tue Jun  3 20:26:02 1997
--- /sys/netinet/tcp_input.c    Tue Jun  3 20:27:35 1997
***************
*** 1118,1145 ****
  	 *    CLOSING, LAST_ACK, TIME_WAIT STATES
  	 *      Close the tcb.
  	 */
! 	if (tiflags&TH_RST) switch (tp->t_state) {
  
! 	case TCPS_SYN_RECEIVED:
! 		so->so_error = ECONNREFUSED;
! 		goto close;
  
! 	case TCPS_ESTABLISHED:
! 	case TCPS_FIN_WAIT_1:
! 	case TCPS_FIN_WAIT_2:
! 	case TCPS_CLOSE_WAIT:
! 		so->so_error = ECONNRESET;
! 	close:
! 		tp->t_state = TCPS_CLOSED;
! 		tcpstat.tcps_drops++;
! 		tp = tcp_close(tp);
! 		goto drop;
  
! 	case TCPS_CLOSING:
! 	case TCPS_LAST_ACK:
! 	case TCPS_TIME_WAIT:
! 		tp = tcp_close(tp);
! 		goto drop;
  	}
  
  	/*
--- 1118,1153 ----
  	 *    CLOSING, LAST_ACK, TIME_WAIT STATES
  	 *      Close the tcb.
  	 */
! 	if (tiflags&TH_RST) {
  
! 		if ((ti->ti_seq != tp->rcv_nxt) ||
! 		    (ti->ti_ack && ((SEQ_LEQ(ti->ti_ack, tp->iss) ||
! 		      SEQ_GT(ti->ti_ack, tp->snd_max)))))
! 			goto drop;
  
! 		switch (tp->t_state) {
! 		
! 		case TCPS_SYN_RECEIVED:
! 			so->so_error = ECONNREFUSED;
! 			goto close;
  
! 		case TCPS_ESTABLISHED:
! 		case TCPS_FIN_WAIT_1:
! 		case TCPS_FIN_WAIT_2:
! 		case TCPS_CLOSE_WAIT:
! 			so->so_error = ECONNRESET;
! 		close:
! 			tp->t_state = TCPS_CLOSED;
! 			tcpstat.tcps_drops++;
! 			tp = tcp_close(tp);
! 			goto drop;
! 
! 		case TCPS_CLOSING:
! 		case TCPS_LAST_ACK:
! 		case TCPS_TIME_WAIT:
! 			tp = tcp_close(tp);
! 			goto drop;
! 		}
  	}
  
  	/*