Hello all, the RFC1948-like sequence number generation patch is ready for
testing.  The patch included will apply cleanly to both a recent -current
and a recent -stable.  I've spent a good deal of time looking at tcpdump
logs, and it looks good to me.

Please test and review this if you feel comfortable doing so.  If you do
not feel comfortable doing so, please simply test it instead.  :)

When testing, please try both with net.inet.tcp.tcp_seq_genscheme set to 0
and 1.  0 Uses the RFC1948 scheme for generating all ISNs, 1 uses RFC1948
only for outgoing SYN; SYN-ACKs are filled with random values instead.
I'd like to leave this selectable, with the default to 1.  1 is slightly
faster, while 0 may feel more comfortable to some users.

Both schemes should work equally well, so please test both.

I've changed the RFC1948 algorithm slightly, by adding a random offset.
Please look this over carefully when reviewing.  Note that this patch is
_functionally_ complete, but does not yet remove cruft from other
generation schemes; I will do this in the final version of the patch.

Enjoy!

Mike "Silby" Silbersack
diff -u -r netinet.old/tcp_input.c netinet/tcp_input.c
--- netinet.old/tcp_input.c     Thu Jul 19 20:45:01 2001
+++ netinet/tcp_input.c Fri Jul 20 22:38:18 2001
@@ -1135,7 +1135,7 @@
                if (iss)
                        tp->iss = iss;
                else {
-                       tp->iss = tcp_new_isn();
+                       tp->iss = tcp_new_isn(tp);
                }
                tp->irs = th->th_seq;
                tcp_sendseqinit(tp);
@@ -1667,7 +1667,7 @@
                        if (thflags & TH_SYN &&
                            tp->t_state == TCPS_TIME_WAIT &&
                            SEQ_GT(th->th_seq, tp->rcv_nxt)) {
-                               iss = tcp_new_isn();
+                               iss = tcp_new_isn(tp);
                                tp = tcp_close(tp);
                                goto findpcb;
                        }
diff -u -r netinet.old/tcp_subr.c netinet/tcp_subr.c
--- netinet.old/tcp_subr.c      Thu Jul 19 20:45:01 2001
+++ netinet/tcp_subr.c  Mon Jul 23 21:59:53 2001
@@ -98,6 +98,7 @@
 #endif /*IPSEC*/
 
 #include <machine/in_cksum.h>
+#include <sys/md5.h>
 
 int    tcp_mssdflt = TCP_MSS;
 SYSCTL_INT(_net_inet_tcp, TCPCTL_MSSDFLT, mssdflt, CTLFLAG_RW, 
@@ -1112,24 +1113,92 @@
 }
 #endif /* INET6 */
 
+/*
+ * Following is where TCP initial sequence number generation occurs.
+ * For ISNs in SYN-ACK packets, we use the output from arc4random();
+ * there is no montonicity requirement for ISNs in SYN-ACK packets,
+ * and they should be as random as possible to avoid spoofing attacks.
+ *
+ * When we send ISNs in outgoing SYN packets, we must ensure monotonicity
+ * so that TIME_WAIT recycling works properly on the hosts we are
+ * connecting to.  To meet this requirement while still being
+ * unpredictable, we use a modified version of the algorithm specified
+ * in RFC 1948.
+ *
+ * The algorithm is as follows:
+ * ISN = Time + isn_offset + md5(fport, lport, faddr, laddr, secret)
+ * 
+ * Time is based off the system timer, and is corrected so that it
+ * increases by one megabyte per second.  This allows for proper
+ * recycling on high speed LANs while still leaving over an hour
+ * before rollover.
+ *
+ * isn_offset is a random value changed whenever secret is changed.
+ * The purpose of this value is to make sure that the exact output
+ * of the hash function cannot be determined.  If this was not present,
+ * it would be a simple matter to subtract time from the ISN and
+ * launch a brute-force attack on the secret of the hash.  This
+ * attack should now be much tougher.  (isn_offset is not part
+ * of the spec outlined in RFC 1948.)
+ *
+ * The md5 hash is what seperates the ISN space of every connection.
+ * As long as the secret remains secret, it should be virtually
+ * impossible for anyone to guess the sequence space of any other
+ * connection.  The secret is changed hourly to ensure that 
+ * a brute force attack on the secret will not be possible in
+ * the near future.
+ *
+ * For more information on this algorithm, please see RFC 1948.
+ */
+
+#define ISN_RESEED_INTVL 3600
+#define ISN_BYTES_PER_SECOND 1048576
+
+int isn_reseed_time;
+u_char isn_secret[32];
+u_int32_t isn_offset;
+
 tcp_seq
