Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 6 Dec 2016 10:55:50 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r309610 - in projects/ipsec/sys: netinet netipsec
Message-ID:  <201612061055.uB6AtolV025077@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Tue Dec  6 10:55:50 2016
New Revision: 309610
URL: https://svnweb.freebsd.org/changeset/base/309610

Log:
  Rework TCP_SIGNATURE support.
  
  Move all TCP_SIGNATURE code into xform_tcp.c.
  Handle TCP_MD5SIG socket option in tcp_ipsec_pcbctl() function.
  Add tcp_ipsec_input() and tcp_ipsec_output() functions to check inbound
  segments and calculate TCP-MD5 digest for outbound segments.
  
  Change how we handle signed and unsigned segments depending from TCP_MD5SIG
  socket option.
  
  tcp_input:
    Since we handle SYN and ACK in the syncache code, thus one of
    TCP_SIGNATURE blocks in tcp_input() is dead code. Remove it.
    Modify syncache_expand() to return -1 when checking of TCP-MD5 digest
    failed. RFC2385 requires that "failing comparison must result in the
    segment being dropped and must not produce any response back to the
    sender." If syncache_expand() returns -1, we just drop the segment.
  
    In other cases if socket has TF_SIGNATURE flag and there is no signature
    in the segment - drop segment. If signature verification is failed -
    drop segment.
  
  tcp_output:
    If socket has TF_SIGNATURE flag, but signature was not created (due to
    missed SA) - drop segment. From my point of view it is useless to send
    TCP segment with wrong signature (all zeroes). It will not pass inbound
    verification and we will not receive the reply.
  
    Change error handling in tcp_output() for case when error code is EACCESS.
    tcp_ipsec_output() returns EACCESS when SA was not found.
    XXX: this needs proper review. Without this if we trying to initiate
    TCP session, but SA is not yet installed, connection will be aborted after
    first failed try to send SYN.
  
    In tcp_addoptions() reset TOF_SIGNATURE flag if some error occurs and
    no space was reserved for signature.
  
  tcp_subr:
    Remove signature_verify_input sysctl. I don't see the reason when it
    could be useful.
  
  tcp_syncache:
    In syncache_add() if listenning socket has TCP_MD5SIG option, first of
    check that received SYN has signature, then check that it is correct.
    Do not create cache entry in case of failed checks.
  
    In syncache_expand() check that ACK has valid signature, if not - drop it.
  
    In syncache_respond() do not send segment if signature was not created,
    but it is required.

Modified:
  projects/ipsec/sys/netinet/tcp_input.c
  projects/ipsec/sys/netinet/tcp_output.c
  projects/ipsec/sys/netinet/tcp_subr.c
  projects/ipsec/sys/netinet/tcp_syncache.c
  projects/ipsec/sys/netinet/tcp_usrreq.c
  projects/ipsec/sys/netinet/tcp_var.h
  projects/ipsec/sys/netipsec/ipsec.h
  projects/ipsec/sys/netipsec/xform_tcp.c

Modified: projects/ipsec/sys/netinet/tcp_input.c
==============================================================================
--- projects/ipsec/sys/netinet/tcp_input.c	Tue Dec  6 10:37:27 2016	(r309609)
+++ projects/ipsec/sys/netinet/tcp_input.c	Tue Dec  6 10:55:50 2016	(r309610)
@@ -487,20 +487,6 @@ cc_post_recovery(struct tcpcb *tp, struc
 	tp->t_bytes_acked = 0;
 }
 
-#ifdef TCP_SIGNATURE
-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
-
 /*
  * Indicate whether this ack should be delayed.  We can delay the ack if
  * following conditions are met:
@@ -611,9 +597,6 @@ tcp_input(struct mbuf **mp, int *offp, i
 	int drop_hdrlen;
 	int thflags;
 	int rstreason = 0;	/* For badport_bandlim accounting purposes */
-#ifdef TCP_SIGNATURE
-	uint8_t sig_checked = 0;
-#endif
 	uint8_t iptos;
 	struct m_tag *fwd_tag = NULL;
 #ifdef INET6
@@ -1135,7 +1118,16 @@ relocked:
 			 * NB: syncache_expand() doesn't unlock
 			 * inp and tcpinfo locks.
 			 */
