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:
parent
8355d59d91
commit
2903309aca
@ -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 @@ 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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 *
|
||||
|
Loading…
x
Reference in New Issue
Block a user