-tcp_new_isn()
+tcp_new_isn(tp)
+       struct tcpcb *tp;
 {
-       if ((tcp_seq_genscheme > 1) || (tcp_seq_genscheme < 0))
-               tcp_seq_genscheme = 1;
-
-       switch (tcp_seq_genscheme) {
-               case 0: /*
-                        * Random positive increments
-                        */
-                       tcp_iss += TCP_ISSINCR/2;
-                       return tcp_iss;
-               case 1: /*
-                        * OpemBSD randomized scheme
-                        */
-                       return tcp_rndiss_next();
+MD5_CTX ctx;
+u_int32_t md5_buffer[4];
+tcp_seq new_isn;
+
+       if (isn_reseed_time < ticks) {
+               /* Reseed the secret and offset. */
+               read_random(&isn_offset, sizeof(u_int32_t));
+               read_random(&isn_secret, sizeof(isn_secret));
+               isn_reseed_time = ticks + (ISN_RESEED_INTVL * hz);
        }
 
+       if (((tp->t_state == TCPS_LISTEN) || (tp->t_state == TCPS_TIME_WAIT))
+          && tcp_seq_genscheme == 1)
+               return arc4random();
+       MD5Init(&ctx);
+       MD5Update(&ctx, (u_char *) &tp->t_inpcb->inp_fport, sizeof(u_short));
+       MD5Update(&ctx, (u_char *) &tp->t_inpcb->inp_lport, sizeof(u_short));
+#ifdef INET6
+       if ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) {
+               MD5Update(&ctx, (u_char *) &tp->t_inpcb->in6p_faddr,
+                         sizeof(struct in6_addr));
+               MD5Update(&ctx, (u_char *) &tp->t_inpcb->in6p_laddr,
+                         sizeof(struct in6_addr));
+       } else
+#endif
+               {
+               MD5Update(&ctx, (u_char *) &tp->t_inpcb->inp_faddr,
+                         sizeof(struct in_addr));
+               MD5Update(&ctx, (u_char *) &tp->t_inpcb->inp_laddr,
+                         sizeof(struct in_addr));
+       }
+       MD5Update(&ctx, (u_char *) &isn_secret, sizeof(isn_secret));
+       MD5Final((u_char *) &md5_buffer, &ctx);
+       new_isn = (tcp_seq) md5_buffer[0];
+       new_isn += ticks * (ISN_BYTES_PER_SECOND / hz);
+       new_isn += isn_offset;
+       return new_isn;
 }
 
 #define TCP_RNDISS_ROUNDS      16
diff -u -r netinet.old/tcp_usrreq.c netinet/tcp_usrreq.c
--- netinet.old/tcp_usrreq.c    Thu Jul 19 20:45:01 2001
+++ netinet/tcp_usrreq.c        Fri Jul 20 22:38:28 2001
@@ -758,7 +758,7 @@
        tcpstat.tcps_connattempt++;
        tp->t_state = TCPS_SYN_SENT;
        callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp);
-       tp->iss = tcp_new_isn();
+       tp->iss = tcp_new_isn(tp);
        tcp_sendseqinit(tp);
 
        /*
@@ -844,7 +844,7 @@
        tcpstat.tcps_connattempt++;
        tp->t_state = TCPS_SYN_SENT;
        callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp);
-       tp->iss = tcp_new_isn();
+       tp->iss = tcp_new_isn(tp);
        tcp_sendseqinit(tp);
 
        /*
diff -u -r netinet.old/tcp_var.h netinet/tcp_var.h
--- netinet.old/tcp_var.h       Thu Jul 19 20:45:01 2001
+++ netinet/tcp_var.h   Thu Jul 19 20:46:01 2001
@@ -414,7 +414,7 @@
 tcp_seq        tcp_rndiss_next __P((void));
 u_int16_t
        tcp_rndiss_encrypt __P((u_int16_t));
-tcp_seq tcp_new_isn __P((void));
+tcp_seq tcp_new_isn __P((struct tcpcb *));
 
 #endif /* _KERNEL */
 

Reply via email to