-			if (!syncache_expand(&inc, &to, th, &so, m)) {
+			rstreason = syncache_expand(&inc, &to, th, &so, m);
+			if (rstreason < 0) {
+				/*
+				 * A failing TCP MD5 signature comparison
+				 * must result in the segment being dropped
+				 * and must not produce any response back
+				 * to the sender.
+				 */
+				goto dropunlock;
+			} else if (rstreason == 0) {
 				/*
 				 * No syncache entry or ACK was not
 				 * for our SYN/ACK.  Send a RST.
@@ -1187,26 +1179,6 @@ tfo_socket_result:
 			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
@@ -1436,26 +1408,17 @@ tfo_socket_result:
 		 */
 		goto dropunlock;
 	}
-
 #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;
+	if (tp->t_flags & TF_SIGNATURE) {
+		tcp_dooptions(&to, optp, optlen, thflags);
+		if ((to.to_flags & TOF_SIGNATURE) == 0) {
+			TCPSTAT_INC(tcps_sig_err_nosigopt);
+			goto dropunlock;
 		}
-		sig_checked = 1;
+		if (tcp_ipsec_input(m, th, to.to_signature) != 0)
+			goto dropunlock;
 	}
 #endif
-
 	TCP_PROBE5(receive, NULL, tp, mtod(m, const char *), tp, th);
 
 	/*
@@ -1632,6 +1595,13 @@ tcp_do_segment(struct mbuf *m, struct tc
 	    (th->th_off << 2) - sizeof(struct tcphdr),
 	    (thflags & TH_SYN) ? TO_SYN : 0);
 
+#ifdef TCP_SIGNATURE
+	if ((tp->t_flags & TF_SIGNATURE) != 0 &&
+	    (to.to_flags & TOF_SIGNATURE) == 0) {
+		TCPSTAT_INC(tcps_sig_err_sigopt);
+		/* XXX: should drop? */
+	}
+#endif
 	/*
 	 * If echoed timestamp is later than the current time,
 	 * fall back to non RFC1323 RTT calculation.  Normalize
@@ -3418,20 +3388,19 @@ tcp_dooptions(struct tcpopt *to, u_char 
 			    (char *)&to->to_tsecr, sizeof(to->to_tsecr));
 			to->to_tsecr = ntohl(to->to_tsecr);
 			break;
-#ifdef TCP_SIGNATURE
-		/*
-		 * XXX In order to reply to a host which has set the
-		 * TCP_SIGNATURE option in its initial SYN, we have to
-		 * record the fact that the option was observed here
-		 * for the syncache code to perform the correct response.
-		 */
 		case TCPOPT_SIGNATURE:
+			/*
+			 * In order to reply to a host which has set the
+			 * TCP_SIGNATURE option in its initial SYN, we have
+			 * to record the fact that the option was observed
+			 * here for the syncache code to perform the correct
+			 * response.
+			 */
 			if (optlen != TCPOLEN_SIGNATURE)
 				continue;
 			to->to_flags |= TOF_SIGNATURE;
 			to->to_signature = cp + 2;
 			break;
-#endif
 		case TCPOPT_SACK_PERMITTED:
 			if (optlen != TCPOLEN_SACK_PERMITTED)
 				continue;

Modified: projects/ipsec/sys/netinet/tcp_output.c
==============================================================================
--- projects/ipsec/sys/netinet/tcp_output.c	Tue Dec  6 10:37:27 2016	(r309609)
+++ projects/ipsec/sys/netinet/tcp_output.c	Tue Dec  6 10:55:50 2016	(r309610)
@@ -825,6 +825,10 @@ send:
 		}
 #ifdef TCP_SIGNATURE
 		/* TCP-MD5 (RFC2385). */
+		/*
+		 * Check that TCP_MD5SIG is enabled in tcpcb to
+		 * account the size needed to set this TCP option.
+		 */
 		if (tp->t_flags & TF_SIGNATURE)
 			to.to_flags |= TOF_SIGNATURE;
 #endif /* TCP_SIGNATURE */
@@ -1251,20 +1255,31 @@ send:
 		 */
 		tp->snd_up = tp->snd_una;		/* drag it along */
 
-#ifdef TCP_SIGNATURE
-	if (to.to_flags & TOF_SIGNATURE) {
-		int sigoff = to.to_signature - opt;
-		tcp_signature_compute(m, 0, len, optlen,
-		    (u_char *)(th + 1) + sigoff, IPSEC_DIR_OUTBOUND);
-	}
-#endif
-
 	/*
 	 * Put TCP length in extended header, and then
 	 * checksum extended header and data.
 	 */
 	m->m_pkthdr.len = hdrlen + len; /* in6_cksum() need this */
 	m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
