git: 7205809809e7 - stable/13 - syncache: accept packet with no SA when TCP_MD5SIG is set

From: Robert Wing <rew_at_FreeBSD.org>
Date: Thu, 10 Feb 2022 19:33:29 UTC
The branch stable/13 has been updated by rew:

URL: https://cgit.FreeBSD.org/src/commit/?id=7205809809e70db0abf2cecaf7f5e056650315bb

commit 7205809809e70db0abf2cecaf7f5e056650315bb
Author:     Robert Wing <rew@FreeBSD.org>
AuthorDate: 2022-01-09 01:07:50 +0000
Commit:     Robert Wing <rew@FreeBSD.org>
CommitDate: 2022-02-10 19:31:33 +0000

    syncache: accept packet with no SA when TCP_MD5SIG is set
    
    When TCP_MD5SIG is set on a socket, all packets are dropped that don't
    contain an MD5 signature. Relax this behavior to accept a non-signed
    packet when a security association doesn't exist with the peer.
    
    This is useful when a listen socket set with TCP_MD5SIG wants to handle
    connections protected with and without MD5 signatures.
    
    Reviewed by:    bz (previous version)
    Sponsored by:   nepustil.net
    Sponsored by:   Klara Inc.
    Differential Revision:  https://reviews.freebsd.org/D33227
    
    (cherry picked from commit eb18708ec8c7e1de6a05aba41971659549991b10)
---
 share/man/man4/tcp.4       |  6 +++++-
 sys/netinet/tcp_syncache.c | 30 ++++++++++++++++++------------
 sys/netipsec/xform_tcp.c   |  5 +++++
 3 files changed, 28 insertions(+), 13 deletions(-)

diff --git a/share/man/man4/tcp.4 b/share/man/man4/tcp.4
index e49df5319efb..3fe2753bf408 100644
--- a/share/man/man4/tcp.4
+++ b/share/man/man4/tcp.4
@@ -34,7 +34,7 @@
 .\"     From: @(#)tcp.4	8.1 (Berkeley) 6/5/93
 .\" $FreeBSD$
 .\"
-.Dd June 27, 2021
+.Dd January 8, 2022
 .Dt TCP 4
 .Os
 .Sh NAME
@@ -339,6 +339,10 @@ This entry can only be specified on a per-host basis at this time.
 .Pp
 If an SADB entry cannot be found for the destination,
 the system does not send any outgoing segments and drops any inbound segments.
+However, during connection negotiation, a non-signed segment will be accepted if
+an SADB entry does not exist between hosts.
+When a non-signed segment is accepted, the established connection is not
+protected with MD5 digests.
 .It Dv TCP_STATS
 Manage collection of connection level statistics using the
 .Xr stats 3
diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c
index a1102574b325..e7fb8da52216 100644
--- a/sys/netinet/tcp_syncache.c
+++ b/sys/netinet/tcp_syncache.c
@@ -1513,19 +1513,25 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
 
 #if defined(IPSEC_SUPPORT) || defined(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.
+	 * When the socket is TCP-MD5 enabled check that,
+	 *  - a signed packet is valid
+	 *  - a non-signed packet does not have a security association
+	 *
+	 *  If a signed packet fails validation or a non-signed packet has a
+	 *  security association, the packet will be dropped.
 	 */
 	if (ltflags & TF_SIGNATURE) {
-		if ((to->to_flags & TOF_SIGNATURE) == 0) {
-			TCPSTAT_INC(tcps_sig_err_nosigopt);
-			goto done;
+		if (to->to_flags & TOF_SIGNATURE) {
+			if (!TCPMD5_ENABLED() ||
+			    TCPMD5_INPUT(m, th, to->to_signature) != 0)
+				goto done;
+		} else {
+			if (TCPMD5_ENABLED() &&
+			    TCPMD5_INPUT(m, NULL, NULL) != ENOENT)
+				goto done;
 		}
-		if (!TCPMD5_ENABLED() ||
-		    TCPMD5_INPUT(m, th, to->to_signature) != 0)
-			goto done;
-	}
+	} else if (to->to_flags & TOF_SIGNATURE)
+		goto done;
 #endif	/* TCP_SIGNATURE */
 	/*
 	 * See if we already have an entry for this connection.
@@ -1723,11 +1729,11 @@ skip_alloc:
 	}
 #if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
 	/*
-	 * If listening socket requested TCP digests, flag this in the
+	 * If incoming packet has an MD5 signature, flag this in the
 	 * syncache so that syncache_respond() will do the right thing
 	 * with the SYN+ACK.
 	 */
-	if (ltflags & TF_SIGNATURE)
+	if (to->to_flags & TOF_SIGNATURE)
 		sc->sc_flags |= SCF_SIGNATURE;
 #endif	/* TCP_SIGNATURE */
 	if (to->to_flags & TOF_SACKPERM)
diff --git a/sys/netipsec/xform_tcp.c b/sys/netipsec/xform_tcp.c
index b53544cd00fb..ce2552f0a205 100644
--- a/sys/netipsec/xform_tcp.c
+++ b/sys/netipsec/xform_tcp.c
@@ -269,6 +269,11 @@ tcp_ipsec_input(struct mbuf *m, struct tcphdr *th, u_char *buf)
 		KMOD_TCPSTAT_INC(tcps_sig_err_buildsig);
 		return (ENOENT);
 	}
+	if (buf == NULL) {
+		key_freesav(&sav);
+		KMOD_TCPSTAT_INC(tcps_sig_err_nosigopt);
+		return (EACCES);
+	}
 	/*
 	 * tcp_input() operates with TCP header fields in host
 	 * byte order. We expect them in network byte order.