Date: Mon, 31 Aug 1998 11:00:03 -0700 (PDT) From: Archie Cobbs <archie@whistle.com> To: freebsd-hackers@FreeBSD.ORG Subject: Re: FreeBSD's RST validation Message-ID: <199808311800.LAA06751@bubba.whistle.com> In-Reply-To: <199808311306.GAA27281@salsa.gv.tsc.tdk.com> from Don Lewis at "Aug 31, 98 06:06:36 am"
next in thread | previous in thread | raw e-mail | index | archive | help
Would some -hackers comment on this bug (posted to freebsd-security)? Thanks, -Archie Don Lewis writes: > On Aug 30, 5:21pm, Tristan Horn wrote: > } Subject: FreeBSD's RST validation > } --ZRyEpB+iJ+qUx0kp > } Content-Type: multipart/mixed; boundary=qGV0fN9tzfkG3CxV > } > } > } --qGV0fN9tzfkG3CxV > } Content-Type: text/plain; charset=us-ascii > } > } RFC 793, pages 36-39 (chapter 3.5) describes closing connections with > } TCP. Page 37 is of particular interest: > } > } Reset Processing > } > } In all states except SYN-SENT, all reset (RST) segments are validated > } by checking their SEQ-fields. A reset is valid if its sequence number > } is in the window. In the SYN-SENT state (a RST received in response > } to an initial SYN), the RST is acceptable if the ACK field > } acknowledges the SYN. > } > } Unfortunately, FreeBSD (2.2.5, 2.2.6, 2.2.7, 3.0) does not appear to > } validate RST segments to this extent. In other words, only the packets' > } IP/port pairs are checked. > > Back in December 1997, I posted the following patch for the LAND attack > and also implemented stricter RST validation. The variation of the > LAND fix in the first two chunks of this patch was implemented (you'll > have to look carefully at the code to find the second chunk), but I don't > believe the rest of the fixes in this patch were applied. > > I've been running a version of this patch altered for 2.1.x since December > without problems. If you remove the first two chunks of this patch, it > will apply cleanly to the 2.2-stable version of tcp_input.c, though I have > no idea if it will work ... > > ----------------- Cut Here -------------------------- > --- tcp_input.c.2_2 Mon Dec 1 16:49:21 1997 > +++ tcp_input.c Wed Dec 3 02:21:45 1997 > @@ -318,19 +318,6 @@ > #endif /* TUBA_INCLUDE */ > > /* > - * Reject attempted self-connects. XXX This actually masks > - * a bug elsewhere, since self-connect should work. > - * However, a urrently-active DoS attack in the Internet > - * sends a phony self-connect request which causes an infinite > - * loop. > - */ > - if (ti->ti_src.s_addr == ti->ti_dst.s_addr > - && ti->ti_sport == ti->ti_dport) { > - tcpstat.tcps_badsyn++; > - goto drop; > - } > - > - /* > * Check that TCP offset makes sense, > * pull out TCP options and adjust length. XXX > */ > @@ -654,6 +641,24 @@ > if (m->m_flags & (M_BCAST|M_MCAST) || > IN_MULTICAST(ntohl(ti->ti_dst.s_addr))) > goto drop; > + > + /* > + * Reject attempted self-connects. > + * > + * Doing the test here should prevent the "LAND" DoS > + * attack without affecting legitimate self-connects > + * which will occur in the SYN-SENT state. > + * > + * In the dropafterack code below we'll also fix the real > + * bug in the SYN-RECEIVED state that causes the infinite > + * loop since it can also be used to generate ACK storms. > + */ > + if (ti->ti_src.s_addr == ti->ti_dst.s_addr > + && ti->ti_sport == ti->ti_dport) { > + tcpstat.tcps_badsyn++; > + goto drop; > + } > + > am = m_get(M_DONTWAIT, MT_SONAME); /* XXX */ > if (am == NULL) > goto drop; > @@ -962,17 +967,99 @@ > > /* > * States other than LISTEN or SYN_SENT. > - * First check timestamp, if present. > + * First check the RST flag and sequence number since reset segments > + * are exempt from the timestamp and connection count tests. This > + * fixes a bug introduced by the Stevens, vol. 2, p. 960 bugfix > + * below which allowed reset segments in half the sequence space > + * to fall though and be processed (which gives forged reset > + * segments with a random sequence number a 50 percent chance of > + * killing a connection). > + * Then check timestamp, if present. > * Then check the connection count, if present. > * Then check that at least some bytes of segment are within > * receive window. If segment begins before rcv_nxt, > * drop leading data (and SYN); if nothing left, just ack. > * > + * > + * If the RST bit is set, check the sequence number to see > + * if this is a valid reset segment. > + * RFC 793 page 37: > + * In all states except SYN-SENT, all reset (RST) segments > + * are validated by checking their SEQ-fields. A reset is > + * valid if its sequence number is in the window. > + * Note: this does not take into account delayed ACKs, so > + * we should test against last_ack_sent instead of rcv_nxt. > + * Also, it does not make sense to allow reset segments with > + * sequence numbers greater than last_ack_sent to be processed > + * since these sequence numbers are just the acknowledgement > + * numbers in our outgoing packets being echoed back at us, > + * and these acknowledgement numbers are monotonically > + * increasing. > + * If we have multiple segments in flight, the intial reset > + * segment sequence numbers will be to the left of last_ack_sent, > + * but they will eventually catch up. > + * In any case, it never made sense to trim reset segments to > + * fit the receive window since RFC 1122 says: > + * 4.2.2.12 RST Segment: RFC-793 Section 3.4 > + * > + * A TCP SHOULD allow a received RST segment to include data. > + * > + * DISCUSSION > + * It has been suggested that a RST segment could contain > + * ASCII text that encoded and explained the cause of the > + * RST. No standard has yet been established for such > + * data. > + * > + * If the reset segment passes the sequence number test examine > + * the state: > + * SYN_RECEIVED STATE: > + * If passive open, return to LISTEN state. > + * If active open, inform user that connection was refused. > + * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES: > + * Inform user that connection was reset, and close tcb. > + * CLOSING, LAST_ACK, TIME_WAIT STATES > + * Close the tcb. > + * TIME_WAIT state: > + * Drop the segment - see Stevens, vol. 2, p. 964 and > + * RFC 1337. > + */ > + if (tiflags&TH_RST) { > + if (tp->last_ack_sent == ti->ti_seq) { > + 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); > + break; > + > + case TCPS_CLOSING: > + case TCPS_LAST_ACK: > + tp = tcp_close(tp); > + break; > + > + case TCPS_TIME_WAIT: > + break; > + } > + } > + goto drop; > + } > + > + /* > * RFC 1323 PAWS: If we have a timestamp reply on this segment > * and it's less than ts_recent, drop it. > */ > - if ((to.to_flag & TOF_TS) != 0 && (tiflags & TH_RST) == 0 && > - tp->ts_recent && TSTMP_LT(to.to_tsval, tp->ts_recent)) { > + if ((to.to_flag & TOF_TS) != 0 && tp->ts_recent && > + TSTMP_LT(to.to_tsval, tp->ts_recent)) { > > /* Check to see if ts_recent is over 24 days old. */ > if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) { > @@ -1003,10 +1090,19 @@ > * RST segments do not have to comply with this. > */ > if ((tp->t_flags & (TF_REQ_CC|TF_RCVD_CC)) == (TF_REQ_CC|TF_RCVD_CC) && > - ((to.to_flag & TOF_CC) == 0 || tp->cc_recv != to.to_cc) && > - (tiflags & TH_RST) == 0) > + ((to.to_flag & TOF_CC) == 0 || tp->cc_recv != to.to_cc)) > goto dropafterack; > > + /* > + * In the SYN-RECEIVED state, validate that the packet belongs to > + * this connection before trimming the data to fit the receive > + * window. Check the sequence number versus IRS since we know > + * the sequence numbers haven't wrapped. This is a partial fix > + * for the "LAND" DoS attack. > + */ > + if (tp->t_state == TCPS_SYN_RECEIVED && SEQ_LT(ti->ti_seq, tp->irs)) > + goto dropwithreset; > + > todrop = tp->rcv_nxt - ti->ti_seq; > if (todrop > 0) { > if (tiflags & TH_SYN) { > @@ -1118,40 +1214,6 @@ > } > > /* > - * If the RST bit is set examine the state: > - * SYN_RECEIVED STATE: > - * If passive open, return to LISTEN state. > - * If active open, inform user that connection was refused. > - * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES: > - * Inform user that connection was reset, and close tcb. > - * 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; > - } > - > - /* > * If a SYN is in the window, then this is an > * error and we send an RST and drop the connection. > */ > @@ -1660,9 +1722,22 @@ > /* > * Generate an ACK dropping incoming segment if it occupies > * sequence space, where the ACK reflects our state. > - */ > - if (tiflags & TH_RST) > - goto drop; > + * > + * We can now skip the test for the RST flag since all > + * paths to this code happen after packets containing > + * RST have been dropped. > + * > + * In the SYN-RECEIVED state, don't send an ACK unless the > + * segment we received passes the SYN-RECEIVED ACK test. > + * If it fails send a RST. This breaks the loop in the > + * "LAND" DoS attack, and also prevents an ACK storm > + * between two listening ports that have been sent forged > + * SYN segments, each with the source address of the other. > + */ > + if (tp->t_state == TCPS_SYN_RECEIVED && (tiflags & TH_ACK) && > + (SEQ_GT(tp->snd_una, ti->ti_ack) || > + SEQ_GT(ti->ti_ack, tp->snd_max)) ) > + goto dropwithreset; > #ifdef TCPDEBUG > if (so->so_options & SO_DEBUG) > tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0); > ----------------- Cut Here -------------------------- > > To Unsubscribe: send mail to majordomo@FreeBSD.org > with "unsubscribe freebsd-security" in the body of the message > ___________________________________________________________________________ Archie Cobbs * Whistle Communications, Inc. * http://www.whistle.com To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199808311800.LAA06751>