+
+#ifdef TCP_SIGNATURE
+	if (to.to_flags & TOF_SIGNATURE) {
+		/*
+		 * Calculate MD5 signature and put it into the place
+		 * determined before.
+		 * NOTE: since TCP options buffer doesn't point into
+		 * mbuf's data, calculate offset and use it.
+		 */
+		if ((error = tcp_ipsec_output(m, th, (u_char *)(th + 1) +
+		    (to.to_signature - opt))) != 0) {
+			/*
+			 * Do not send segment if the calculation of MD5
+			 * digest has failed.
+			 */
+			goto out;
+		}
+	}
+#endif
 #ifdef INET6
 	if (isipv6) {
 		/*
@@ -1561,6 +1576,9 @@ timer:
 		}
 		SOCKBUF_UNLOCK_ASSERT(&so->so_snd);	/* Check gotos. */
 		switch (error) {
+		case EACCES:
+			tp->t_softerror = error;
+			return (0);
 		case EPERM:
 			tp->t_softerror = error;
 			return (error);
@@ -1728,7 +1746,6 @@ tcp_addoptions(struct tcpopt *to, u_char
 			bcopy((u_char *)&to->to_tsecr, optp, sizeof(to->to_tsecr));
 			optp += sizeof(to->to_tsecr);
 			break;
-#ifdef TCP_SIGNATURE
 		case TOF_SIGNATURE:
 			{
 			int siglen = TCPOLEN_SIGNATURE - 2;
@@ -1737,8 +1754,10 @@ tcp_addoptions(struct tcpopt *to, u_char
 				optlen += TCPOLEN_NOP;
 				*optp++ = TCPOPT_NOP;
 			}
-			if (TCP_MAXOLEN - optlen < TCPOLEN_SIGNATURE)
+			if (TCP_MAXOLEN - optlen < TCPOLEN_SIGNATURE) {
+				to->to_flags &= ~TOF_SIGNATURE;
 				continue;
+			}
 			optlen += TCPOLEN_SIGNATURE;
 			*optp++ = TCPOPT_SIGNATURE;
 			*optp++ = TCPOLEN_SIGNATURE;
@@ -1747,7 +1766,6 @@ tcp_addoptions(struct tcpopt *to, u_char
 				 *optp++ = 0;
 			break;
 			}
-#endif
 		case TOF_SACK:
 			{
 			int sackblks = 0;

Modified: projects/ipsec/sys/netinet/tcp_subr.c
==============================================================================
--- projects/ipsec/sys/netinet/tcp_subr.c	Tue Dec  6 10:37:27 2016	(r309609)
+++ projects/ipsec/sys/netinet/tcp_subr.c	Tue Dec  6 10:55:50 2016	(r309610)
@@ -233,12 +233,6 @@ static int	tcp_soreceive_stream;
 SYSCTL_INT(_net_inet_tcp, OID_AUTO, soreceive_stream, CTLFLAG_RDTUN,
     &tcp_soreceive_stream, 0, "Using soreceive_stream for TCP sockets");
 
-#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)
 
@@ -1127,8 +1121,10 @@ tcp_respond(struct tcpcb *tp, void *ipge
 
 #ifdef TCP_SIGNATURE
 	if (to.to_flags & TOF_SIGNATURE) {
-		tcp_signature_compute(m, 0, 0, optlen, to.to_signature,
-		    IPSEC_DIR_OUTBOUND);
+		if (tcp_ipsec_output(m, nth, to.to_signature) != 0) {
+			m_freem(m);
+			return;
+		}
 	}
 #endif
 
@@ -2527,299 +2523,6 @@ tcp_maxseg(const struct tcpcb *tp)
 	return (tp->t_maxseg - optlen);
 }
 
-#ifdef TCP_SIGNATURE
-/*
- * Callback function invoked by m_apply() to digest TCP segment data
- * contained within an mbuf chain.
- */
-static int
-tcp_signature_apply(void *fstate, void *data, u_int len)
-{
-
-	MD5Update(fstate, (u_char *)data, len);
-	return (0);
-}
-
-/*
- * XXX The key is retrieved from the system's PF_KEY SADB, by keying a
- * search with the destination IP address, and a 'magic SPI' to be
- * determined by the application. This is hardcoded elsewhere to 1179
-*/
-struct secasvar *
-tcp_get_sav(struct mbuf *m, u_int direction)
-{
-	union sockaddr_union dst;
-	struct secasvar *sav;
-	struct ip *ip;
-#ifdef INET6
-	struct ip6_hdr *ip6;
-	char ip6buf[INET6_ADDRSTRLEN];
-#endif
-
-	/* Extract the destination from the IP header in the mbuf. */
-	bzero(&dst, sizeof(union sockaddr_union));
-	ip = mtod(m, struct ip *);
-#ifdef INET6
-	ip6 = NULL;	/* Make the compiler happy. */
-#endif
-	switch (ip->ip_v) {
-#ifdef INET
-	case IPVERSION:
-		dst.sa.sa_len = sizeof(struct sockaddr_in);
-		dst.sa.sa_family = AF_INET;
-		dst.sin.sin_addr = (direction == IPSEC_DIR_INBOUND) ?
-		    ip->ip_src : ip->ip_dst;
-		break;
-#endif
-#ifdef INET6
-	case (IPV6_VERSION >> 4):
-		ip6 = mtod(m, struct ip6_hdr *);
-		dst.sa.sa_len = sizeof(struct sockaddr_in6);
-		dst.sa.sa_family = AF_INET6;
-		dst.sin6.sin6_addr = (direction == IPSEC_DIR_INBOUND) ?
-		    ip6->ip6_src : ip6->ip6_dst;
-		break;
-#endif
-	default:
-		return (NULL);
-		/* NOTREACHED */
-		break;
-	}
-
-	/* Look up an SADB entry which matches the address of the peer. */
-	sav = key_allocsa(&dst, IPPROTO_TCP, htonl(TCP_SIG_SPI));
-	if (sav == NULL) {
-		ipseclog((LOG_ERR, "%s: SADB lookup failed for %s\n", __func__,
-		    (ip->ip_v == IPVERSION) ? inet_ntoa(dst.sin.sin_addr) :
-#ifdef INET6
-			(ip->ip_v == (IPV6_VERSION >> 4)) ?
-			    ip6_sprintf(ip6buf, &dst.sin6.sin6_addr) :
-#endif
-			"(unsupported)"));
-	}
-
-	return (sav);
-}
-
-/*
- * Compute 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
- * sav		pointer to security assosiation
- *
- * We do this over ip, tcphdr, segment data, and the key in the SADB.
- * When called from tcp_input(), we can be sure that th_sum has been
- * zeroed out and verified already.
- *
- * Releases reference to SADB key before return. 
- *
- * Return 0 if successful, otherwise return -1.
- *
- */
-int
-tcp_signature_do_compute(struct mbuf *m, int len, int optlen,
-    u_char *buf, struct secasvar *sav)
-{
-#ifdef INET
-	struct ippseudo ippseudo;
-#endif
-	MD5_CTX ctx;
-	int doff;
-	struct ip *ip;
-#ifdef INET
-	struct ipovly *ipovly;
-#endif
-	struct tcphdr *th;
-#ifdef INET6
-	struct ip6_hdr *ip6;
-	struct in6_addr in6;
-	uint32_t plen;
-	uint16_t nhdr;
-#endif
-	u_short savecsum;
-
-	KASSERT(m != NULL, ("NULL mbuf chain"));
-	KASSERT(buf != NULL, ("NULL signature pointer"));
-
-	/* Extract the destination from the IP header in the mbuf. */
-	ip = mtod(m, struct ip *);
-#ifdef INET6
-	ip6 = NULL;	/* Make the compiler happy. */
-#endif
-
-	MD5Init(&ctx);
-	/*
-	 * Step 1: Update MD5 hash with IP(v6) pseudo-header.
-	 *
-	 * XXX The ippseudo header MUST be digested in network byte order,
-	 * or else we'll fail the regression test. Assume all fields we've
-	 * been doing arithmetic on have been in host byte order.
-	 * XXX One cannot depend on ipovly->ih_len here. When called from
-	 * tcp_output(), the underlying ip_len member has not yet been set.
-	 */
-	switch (ip->ip_v) {
-#ifdef INET
-	case IPVERSION:
-		ipovly = (struct ipovly *)ip;
-		ippseudo.ippseudo_src = ipovly->ih_src;
-		ippseudo.ippseudo_dst = ipovly->ih_dst;
-		ippseudo.ippseudo_pad = 0;
-		ippseudo.ippseudo_p = IPPROTO_TCP;
-		ippseudo.ippseudo_len = htons(len + sizeof(struct tcphdr) +
-		    optlen);
-		MD5Update(&ctx, (char *)&ippseudo, sizeof(struct ippseudo));
-
-		th = (struct tcphdr *)((u_char *)ip + sizeof(struct ip));
-		doff = sizeof(struct ip) + sizeof(struct tcphdr) + optlen;
-		break;
-#endif
-#ifdef INET6
-	/*
-	 * RFC 2385, 2.0  Proposal
-	 * For IPv6, the pseudo-header is as described in RFC 2460, namely the
-	 * 128-bit source IPv6 address, 128-bit destination IPv6 address, zero-
-	 * extended next header value (to form 32 bits), and 32-bit segment
-	 * length.
-	 * Note: Upper-Layer Packet Length comes before Next Header.
-	 */
-	case (IPV6_VERSION >> 4):
-		ip6 = mtod(m, struct ip6_hdr *);
-		in6 = ip6->ip6_src;
-		in6_clearscope(&in6);
-		MD5Update(&ctx, (char *)&in6, sizeof(struct in6_addr));
-		in6 = ip6->ip6_dst;
-		in6_clearscope(&in6);
-		MD5Update(&ctx, (char *)&in6, sizeof(struct in6_addr));
-		plen = htonl(len + sizeof(struct tcphdr) + optlen);
-		MD5Update(&ctx, (char *)&plen, sizeof(uint32_t));
-		nhdr = 0;
-		MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
-		MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
-		MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
-		nhdr = IPPROTO_TCP;
-		MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
-
-		th = (struct tcphdr *)((u_char *)ip6 + sizeof(struct ip6_hdr));
-		doff = sizeof(struct ip6_hdr) + sizeof(struct tcphdr) + optlen;
-		break;
-#endif
-	default:
-		key_freesav(&sav);
-		return (-1);
-		/* NOTREACHED */
-		break;
-	}
-
-
-	/*
-	 * Step 2: Update MD5 hash with TCP header, excluding options.
-	 * The TCP checksum must be set to zero.
-	 */
-	savecsum = th->th_sum;
-	th->th_sum = 0;
-	MD5Update(&ctx, (char *)th, sizeof(struct tcphdr));
-	th->th_sum = savecsum;
-
-	/*
-	 * Step 3: Update MD5 hash with TCP segment data.
-	 *         Use m_apply() to avoid an early m_pullup().
-	 */
-	if (len > 0)
-		m_apply(m, doff, len, tcp_signature_apply, &ctx);
-
-	/*
-	 * Step 4: Update MD5 hash with shared secret.
-	 */
-	MD5Update(&ctx, sav->key_auth->key_data, _KEYLEN(sav->key_auth));
-	MD5Final(buf, &ctx);
-
-	key_sa_recordxfer(sav, m);
-	key_freesav(&sav);
-	return (0);
-}
-
-/*
- * Compute TCP-MD5 hash of a TCP segment. (RFC2385)
- *
- * Return 0 if successful, otherwise return -1.
- */
-int
-tcp_signature_compute(struct mbuf *m, int _unused, int len, int optlen,
-    u_char *buf, u_int direction)
-{
-	struct secasvar *sav;
-
-	if ((sav = tcp_get_sav(m, direction)) == NULL)
-		return (-1);
-
-	return (tcp_signature_do_compute(m, len, optlen, buf, sav));
-}
-
-/*
- * 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
 sysctl_drop(SYSCTL_HANDLER_ARGS)
 {

Modified: projects/ipsec/sys/netinet/tcp_syncache.c
==============================================================================
--- projects/ipsec/sys/netinet/tcp_syncache.c	Tue Dec  6 10:37:27 2016	(r309609)
+++ projects/ipsec/sys/netinet/tcp_syncache.c	Tue Dec  6 10:55:50 2016	(r309610)
@@ -996,7 +996,55 @@ syncache_expand(struct in_conninfo *inc,
 				    "(probably spoofed)\n", s, __func__);
 			goto failed;
 		}
+#ifdef TCP_SIGNATURE
+		/* If received ACK has MD5 signature, check it. */
+		if ((to->to_flags & TOF_SIGNATURE) != 0 &&
+		    tcp_ipsec_input(m, th, to->to_signature) != 0) {
+			/* Drop the ACK. */
+			if ((s = tcp_log_addrs(inc, th, NULL, NULL))) {
+				log(LOG_DEBUG, "%s; %s: Segment rejected, "
+				    "MD5 signature doesn't match.\n",
+				    s, __func__);
+				free(s, M_TCPLOG);
+			}
+			TCPSTAT_INC(tcps_sig_err_sigopt);
+			return (-1); /* Do not send RST */
+		}
+#endif /* TCP_SIGNATURE */
 	} else {
+#ifdef TCP_SIGNATURE
+		/*
+		 * If listening socket requested TCP digests, check that
+		 * received ACK has signature and it is correct.
+		 * If not, drop the ACK and leave sc entry in th cache,
+		 * because SYN was received with correct signature.
+		 */
+		if (sc->sc_flags & SCF_SIGNATURE) {
+			if ((to->to_flags & TOF_SIGNATURE) == 0) {
+				/* No signature */
+				TCPSTAT_INC(tcps_sig_err_nosigopt);
+				SCH_UNLOCK(sch);
+				if ((s = tcp_log_addrs(inc, th, NULL, NULL))) {
+					log(LOG_DEBUG, "%s; %s: Segment "
+					    "rejected, MD5 signature wasn't "
+					    "provided.\n", s, __func__);
+					free(s, M_TCPLOG);
+				}
+				return (-1); /* Do not send RST */
+			}
+			if (tcp_ipsec_input(m, th, to->to_signature) != 0) {
+				/* Doesn't match or no SA */
+				SCH_UNLOCK(sch);
+				if ((s = tcp_log_addrs(inc, th, NULL, NULL))) {
+					log(LOG_DEBUG, "%s; %s: Segment "
+					    "rejected, MD5 signature doesn't "
+					    "match.\n", s, __func__);
+					free(s, M_TCPLOG);
+				}
+				return (-1); /* Do not send RST */
+			}
+		}
+#endif /* TCP_SIGNATURE */
 		/*
 		 * Pull out the entry to unlock the bucket row.
 		 * 
@@ -1260,6 +1308,21 @@ syncache_add(struct in_conninfo *inc, st
 		ipopts = NULL;
 #endif
 
+#ifdef TCP_SIGNATURE
+	/*
+	 * If listening socket requested TCP digests, check that received
+	 * SYN has signature and it is correct. If signature doesn't match
+	 * or TCP_SIGNATURE support isn't enabled, drop the packet.
+	 */
+	if (ltflags & TF_SIGNATURE) {
+		if ((to->to_flags & TOF_SIGNATURE) == 0) {
+			TCPSTAT_INC(tcps_sig_err_nosigopt);
+			goto done;
+		}
+		if (tcp_ipsec_input(m, th, to->to_signature) != 0)
+			goto done;
+	}
+#endif	/* TCP_SIGNATURE */
 	/*
 	 * See if we already have an entry for this connection.
 	 * If we do, resend the SYN,ACK, and reset the retransmit timer.
@@ -1437,13 +1500,13 @@ skip_alloc:
 	}
 #ifdef TCP_SIGNATURE
 	/*
-	 * If listening socket requested TCP digests, OR received SYN
-	 * contains the option, flag this in the syncache so that
-	 * syncache_respond() will do the right thing with the SYN+ACK.
+	 * If listening socket requested TCP digests, flag this in the
+	 * syncache so that syncache_respond() will do the right thing
+	 * with the SYN+ACK.
 	 */
-	if (to->to_flags & TOF_SIGNATURE || ltflags & TF_SIGNATURE)
+	if (ltflags & TF_SIGNATURE)
 		sc->sc_flags |= SCF_SIGNATURE;
-#endif
+#endif	/* TCP_SIGNATURE */
 	if (to->to_flags & TOF_SACKPERM)
 		sc->sc_flags |= SCF_SACK;
 	if (to->to_flags & TOF_MSS)
@@ -1534,10 +1597,6 @@ syncache_respond(struct syncache *sc, st
 #ifdef INET6
 	struct ip6_hdr *ip6 = NULL;
 #endif
-#ifdef TCP_SIGNATURE
-	struct secasvar *sav;
-#endif
-
 	hlen =
 #ifdef INET6
 	       (sc->sc_inc.inc_flags & INC_ISIPV6) ? sizeof(struct ip6_hdr) :
@@ -1647,31 +1706,9 @@ syncache_respond(struct syncache *sc, st
 		if (sc->sc_flags & SCF_SACK)
 			to.to_flags |= TOF_SACKPERM;
 #ifdef TCP_SIGNATURE
-		sav = NULL;
-		if (sc->sc_flags & SCF_SIGNATURE) {
-			sav = tcp_get_sav(m, IPSEC_DIR_OUTBOUND);
-			if (sav != NULL)
-				to.to_flags |= TOF_SIGNATURE;
-			else {
-
-				/*
-				 * We've got SCF_SIGNATURE flag
-				 * inherited from listening socket,
-				 * but no SADB key for given source
-				 * address. Assume signature is not
-				 * required and remove signature flag
-				 * instead of silently dropping
-				 * connection.
-				 */
-				if (locked == 0)
-					SCH_LOCK(sch);
-				sc->sc_flags &= ~SCF_SIGNATURE;
-				if (locked == 0)
-					SCH_UNLOCK(sch);
-			}
-		}
+		if (sc->sc_flags & SCF_SIGNATURE)
+			to.to_flags |= TOF_SIGNATURE;
 #endif
-
 #ifdef TCP_RFC7413
 		if (sc->sc_tfo_cookie) {
 			to.to_flags |= TOF_FASTOPEN;
@@ -1687,18 +1724,24 @@ syncache_respond(struct syncache *sc, st
 		th->th_off = (sizeof(struct tcphdr) + optlen) >> 2;
 		m->m_len += optlen;
 		m->m_pkthdr.len += optlen;
-
-#ifdef TCP_SIGNATURE
-		if (sc->sc_flags & SCF_SIGNATURE)
-			tcp_signature_do_compute(m, 0, optlen,
-			    to.to_signature, sav);
-#endif
 #ifdef INET6
 		if (sc->sc_inc.inc_flags & INC_ISIPV6)
 			ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) + optlen);
 		else
 #endif
 			ip->ip_len = htons(ntohs(ip->ip_len) + optlen);
+#ifdef TCP_SIGNATURE
+		if (sc->sc_flags & SCF_SIGNATURE) {
+			KASSERT(to.to_flags & TOF_SIGNATURE,
+			    ("tcp_addoptions() didn't set tcp_signature"));
+
+			/* NOTE: to.to_signature is inside of mbuf */
+			if (tcp_ipsec_output(m, th, to.to_signature) != 0) {
+				m_freem(m);
+				return (EACCES);
+			}
+		}
+#endif
 	} else
 		optlen = 0;
 

Modified: projects/ipsec/sys/netinet/tcp_usrreq.c
==============================================================================
--- projects/ipsec/sys/netinet/tcp_usrreq.c	Tue Dec  6 10:37:27 2016	(r309609)
+++ projects/ipsec/sys/netinet/tcp_usrreq.c	Tue Dec  6 10:55:50 2016	(r309610)
@@ -101,6 +101,9 @@ __FBSDID("$FreeBSD$");
 #ifdef TCP_OFFLOAD
 #include <netinet/tcp_offload.h>
 #endif
+#ifdef TCP_SIGNATURE
+#include <netipsec/ipsec.h>
+#endif
 
 /*
  * TCP protocol interface to socket abstraction.
@@ -1554,17 +1557,9 @@ tcp_default_ctloutput(struct socket *so,
 		switch (sopt->sopt_name) {
 #ifdef TCP_SIGNATURE
 		case TCP_MD5SIG:
-			INP_WUNLOCK(inp);
-			error = sooptcopyin(sopt, &optval, sizeof optval,
-			    sizeof optval);
+			error = tcp_ipsec_pcbctl(inp, sopt);
 			if (error)
 				return (error);
-
-			INP_WLOCK_RECHECK(inp);
-			if (optval > 0)
-				tp->t_flags |= TF_SIGNATURE;
-			else
-				tp->t_flags &= ~TF_SIGNATURE;
 			goto unlock_and_done;
 #endif /* TCP_SIGNATURE */
 
@@ -1794,9 +1789,7 @@ unlock_and_done:
 		switch (sopt->sopt_name) {
 #ifdef TCP_SIGNATURE
 		case TCP_MD5SIG:
-			optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0;
-			INP_WUNLOCK(inp);
-			error = sooptcopyout(sopt, &optval, sizeof optval);
+			error = tcp_ipsec_pcbctl(inp, sopt);
 			break;
 #endif
 

Modified: projects/ipsec/sys/netinet/tcp_var.h
==============================================================================
--- projects/ipsec/sys/netinet/tcp_var.h	Tue Dec  6 10:37:27 2016	(r309609)
+++ projects/ipsec/sys/netinet/tcp_var.h	Tue Dec  6 10:55:50 2016	(r309610)
@@ -363,21 +363,6 @@ struct tcpcb {
 #define	TCPOOB_HAVEDATA	0x01
 #define	TCPOOB_HADDATA	0x02
 
-#ifdef TCP_SIGNATURE
-/*
- * Defines which are needed by the xform_tcp module and tcp_[in|out]put
- * for SADB verification and lookup.
- */
-#define	TCP_SIGLEN	16	/* length of computed digest in bytes */
-#define	TCP_KEYLEN_MIN	1	/* minimum length of TCP-MD5 key */
-#define	TCP_KEYLEN_MAX	80	/* maximum length of TCP-MD5 key */
-/*
- * Only a single SA per host may be specified at this time. An SPI is
- * needed in order for the KEY_ALLOCSA() lookup to work.
- */
-#define	TCP_SIG_SPI	0x1000
-#endif /* TCP_SIGNATURE */
-
 /*
  * Flags for PLPMTU handling, t_flags2
  */
@@ -614,7 +599,7 @@ struct	tcpstat {
 	/* TCP_SIGNATURE related stats */
 	uint64_t tcps_sig_rcvgoodsig;	/* Total matching signature received */
 	uint64_t tcps_sig_rcvbadsig;	/* Total bad signature received */
-	uint64_t tcps_sig_err_buildsig;	/* Mismatching signature received */
+	uint64_t tcps_sig_err_buildsig;	/* Failed to make signature */
 	uint64_t tcps_sig_err_sigopt;	/* No signature expected by socket */
 	uint64_t tcps_sig_err_nosigopt;	/* No signature provided by segment */
 
@@ -835,17 +820,6 @@ void	 tcp_tw_zone_change(void);
 int	 tcp_twcheck(struct inpcb *, struct tcpopt *, struct tcphdr *,
 	    struct mbuf *, int);
 void	 tcp_setpersist(struct tcpcb *);
-#ifdef TCP_SIGNATURE
-struct secasvar;
-struct secasvar *tcp_get_sav(struct mbuf *, u_int);
-int	 tcp_signature_do_compute(struct mbuf *, int, int, u_char *,
-	    struct secasvar *);
-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);
-int	tcp_signature_check(struct mbuf *m, int off0, int tlen, int optlen,
-	    struct tcpopt *to, struct tcphdr *th, u_int tcpbflag);
-#endif
 void	 tcp_slowtimo(void);
 struct tcptemp *
 	 tcpip_maketemplate(struct inpcb *);
@@ -889,17 +863,6 @@ tcp_fields_to_host(struct tcphdr *th)
 	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);
-}
-#endif
 #endif /* _KERNEL */
 
 #endif /* _NETINET_TCP_VAR_H_ */

Modified: projects/ipsec/sys/netipsec/ipsec.h
==============================================================================
--- projects/ipsec/sys/netipsec/ipsec.h	Tue Dec  6 10:37:27 2016	(r309609)
+++ projects/ipsec/sys/netipsec/ipsec.h	Tue Dec  6 10:55:50 2016	(r309610)
@@ -297,6 +297,7 @@ struct inpcb;
 struct m_tag;
 struct secasvar;
 struct sockopt;
+struct tcphdr;
 union sockaddr_union;
 
 int ipsec_if_input(struct mbuf *, struct secasvar *, uint32_t);
@@ -315,10 +316,16 @@ int ipsec_delete_pcbpolicy(struct inpcb 
 int ipsec_copy_pcbpolicy(struct inpcb *, struct inpcb *);
 int ipsec_control_pcbpolicy(struct inpcb *, struct sockopt *);
 
+int tcp_ipsec_pcbctl(struct inpcb *, struct sockopt *);
+int tcp_ipsec_input(struct mbuf *, struct tcphdr *, u_char *);
+int tcp_ipsec_output(struct mbuf *, struct tcphdr *, u_char *);
+
 int ipsec_chkreplay(uint32_t, struct secasvar *);
 int ipsec_updatereplay(uint32_t, struct secasvar *);
 int ipsec_updateid(struct secasvar *, uint64_t *, uint64_t *);
 
+void ipsec_setsockaddrs(const struct mbuf *, union sockaddr_union *,
+    union sockaddr_union *);
 char *ipsec_address(const union sockaddr_union *, char *, socklen_t);
 char *ipsec_logsastr(struct secasvar *, char *, size_t);
 

Modified: projects/ipsec/sys/netipsec/xform_tcp.c
==============================================================================
--- projects/ipsec/sys/netipsec/xform_tcp.c	Tue Dec  6 10:37:27 2016	(r309609)
+++ projects/ipsec/sys/netipsec/xform_tcp.c	Tue Dec  6 10:55:50 2016	(r309610)
@@ -1,7 +1,6 @@
-/*	$FreeBSD$ */
-
 /*-
  * Copyright (c) 2003 Bruce M. Simpson <bms@spc.org>
+ * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,6 +27,9 @@
  */
 
 /* TCP MD5 Signature Option (RFC2385) */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
 #include "opt_inet.h"
 #include "opt_inet6.h"
 
@@ -35,12 +37,15 @@
 #include <sys/systm.h>
 #include <sys/mbuf.h>
 #include <sys/lock.h>
+#include <sys/md5.h>
+#include <sys/rwlock.h>
 #include <sys/socket.h>
+#include <sys/sockopt.h>
 #include <sys/kernel.h>
 #include <sys/protosw.h>
-#include <sys/sysctl.h>
 
 #include <netinet/in.h>
+#include <netinet/in_pcb.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
 #include <netinet/ip_var.h>
@@ -60,13 +65,255 @@
 #include <netipsec/key.h>
 #include <netipsec/key_debug.h>
 
+#ifdef TCP_SIGNATURE
+
+#define	TCP_SIGLEN	16	/* length of computed digest in bytes */
+#define	TCP_KEYLEN_MIN	1	/* minimum length of TCP-MD5 key */
+#define	TCP_KEYLEN_MAX	80	/* maximum length of TCP-MD5 key */
+
+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);
+}
+
+int
+tcp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt)
+{
+	struct tcpcb *tp;
+	int error, optval;
+
+	INP_WLOCK_ASSERT(inp);
+	if (sopt->sopt_name != TCP_MD5SIG) {
+		INP_WUNLOCK(inp);
+		return (ENOPROTOOPT);
+	}
+
+	tp = intotcpcb(inp);
+	if (sopt->sopt_dir == SOPT_GET) {
+		optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0;
+		INP_WUNLOCK(inp);
+
+		/* On success return with released INP_WLOCK */
+		return (sooptcopyout(sopt, &optval, sizeof(optval)));
+	}
+
+	INP_WUNLOCK(inp);
+
+	error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval));
+	if (error != 0)
+		return (error);
+
+	/* INP_WLOCK_RECHECK */
+	INP_WLOCK(inp);
+	if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
+		INP_WUNLOCK(inp);
+		return (ECONNRESET);
+	}
+	if (optval > 0)
+		tp->t_flags |= TF_SIGNATURE;
+	else
+		tp->t_flags &= ~TF_SIGNATURE;
+
+	/* On success return with acquired INP_WLOCK */
+	return (error);
+}
+
+/*
+ * Callback function invoked by m_apply() to digest TCP segment data
+ * contained within an mbuf chain.
+ */
+static int
+tcp_signature_apply(void *fstate, void *data, u_int len)
+{
+
+	MD5Update(fstate, (u_char *)data, len);
+	return (0);
+}
+
+#ifdef INET
+static int

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201612061055.uB6AtolV025077>