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
This commit is contained in:
Attilio Rao 2011-04-25 17:13:40 +00:00
parent 8355d59d91
commit 2903309aca
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=221023
4 changed files with 179 additions and 8 deletions

View File

@ -215,6 +215,12 @@ static void tcp_pulloutofband(struct socket *,
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, struct tcphdr *th)
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 @@ tcp_input(struct mbuf *m, int off0)
}
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 @@ tcp_input(struct mbuf *m, int off0)
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 @@ tcp_input(struct mbuf *m, int off0)
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

View File

@ -213,6 +213,12 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, soreceive_stream, CTLFLAG_RDTUN,
&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, int _unused, int len, int optlen,
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

View File

@ -1010,7 +1010,8 @@ _syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
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, struct tcpopt *to, struct tcphdr *th,
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, struct tcpopt *to, struct tcphdr *th,
* 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;

View File

@ -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 *