Author: attilio
Date: Mon Apr 25 17:13:40 2011
New Revision: 221023
URL: http://svn.freebsd.org/changeset/base/221023

Log:
  Add the possibility to verify MD5 hash of incoming TCP packets.
  As long as this is a costy function, even when compiled in (along with
  the option TCP_SIGNATURE), it can be disabled via the
  net.inet.tcp.signature_verify_input sysctl.
  
  Sponsored by: Sandvine Incorporated
  Reviewed by:  emaste, bz
  MFC after:    2 weeks

Modified:
  head/sys/netinet/tcp_input.c
  head/sys/netinet/tcp_subr.c
  head/sys/netinet/tcp_syncache.c
  head/sys/netinet/tcp_var.h

Modified: head/sys/netinet/tcp_input.c
==============================================================================
--- head/sys/netinet/tcp_input.c        Mon Apr 25 16:37:47 2011        
(r221022)
+++ head/sys/netinet/tcp_input.c        Mon Apr 25 17:13:40 2011        
(r221023)
@@ -215,6 +215,12 @@ static void         tcp_pulloutofband(struct so
                     struct tcphdr *, struct mbuf *, int);
 static void     tcp_xmit_timer(struct tcpcb *, int);
 static void     tcp_newreno_partial_ack(struct tcpcb *, struct tcphdr *);
+static void inline     tcp_fields_to_host(struct tcphdr *);
+#ifdef TCP_SIGNATURE
+static void inline     tcp_fields_to_net(struct tcphdr *);
+static int inline      tcp_signature_verify_input(struct mbuf *, int, int,
+                           int, struct tcpopt *, struct tcphdr *, u_int);
+#endif
 static void inline     cc_ack_received(struct tcpcb *tp, struct tcphdr *th,
                            uint16_t type);
 static void inline     cc_conn_init(struct tcpcb *tp);
@@ -440,6 +446,40 @@ cc_post_recovery(struct tcpcb *tp, struc
        tp->t_bytes_acked = 0;
 }
 
+static inline void
+tcp_fields_to_host(struct tcphdr *th)
+{
+
+       th->th_seq = ntohl(th->th_seq);
+       th->th_ack = ntohl(th->th_ack);
+       th->th_win = ntohs(th->th_win);
+       th->th_urp = ntohs(th->th_urp);
+}
+
+#ifdef TCP_SIGNATURE
+static inline void
+tcp_fields_to_net(struct tcphdr *th)
+{
+
+       th->th_seq = htonl(th->th_seq);
+       th->th_ack = htonl(th->th_ack);
+       th->th_win = htons(th->th_win);
+       th->th_urp = htons(th->th_urp);
+}
+
+static inline int
+tcp_signature_verify_input(struct mbuf *m, int off0, int tlen, int optlen,
+    struct tcpopt *to, struct tcphdr *th, u_int tcpbflag)
+{
+       int ret;
+
+       tcp_fields_to_net(th);
+       ret = tcp_signature_verify(m, off0, tlen, optlen, to, th, tcpbflag);
+       tcp_fields_to_host(th);
+       return (ret);
+}
+#endif
+
 /* Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. */
 #ifdef INET6
 #define ND6_HINT(tp) \
@@ -519,6 +559,9 @@ tcp_input(struct mbuf *m, int off0)
        int thflags;
        int rstreason = 0;      /* For badport_bandlim accounting purposes */
        uint8_t iptos;
+#ifdef TCP_SIGNATURE
+       uint8_t sig_checked = 0;
+#endif
 #ifdef IPFIREWALL_FORWARD
        struct m_tag *fwd_tag;
 #endif
@@ -676,10 +719,7 @@ tcp_input(struct mbuf *m, int off0)
        /*
         * Convert TCP protocol specific fields to host format.
         */
-       th->th_seq = ntohl(th->th_seq);
-       th->th_ack = ntohl(th->th_ack);
-       th->th_win = ntohs(th->th_win);
-       th->th_urp = ntohs(th->th_urp);
+       tcp_fields_to_host(th);
 
        /*
         * Delay dropping TCP, IP headers, IPv6 ext headers, and TCP options.
@@ -861,8 +901,24 @@ relocked:
                }
                INP_INFO_WLOCK_ASSERT(&V_tcbinfo);
 
+#ifdef TCP_SIGNATURE
+               tcp_dooptions(&to, optp, optlen,
+                   (thflags & TH_SYN) ? TO_SYN : 0);
+               if (sig_checked == 0) {
+                       tp = intotcpcb(inp);
+                       if (tp == NULL || tp->t_state == TCPS_CLOSED) {
+                               rstreason = BANDLIM_RST_CLOSEDPORT;
+                               goto dropwithreset;
+                       }
+                       if (!tcp_signature_verify_input(m, off0, tlen, optlen,
+                           &to, th, tp->t_flags))
+                               goto dropunlock;
+                       sig_checked = 1;
+               }
+#else
                if (thflags & TH_SYN)
                        tcp_dooptions(&to, optp, optlen, TO_SYN);
+#endif
                /*
                 * NB: tcp_twcheck unlocks the INP and frees the mbuf.
                 */
@@ -1021,6 +1077,26 @@ relocked:
                        tp = intotcpcb(inp);
                        KASSERT(tp->t_state == TCPS_SYN_RECEIVED,
                            ("%s: ", __func__));
+#ifdef TCP_SIGNATURE
+                       if (sig_checked == 0)  {
+                               tcp_dooptions(&to, optp, optlen,
+                                   (thflags & TH_SYN) ? TO_SYN : 0);
+                               if (!tcp_signature_verify_input(m, off0, tlen,
+                                   optlen, &to, th, tp->t_flags)) {
+
+                                       /*
+                                        * In SYN_SENT state if it receives an
+                                        * RST, it is allowed for further
+                                        * processing.
+                                        */
+                                       if ((thflags & TH_RST) == 0 ||
+                                           (tp->t_state == TCPS_SYN_SENT) == 0)
+                                               goto dropunlock;
+                               }
+                               sig_checked = 1;
+                       }
+#endif
+
                        /*
                         * Process the segment and the data it
                         * contains.  tcp_do_segment() consumes
@@ -1225,6 +1301,25 @@ relocked:
                return;
        }
 
+#ifdef TCP_SIGNATURE
+       if (sig_checked == 0)  {
+               tcp_dooptions(&to, optp, optlen,
+                   (thflags & TH_SYN) ? TO_SYN : 0);
+               if (!tcp_signature_verify_input(m, off0, tlen, optlen, &to,
+                   th, tp->t_flags)) {
+
+                       /*
+                        * In SYN_SENT state if it receives an RST, it is
+                        * allowed for further processing.
+                        */
+                       if ((thflags & TH_RST) == 0 ||
+                           (tp->t_state == TCPS_SYN_SENT) == 0)
+                               goto dropunlock;
+               }
+               sig_checked = 1;
+       }
+#endif
+
        /*
         * Segment belongs to a connection in SYN_SENT, ESTABLISHED or later
         * state.  tcp_do_segment() always consumes the mbuf chain, unlocks

Modified: head/sys/netinet/tcp_subr.c
==============================================================================
--- head/sys/netinet/tcp_subr.c Mon Apr 25 16:37:47 2011        (r221022)
+++ head/sys/netinet/tcp_subr.c Mon Apr 25 17:13:40 2011        (r221023)
@@ -213,6 +213,12 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, sore
     &tcp_soreceive_stream, 0, "Using soreceive_stream for TCP sockets");
 #endif
 
+#ifdef TCP_SIGNATURE
+static int     tcp_sig_checksigs = 1;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, signature_verify_input, CTLFLAG_RW,
+    &tcp_sig_checksigs, 0, "Verify RFC2385 digests on inbound traffic");
+#endif
+
 VNET_DEFINE(uma_zone_t, sack_hole_zone);
 #define        V_sack_hole_zone                VNET(sack_hole_zone)
 
@@ -1998,6 +2004,66 @@ tcp_signature_compute(struct mbuf *m, in
        KEY_FREESAV(&sav);
        return (0);
 }
+
+/*
+ * Verify the TCP-MD5 hash of a TCP segment. (RFC2385)
+ *
+ * Parameters:
+ * m           pointer to head of mbuf chain
+ * len         length of TCP segment data, excluding options
+ * optlen      length of TCP segment options
+ * buf         pointer to storage for computed MD5 digest
+ * direction   direction of flow (IPSEC_DIR_INBOUND or OUTBOUND)
+ *
+ * Return 1 if successful, otherwise return 0.
+ */
+int
+tcp_signature_verify(struct mbuf *m, int off0, int tlen, int optlen,
+    struct tcpopt *to, struct tcphdr *th, u_int tcpbflag)
+{
+       char tmpdigest[TCP_SIGLEN];
+
+       if (tcp_sig_checksigs == 0)
+               return (1);
+       if ((tcpbflag & TF_SIGNATURE) == 0) {
+               if ((to->to_flags & TOF_SIGNATURE) != 0) {
+
+                       /*
+                        * If this socket is not expecting signature but
+                        * the segment contains signature just fail.
+                        */
+                       TCPSTAT_INC(tcps_sig_err_sigopt);
+                       TCPSTAT_INC(tcps_sig_rcvbadsig);
+                       return (0);
+               }
+
+               /* Signature is not expected, and not present in segment. */
+               return (1);
+       }
+
+       /*
+        * If this socket is expecting signature but the segment does not
+        * contain any just fail.
+        */
+       if ((to->to_flags & TOF_SIGNATURE) == 0) {
+               TCPSTAT_INC(tcps_sig_err_nosigopt);
+               TCPSTAT_INC(tcps_sig_rcvbadsig);
+               return (0);
+       }
+       if (tcp_signature_compute(m, off0, tlen, optlen, &tmpdigest[0],
+           IPSEC_DIR_INBOUND) == -1) {
+               TCPSTAT_INC(tcps_sig_err_buildsig);
+               TCPSTAT_INC(tcps_sig_rcvbadsig);
+               return (0);
+       }
+       
+       if (bcmp(to->to_signature, &tmpdigest[0], TCP_SIGLEN) != 0) {
+               TCPSTAT_INC(tcps_sig_rcvbadsig);
+               return (0);
+       }
+       TCPSTAT_INC(tcps_sig_rcvgoodsig);
+       return (1);
+}
 #endif /* TCP_SIGNATURE */
 
 static int

Modified: head/sys/netinet/tcp_syncache.c
==============================================================================
--- head/sys/netinet/tcp_syncache.c     Mon Apr 25 16:37:47 2011        
(r221022)
+++ head/sys/netinet/tcp_syncache.c     Mon Apr 25 17:13:40 2011        
(r221023)
@@ -1010,7 +1010,8 @@ _syncache_add(struct in_conninfo *inc, s
        struct syncache_head *sch;
        struct mbuf *ipopts = NULL;
        u_int32_t flowtmp;
-       int win, sb_hiwat, ip_ttl, ip_tos, noopt;
+       u_int ltflags;
+       int win, sb_hiwat, ip_ttl, ip_tos;
        char *s;
 #ifdef INET6
        int autoflowlabel = 0;
@@ -1043,7 +1044,7 @@ _syncache_add(struct in_conninfo *inc, s
        ip_tos = inp->inp_ip_tos;
        win = sbspace(&so->so_rcv);
        sb_hiwat = so->so_rcv.sb_hiwat;
-       noopt = (tp->t_flags & TF_NOOPT);
+       ltflags = (tp->t_flags & (TF_NOOPT | TF_SIGNATURE));
 
        /* By the time we drop the lock these should no longer be used. */
        so = NULL;
@@ -1238,14 +1239,14 @@ _syncache_add(struct in_conninfo *inc, s
         * XXX: Currently we always record the option by default and will
         * attempt to use it in syncache_respond().
         */
-       if (to->to_flags & TOF_SIGNATURE)
+       if (to->to_flags & TOF_SIGNATURE || ltflags & TF_SIGNATURE)
                sc->sc_flags |= SCF_SIGNATURE;
 #endif
        if (to->to_flags & TOF_SACKPERM)
                sc->sc_flags |= SCF_SACK;
        if (to->to_flags & TOF_MSS)
                sc->sc_peer_mss = to->to_mss;   /* peer mss may be zero */
-       if (noopt)
+       if (ltflags & TF_NOOPT)
                sc->sc_flags |= SCF_NOOPT;
        if ((th->th_flags & (TH_ECE|TH_CWR)) && V_tcp_do_ecn)
                sc->sc_flags |= SCF_ECN;

Modified: head/sys/netinet/tcp_var.h
==============================================================================
--- head/sys/netinet/tcp_var.h  Mon Apr 25 16:37:47 2011        (r221022)
+++ head/sys/netinet/tcp_var.h  Mon Apr 25 17:13:40 2011        (r221023)
@@ -485,6 +485,13 @@ struct     tcpstat {
        u_long  tcps_ecn_shs;           /* ECN successful handshakes */
        u_long  tcps_ecn_rcwnd;         /* # times ECN reduced the cwnd */
 
+       /* TCP_SIGNATURE related stats */
+       u_long  tcps_sig_rcvgoodsig;    /* Total matching signature received */
+       u_long  tcps_sig_rcvbadsig;     /* Total bad signature received */
+       u_long  tcps_sig_err_buildsig;  /* Mismatching signature received */
+       u_long  tcps_sig_err_sigopt;    /* No signature expected by socket */
+       u_long  tcps_sig_err_nosigopt;  /* No signature provided by segment */
+
        u_long  _pad[12];               /* 6 UTO, 6 TBD */
 };
 
@@ -684,6 +691,8 @@ int  tcp_twrespond(struct tcptw *, int);
 void    tcp_setpersist(struct tcpcb *);
 #ifdef TCP_SIGNATURE
 int     tcp_signature_compute(struct mbuf *, int, int, int, u_char *, u_int);
+int     tcp_signature_verify(struct mbuf *, int, int, int, struct tcpopt *,
+           struct tcphdr *, u_int);
 #endif
 void    tcp_slowtimo(void);
 struct tcptemp *
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to