DragonFly BSD
DragonFly kernel List (threaded) for 2010-09
[Date Prev][Date Next]  [Thread Prev][Thread Next]  [Date Index][Thread Index]

[PATCH] Import of RFC 2385 (TCP-MD5) digest support from FreeBSD


From: BERARD David <contact@xxxxxxxxxxxxxx>
Date: Thu, 2 Sep 2010 15:17:07 +0000

This can be enabled by compiling a kernel with options TCP_SIGNATURE
and IPSEC.

For the uninitiated, this is a TCP option which provides for a means of
authenticating TCP sessions which came into being before IPSEC. It is
still relevant today, however, as it is used by many commercial router
vendors, particularly with BGP, and as such has become a requirement for
interconnect at many major Internet points of presence.

Tested with a Cisco 2611XM running IOS 12.3(24), and Quagga 0.99.17
---
 lib/libipsec/ipsec_dump_policy.c |    3 +
 lib/libipsec/pfkey.c             |   15 +++++-
 lib/libipsec/pfkey_dump.c        |    3 +
 lib/libipsec/policy_token.l      |    1 +
 share/man/man4/tcp.4             |   31 +++++++++++
 sys/conf/options                 |    1 +
 sys/config/LINT                  |    8 +++
 sys/netinet/ip.h                 |   12 ++++
 sys/netinet/ip_output.c          |    1 +
 sys/netinet/tcp.h                |    3 +
 sys/netinet/tcp_input.c          |   17 ++++++-
 sys/netinet/tcp_output.c         |   32 +++++++++++
 sys/netinet/tcp_subr.c           |  108 ++++++++++++++++++++++++++++++++++++++
 sys/netinet/tcp_syncache.c       |   42 +++++++++++++++
 sys/netinet/tcp_usrreq.c         |   14 +++++
 sys/netinet/tcp_var.h            |   25 ++++++++-
 sys/netinet6/ipsec.h             |    2 +
 sys/netproto/key/key.c           |   17 ++++++
 usr.sbin/setkey/parse.y          |   14 ++++-
 usr.sbin/setkey/setkey.8         |    7 +++
 usr.sbin/setkey/token.l          |    2 +
 21 files changed, 352 insertions(+), 6 deletions(-)

diff --git a/lib/libipsec/ipsec_dump_policy.c b/lib/libipsec/ipsec_dump_policy.c
index 0d1460c..05a7b44 100644
--- a/lib/libipsec/ipsec_dump_policy.c
+++ b/lib/libipsec/ipsec_dump_policy.c
@@ -191,6 +191,9 @@ ipsec_dump_ipsecrequest(char *buf, size_t len,
 	case IPPROTO_IPCOMP:
 		proto = "ipcomp";
 		break;
+	case IPPROTO_TCP:
+		proto = "tcp";
+		break;
 	default:
 		__ipsec_errcode = EIPSEC_INVAL_PROTO;
 		return NULL;
diff --git a/lib/libipsec/pfkey.c b/lib/libipsec/pfkey.c
index 75b1cb1..ee881bc 100644
--- a/lib/libipsec/pfkey.c
+++ b/lib/libipsec/pfkey.c
@@ -79,12 +79,13 @@ static caddr_t pfkey_setsadbxsa2 (caddr_t, caddr_t, u_int32_t, u_int32_t);
 /*
  * make and search supported algorithm structure.
  */
-static struct sadb_supported *ipsec_supported[] = { NULL, NULL, NULL, };
+static struct sadb_supported *ipsec_supported[] = { NULL, NULL, NULL, NULL};
 
 static int supported_map[] = {
 	SADB_SATYPE_AH,
 	SADB_SATYPE_ESP,
 	SADB_X_SATYPE_IPCOMP,
+	SADB_X_SATYPE_TCPSIGNATURE,
 };
 
 static int
@@ -1166,6 +1167,16 @@ pfkey_send_x1(so, type, satype, mode, src, dst, spi, reqid, wsize,
 			return -1;
 		}
 		break;
+	case SADB_X_SATYPE_TCPSIGNATURE:
+		if (e_type != SADB_EALG_NONE) {
+			__ipsec_errcode = EIPSEC_INVAL_ALGS;
+			return -1;
+		}
+		if (a_type != SADB_X_AALG_TCP_MD5) {
+			__ipsec_errcode = EIPSEC_INVAL_ALGS;
+			return -1;
+		}
+		break;
 	default:
 		__ipsec_errcode = EIPSEC_INVAL_SATYPE;
 		return -1;
@@ -1376,6 +1387,7 @@ pfkey_send_x3(so, type, satype)
 		case SADB_SATYPE_AH:
 		case SADB_SATYPE_ESP:
 		case SADB_X_SATYPE_IPCOMP:
+		case SADB_X_SATYPE_TCPSIGNATURE:
 			break;
 		default:
 			__ipsec_errcode = EIPSEC_INVAL_SATYPE;
@@ -1835,6 +1847,7 @@ pfkey_check(mhp)
 	case SADB_SATYPE_ESP:
 	case SADB_SATYPE_AH:
 	case SADB_X_SATYPE_IPCOMP:
+	case SADB_X_SATYPE_TCPSIGNATURE:
 		switch (msg->sadb_msg_type) {
 		case SADB_X_SPDADD:
 		case SADB_X_SPDDELETE:
diff --git a/lib/libipsec/pfkey_dump.c b/lib/libipsec/pfkey_dump.c
index ba4d2bb..2b4901c 100644
--- a/lib/libipsec/pfkey_dump.c
+++ b/lib/libipsec/pfkey_dump.c
@@ -124,6 +124,8 @@ static char *str_satype[] = {
 	"ripv2",
 	"mip",
 	"ipcomp",
+	"policy",
+	"tcp",
 };
 
 static char *str_mode[] = {
@@ -162,6 +164,7 @@ static struct val2str str_alg_auth[] = {
 	{ SADB_X_AALG_MD5, "md5", },
 	{ SADB_X_AALG_SHA, "sha", },
 	{ SADB_X_AALG_NULL, "null", },
+	{ SADB_X_AALG_TCP_MD5, "tcp-md5", },
 #ifdef SADB_X_AALG_SHA2_256
 	{ SADB_X_AALG_SHA2_256, "hmac-sha2-256", },
 #endif
diff --git a/lib/libipsec/policy_token.l b/lib/libipsec/policy_token.l
index f51607d..77ff00b 100644
--- a/lib/libipsec/policy_token.l
+++ b/lib/libipsec/policy_token.l
@@ -104,6 +104,7 @@ entrust		{ yylval.num = IPSEC_POLICY_ENTRUST; return(ACTION); }
 esp		{ yylval.num = IPPROTO_ESP; return(PROTOCOL); }
 ah		{ yylval.num = IPPROTO_AH; return(PROTOCOL); }
 ipcomp		{ yylval.num = IPPROTO_IPCOMP; return(PROTOCOL); }
+tcp		{ yylval.num = IPPROTO_TCP; return(PROTOCOL); }
 
 transport	{ yylval.num = IPSEC_MODE_TRANSPORT; return(MODE); }
 tunnel		{ yylval.num = IPSEC_MODE_TUNNEL; return(MODE); }
diff --git a/share/man/man4/tcp.4 b/share/man/man4/tcp.4
index 1c14006..f9b5862 100644
--- a/share/man/man4/tcp.4
+++ b/share/man/man4/tcp.4
@@ -156,6 +156,31 @@ option is set to a non-zero value,
 .Tn TCP
 will delay sending any data at all until either the socket is closed,
 or the internal send buffer is filled.
+.It Dv TCP_SIGNATURE_ENABLE
+This option enables the use of MD5 digests (also known as TCP-MD5)
+on writes to the specified socket.
+In the current release, only outgoing traffic is digested;
+digests on incoming traffic are not verified.
+The current default behavior for the system is to respond to a system
+advertising this option with TCP-MD5; this may change.
+.Pp
+One common use for this in a FreeBSD router deployment is to enable
+based routers to interwork with Cisco equipment at peering points.
+Support for this feature conforms to RFC 2385.
+Only IPv4 (AF_INET) sessions are supported.
+.Pp
+In order for this option to function correctly, it is necessary for the
+administrator to add a tcp-md5 key entry to the system's security
+associations database (SADB) using the
+.Xr setkey 8
+utility.
+This entry must have an SPI of 0x1000 and can therefore only be specified
+on a per-host basis at this time.
+.Pp
+If an SADB entry cannot be found for the destination, the outgoing traffic
+will have an invalid digest option prepended, and the following error message
+will be visible on the system console:
+.Em "tcpsignature_compute: SADB lookup failed for %d.%d.%d.%d" .
 .El
 .Pp
 The option level for the
@@ -381,6 +406,7 @@ address.
 .Xr blackhole 4 ,
 .Xr inet 4 ,
 .Xr intro 4 ,
+.Xr setkey 8,
 .Xr ip 4
 .Rs
 .%A V. Jacobson
@@ -389,6 +415,11 @@ address.
 .%T "TCP Extensions for High Performance"
 .%O RFC 1323
 .Re
+.Rs
+.%A "A. Heffernan"
+.%T "Protection of BGP Sessions via the TCP MD5 Signature Option"
+.%O "RFC 2385"
+.Re
 .Sh HISTORY
 The
 .Nm
diff --git a/sys/conf/options b/sys/conf/options
index 46644b6..3424890 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -316,6 +316,7 @@ PPP_DEFLATE		opt_ppp.h
 PPP_FILTER		opt_ppp.h
 SLIP_IFF_OPTS		opt_slip.h
 TCPDEBUG
+TCP_SIGNATURE		opt_inet.h
 TCP_DROP_SYNFIN		opt_tcp_input.h
 
 XBONEHACK
diff --git a/sys/config/LINT b/sys/config/LINT
index 2149f0f..0675084 100644
--- a/sys/config/LINT
+++ b/sys/config/LINT
@@ -653,6 +653,14 @@ options         MBUF_STRESS_TEST
 options                ACCEPT_FILTER_DATA
 options                ACCEPT_FILTER_HTTP
 
+# TCP_SIGNATURE adds support for RFC 2385 (TCP-MD5) digests. These are
+# carried in TCP option 19. This option is commonly used to protect
+# TCP sessions (e.g. BGP) where IPSEC is not available nor desirable.
+# This is enabled on a per-socket basis using the TCP_MD5SIG socket option.
+# This requires the use of 'device crypto', 'options IPSEC'
+# or 'device cryptodev'.
+options   TCP_SIGNATURE   #include support for RFC 2385
+
 #
 # TCP_DROP_SYNFIN adds support for ignoring TCP packets with SYN+FIN. This
 # prevents nmap et al. from identifying the TCP/IP stack, but breaks support
diff --git a/sys/netinet/ip.h b/sys/netinet/ip.h
index 06b8686..e6d5574 100644
--- a/sys/netinet/ip.h
+++ b/sys/netinet/ip.h
@@ -211,4 +211,16 @@ struct	ip_timestamp {
 
 #define	IP_MSS		576		/* default maximum segment size */
 
+/*
+ * This is the real IPv4 pseudo header, used for computing the TCP and UDP
+ * checksums. For the Internet checksum, struct ipovly can be used instead.
+ * For stronger checksums, the real thing must be used.
+ */
+struct ippseudo {
+	struct	in_addr	ippseudo_src;	/* source internet address */
+	struct	in_addr	ippseudo_dst;	/* destination internet address */
+	u_int8_t	ippseudo_pad;	/* pad, must be zero */
+	u_int8_t	ippseudo_p;	/* protocol */
+	u_int16_t	ippseudo_len;	/* protocol length */
+} __packed;
 #endif
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index 29cbae6..d59e840 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -570,6 +570,7 @@ sendit:
 
 	case IPSEC_POLICY_BYPASS:
 	case IPSEC_POLICY_NONE:
+	case IPSEC_POLICY_TCP:
 		/* no need to do IPsec. */
 		goto skip_ipsec;
 
diff --git a/sys/netinet/tcp.h b/sys/netinet/tcp.h
index 7c48b70..7caa576 100644
--- a/sys/netinet/tcp.h
+++ b/sys/netinet/tcp.h
@@ -110,6 +110,8 @@ struct tcphdr {
 #define	TCPOPT_CC		11		/* CC options: RFC-1644 */
 #define TCPOPT_CCNEW		12
 #define TCPOPT_CCECHO		13
+#define TCPOPT_SIGNATURE		19      /* Keyed MD5: RFC 2385 */
+#define TCPOLEN_SIGNATURE		18
 
 /*
  * Default maximum segment size for TCP.
@@ -160,5 +162,6 @@ struct tcphdr {
 #define	TCP_MAXSEG	0x02	/* set maximum segment size */
 #define TCP_NOPUSH	0x04	/* don't push last block of write */
 #define TCP_NOOPT	0x08	/* don't use TCP options */
+#define TCP_SIGNATURE_ENABLE    0x10    /* use MD5 digests (RFC2385) */
 
 #endif
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index 9bcc360..c978d86 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -69,6 +69,7 @@
  */
 
 #include "opt_ipfw.h"		/* for ipfw_fwd		*/
+#include "opt_inet.h"
 #include "opt_inet6.h"
 #include "opt_ipsec.h"
 #include "opt_tcpdebug.h"
@@ -520,7 +521,8 @@ tcp_input(struct mbuf *m, ...)
 	struct inpcb *inp = NULL;
 	u_char *optp = NULL;
 	int optlen = 0;
-	int len, tlen, off;
+	int tlen, off;
+	int len = 0;
 	int drop_hdrlen;
 	struct tcpcb *tp = NULL;
 	int thflags;
@@ -2701,6 +2703,19 @@ tcp_dooptions(struct tcpopt *to, u_char *cp, int cnt, boolean_t is_syn)
 				r->rblk_end = ntohl(r->rblk_end);
 			}
 			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:
+			if (optlen != TCPOLEN_SIGNATURE)
+				continue;
+			to->to_flags |= (TOF_SIGNATURE | TOF_SIGLEN);
+			break;
+#endif /* TCP_SIGNATURE */
 		default:
 			continue;
 		}
diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
index 97c5ed7..dd8292b 100644
--- a/sys/netinet/tcp_output.c
+++ b/sys/netinet/tcp_output.c
@@ -68,6 +68,7 @@
  * $DragonFly: src/sys/netinet/tcp_output.c,v 1.34 2007/04/22 01:13:14 dillon Exp $
  */
 
+#include "opt_inet.h"
 #include "opt_inet6.h"
 #include "opt_ipsec.h"
 #include "opt_tcpdebug.h"
@@ -151,6 +152,7 @@ tcp_output(struct tcpcb *tp)
 	long len, recvwin, sendwin;
 	int nsacked = 0;
 	int off, flags, error;
+	int sigoff = 0;
 	struct mbuf *m;
 	struct ip *ip = NULL;
 	struct ipovly *ipov = NULL;
@@ -599,6 +601,29 @@ send:
 	     tp->reportblk.rblk_start != tp->reportblk.rblk_end))
 		tcp_sack_fill_report(tp, opt, &optlen);
 
+#ifdef TCP_SIGNATURE
+	if (!isipv6)
+		if (tp->t_flags & TF_SIGNATURE) {
+			int i;
+			u_char *bp;
+			/*
+			 * Initialize TCP-MD5 option (RFC2385)
+			 */
+			bp = (u_char *)opt + optlen;
+			*bp++ = TCPOPT_SIGNATURE;
+			*bp++ = TCPOLEN_SIGNATURE;
+			sigoff = optlen + 2;
+			for (i = 0; i < TCP_SIGLEN; i++)
+				*bp++ = 0;
+			optlen += TCPOLEN_SIGNATURE;
+			/*
+			 * Terminate options list and maintain 32-bit alignment.
+			 */
+			*bp++ = TCPOPT_NOP;
+			*bp++ = TCPOPT_EOL;
+			optlen += 2;
+		}
+#endif /* TCP_SIGNATURE */
 	KASSERT(optlen <= TCP_MAXOLEN, ("too many TCP options"));
 	hdrlen += optlen;
 
@@ -818,6 +843,13 @@ send:
 		tp->snd_up = tp->snd_una;		/* drag it along */
 	}
 
+#ifdef TCP_SIGNATURE
+	if (!isipv6)
+		if (tp->t_flags & TF_SIGNATURE)
+			tcpsignature_compute(m, sizeof(struct ip), len, optlen,
+					(u_char *)(th + 1) + sigoff, IPSEC_DIR_OUTBOUND);
+#endif /* TCP_SIGNATURE */
+
 	/*
 	 * Put TCP length in extended header, and then
 	 * checksum extended header and data.
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index ab3327e..fafda1e 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -69,6 +69,7 @@
  */
 
 #include "opt_compat.h"
+#include "opt_inet.h"
 #include "opt_inet6.h"
 #include "opt_ipsec.h"
 #include "opt_tcpdebug.h"
@@ -126,6 +127,7 @@
 
 #ifdef IPSEC
 #include <netinet6/ipsec.h>
+#include <netproto/key/key.h>
 #ifdef INET6
 #include <netinet6/ipsec6.h>
 #endif
@@ -1979,3 +1981,109 @@ tcp_xmit_bandwidth_limit(struct tcpcb *tp, tcp_seq ack_seq)
 		bwnd = tp->t_maxseg * 2;
 	tp->snd_bwnd = bwnd;
 }
+
+#ifdef TCP_SIGNATURE
+/*
+ * Compute TCP-MD5 hash of a TCPv4 segment. (RFC2385)
+ *
+ * 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.
+ *
+ * This function is for IPv4 use only. Calling this function with an
+ * IPv6 packet in the mbuf chain will yield undefined results.
+ *
+ * Return 0 if successful, otherwise return -1.
+ *
+ * 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
+ * right now. Another branch of this code exists which uses the SPD to
+ * specify per-application flows but it is unstable.
+ */
+int
+tcpsignature_compute(
+	struct mbuf *m,		/* mbuf chain */
+	int off0,		/* offset to TCP header */
+	int len,		/* length of TCP data */
+	int optlen,		/* length of TCP options */
+	u_char *buf,		/* storage for MD5 digest */
+	u_int direction)	/* direction of flow */
+{
+	struct ippseudo ippseudo;
+	MD5_CTX ctx;
+	int doff;
+	struct ip *ip;
+	struct ipovly *ipovly;
+	struct secasvar *sav;
+	struct tcphdr *th;
+	u_short savecsum;
+
+	KASSERT(m != NULL, ("passed NULL mbuf. Game over."));
+	KASSERT(buf != NULL, ("passed NULL storage pointer for MD5 signature"));
+	/*
+	 * Extract the destination from the IP header in the mbuf.
+	 */
+	ip = mtod(m, struct ip *);
+	/*
+	 * Look up an SADB entry which matches the address found in
+	 * the segment.
+	 */
+	sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
+			IPPROTO_TCP, htonl(TCP_SIG_SPI));
+	if (sav == NULL) {
+		kprintf("%s: SADB lookup failed\n", __func__);
+		return (EINVAL);
+	}
+	MD5Init(&ctx);
+
+	ipovly = (struct ipovly *)ip;
+	th = (struct tcphdr *)((u_char *)ip + off0);
+	doff = off0 + sizeof(struct tcphdr) + optlen;
+	/*
+	 * Step 1: Update MD5 hash with IP 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.
+	 */
+	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));
+	/*
+	 * 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, tcpsignature_apply, &ctx);
+	/*
+	 * Step 4: Update MD5 hash with shared secret.
+	 */
+	MD5Update(&ctx, _KEYBUF(sav->key_auth), _KEYLEN(sav->key_auth));
+	MD5Final(buf, &ctx);
+	key_sa_recordxfer(sav, m);
+	key_freesav(sav);
+	return (0);
+}
+
+int
+tcpsignature_apply(void *fstate, void *data, unsigned int len)
+{
+
+	MD5Update((MD5_CTX *)fstate, (unsigned char *)data, len);
+	return (0);
+}
+#endif /* TCP_SIGNATURE */
diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c
index 9aa6eb5..b8f1825 100644
--- a/sys/netinet/tcp_syncache.c
+++ b/sys/netinet/tcp_syncache.c
@@ -72,6 +72,7 @@
  * $DragonFly: src/sys/netinet/tcp_syncache.c,v 1.35 2008/11/22 11:03:35 sephe Exp $
  */
 
+#include "opt_inet.h"
 #include "opt_inet6.h"
 #include "opt_ipsec.h"
 
@@ -848,6 +849,12 @@ syncache_socket(struct syncache *sc, struct socket *lso, struct mbuf *m)
 	if (sc->sc_flags & SCF_SACK_PERMITTED)
 		tp->t_flags |= TF_SACK_PERMITTED;
 
+#ifdef TCP_SIGNATURE
+	if (sc->sc_flags & SCF_SIGNATURE)
+		tp->t_flags |= TF_SIGNATURE;
+#endif /* TCP_SIGNATURE */
+
+
 	tcp_mss(tp, sc->sc_peer_mss);
 
 	/*
@@ -1081,6 +1088,17 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
 		sc->sc_flags |= SCF_SACK_PERMITTED;
 	if (tp->t_flags & TF_NOOPT)
 		sc->sc_flags = SCF_NOOPT;
+#ifdef TCP_SIGNATURE
+	/*
+	 * If listening socket requested TCP digests, and received SYN
+	 * contains the option, flag this in the syncache so that
+	 * syncache_respond() will do the right thing with the SYN+ACK.
+	 * XXX Currently we always record the option by default and will
+	 * attempt to use it in syncache_respond().
+	 */
+	if (to->to_flags & TOF_SIGNATURE)
+		sc->sc_flags = SCF_SIGNATURE;
+#endif /* TCP_SIGNATURE */
 
 	if (syncache_respond(sc, m) == 0) {
 		syncache_insert(sc, sch);
@@ -1137,6 +1155,10 @@ syncache_respond(struct syncache *sc, struct mbuf *m)
 		    ((sc->sc_flags & SCF_TIMESTAMP) ? TCPOLEN_TSTAMP_APPA : 0) +
 		    ((sc->sc_flags & SCF_SACK_PERMITTED) ?
 			TCPOLEN_SACK_PERMITTED_ALIGNED : 0);
+#ifdef TCP_SIGNATURE
+				optlen += ((sc->sc_flags & SCF_SIGNATURE) ?
+						(TCPOLEN_SIGNATURE + 2) : 0);
+#endif /* TCP_SIGNATURE */
 	}
 	tlen = hlen + sizeof(struct tcphdr) + optlen;
 
@@ -1237,6 +1259,26 @@ syncache_respond(struct syncache *sc, struct mbuf *m)
 		optp += TCPOLEN_TSTAMP_APPA;
 	}
 
+#ifdef TCP_SIGNATURE
+	/*
+	 * Handle TCP-MD5 passive opener response.
+	 */
+	if (sc->sc_flags & SCF_SIGNATURE) {
+		u_int8_t *bp = optp;
+		int i;
+		
+		*bp++ = TCPOPT_SIGNATURE;
+		*bp++ = TCPOLEN_SIGNATURE;
+		for (i = 0; i < TCP_SIGLEN; i++)
+			*bp++ = 0;
+		tcpsignature_compute(m, sizeof(struct ip), 0, optlen,
+				optp + 2, IPSEC_DIR_OUTBOUND);
+		*bp++ = TCPOPT_NOP;
+		*bp++ = TCPOPT_EOL;
+		optp += TCPOLEN_SIGNATURE + 2;
+}
+#endif /* TCP_SIGNATURE */
+
 	if (sc->sc_flags & SCF_SACK_PERMITTED) {
 		*((u_int32_t *)optp) = htonl(TCPOPT_SACK_PERMITTED_ALIGNED);
 		optp += TCPOLEN_SACK_PERMITTED_ALIGNED;
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index 785d25a..b3a456f 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -69,6 +69,7 @@
  */
 
 #include "opt_ipsec.h"
+#include "opt_inet.h"
 #include "opt_inet6.h"
 #include "opt_tcpdebug.h"
 
@@ -1252,6 +1253,14 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt)
 		if (error)
 			break;
 		switch (sopt->sopt_name) {
+#ifdef TCP_SIGNATURE
+		case TCP_SIGNATURE_ENABLE:
+			if (optval > 0)
+				tp->t_flags |= TF_SIGNATURE;
+			else
+				tp->t_flags &= ~TF_SIGNATURE;
+			break;
+#endif /* TCP_SIGNATURE */
 		case TCP_NODELAY:
 		case TCP_NOOPT:
 			switch (sopt->sopt_name) {
@@ -1309,6 +1318,11 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt)
 
 	case SOPT_GET:
 		switch (sopt->sopt_name) {
+#ifdef TCP_SIGNATURE
+		case TCP_SIGNATURE_ENABLE:
+			optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0;
+			break;
+#endif /* TCP_SIGNATURE */
 		case TCP_NODELAY:
 			optval = tp->t_flags & TF_NODELAY;
 			break;
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index 248394e..3549565 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -160,7 +160,7 @@ struct tcpcb {
 #define	TF_NEEDFIN	0x00000800	/* send FIN (implicit state) */
 #define	TF_NOPUSH	0x00001000	/* don't push */
 #define TF_SYNCACHE	0x00002000	/* syncache present */
-/* 0x00001000 - 0x00008000 were used for T/TCP */
+#define TF_SIGNATURE 0x00004000  /* require MD5 digests (RFC2385) */
 #define	TF_MORETOCOME	0x00010000	/* More data to be appended to sock */
 #define	TF_LQ_OVERFLOW	0x00020000	/* listen queue overflow */
 #define	TF_LASTIDLE	0x00040000	/* connection was previously idle */
@@ -274,6 +274,21 @@ struct tcpcb {
 #define	ENTER_FASTRECOVERY(tp)	tp->t_flags |= TF_FASTRECOVERY
 #define	EXIT_FASTRECOVERY(tp)	tp->t_flags &= ~TF_FASTRECOVERY
 
+#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 */
+
 /*
  * TCP statistics.
  */
@@ -402,6 +417,8 @@ struct tcpopt {
 #define	TOF_SCALE		0x0020
 #define	TOF_SACK_PERMITTED	0x0040
 #define	TOF_SACK		0x0080
+#define TOF_SIGNATURE   0x0100          /* signature option present */
+#define TOF_SIGLEN      0x0200          /* sigature length valid (RFC2385) */
 	u_int32_t	to_tsval;
 	u_int32_t	to_tsecr;
 	u_int16_t	to_mss;
@@ -432,6 +449,7 @@ struct syncache {
 #define SCF_TIMESTAMP		0x04		/* negotiated timestamps */
 #define SCF_UNREACH		0x10		/* icmp unreachable received */
 #define	SCF_SACK_PERMITTED	0x20		/* saw SACK permitted option */
+#define SCF_SIGNATURE   0x40    /* send MD5 digests */
 #define SCF_MARKER		0x80		/* not a real entry */
 	TAILQ_ENTRY(syncache) sc_hash;
 	TAILQ_ENTRY(syncache) sc_timerq;
@@ -627,6 +645,11 @@ void	 syncache_chkrst(struct in_conninfo *, struct tcphdr *);
 void	 syncache_badack(struct in_conninfo *);
 void	 syncache_destroy(struct tcpcb *tp);
 
+#ifdef TCP_SIGNATURE
+int tcpsignature_apply(void *fstate, void *data, unsigned int len);
+int tcpsignature_compute(struct mbuf *m, int off0, int len, int tcpoptlen,
+		u_char *buf, u_int direction);
+#endif /* TCP_SIGNATURE */
 
 extern	struct pr_usrreqs tcp_usrreqs;
 extern	u_long tcp_sendspace;
diff --git a/sys/netinet6/ipsec.h b/sys/netinet6/ipsec.h
index 248118c..7c2ed8e 100644
--- a/sys/netinet6/ipsec.h
+++ b/sys/netinet6/ipsec.h
@@ -140,6 +140,7 @@ struct secspacq {
 #define	IPSEC_MODE_ANY		0	/* i.e. wildcard. */
 #define	IPSEC_MODE_TRANSPORT	1
 #define	IPSEC_MODE_TUNNEL	2
+#define IPSEC_MODE_TCPMD5 3  /* TCP MD5 mode */
 
 /*
  * Direction of security policy.
@@ -163,6 +164,7 @@ struct secspacq {
 #define IPSEC_POLICY_IPSEC	2	/* do IPsec */
 #define IPSEC_POLICY_ENTRUST	3	/* consulting SPD if present. */
 #define IPSEC_POLICY_BYPASS	4	/* only for privileged socket. */
+#define IPSEC_POLICY_TCP 5 /* TCP MD5 policy */
 
 /* Security protocol level */
 #define	IPSEC_LEVEL_DEFAULT	0	/* reference to system default */
diff --git a/sys/netproto/key/key.c b/sys/netproto/key/key.c
index c56e3be..a00d8df 100644
--- a/sys/netproto/key/key.c
+++ b/sys/netproto/key/key.c
@@ -2945,6 +2945,7 @@ key_setsaval(struct secasvar *sav, struct mbuf *m,
 		switch (mhp->msg->sadb_msg_satype) {
 		case SADB_SATYPE_AH:
 		case SADB_SATYPE_ESP:
+		case SADB_X_SATYPE_TCPSIGNATURE:
 			if (len == PFKEY_ALIGN8(sizeof(struct sadb_key)) &&
 			    sav->alg_auth != SADB_X_AALG_NULL)
 				error = EINVAL;
@@ -3000,6 +3001,7 @@ key_setsaval(struct secasvar *sav, struct mbuf *m,
 			sav->key_enc = NULL;	/*just in case*/
 			break;
 		case SADB_SATYPE_AH:
+		case SADB_X_SATYPE_TCPSIGNATURE:
 		default:
 			error = EINVAL;
 			break;
@@ -3034,6 +3036,7 @@ key_setsaval(struct secasvar *sav, struct mbuf *m,
 		break;
 	case SADB_SATYPE_AH:
 	case SADB_X_SATYPE_IPCOMP:
+	case SADB_X_SATYPE_TCPSIGNATURE:
 		break;
 	default:
 		ipseclog((LOG_DEBUG, "key_setsaval: invalid SA type.\n"));
@@ -3213,6 +3216,15 @@ key_mature(struct secasvar *sav)
 		checkmask = 4;
 		mustmask = 4;
 		break;
+	case IPPROTO_TCP:
+		if (sav->alg_auth != SADB_X_AALG_TCP_MD5) {
+			ipseclog((LOG_DEBUG, "key_mature: "
+				"protocol and algorithm mismated.\n"));
+			return(EINVAL);
+		}
+		checkmask = 0;
+		mustmask = 0;
+		break;
 	default:
 		ipseclog((LOG_DEBUG, "key_mature: Invalid satype.\n"));
 		return EPROTONOSUPPORT;
@@ -4407,6 +4419,8 @@ key_satype2proto(u_int8_t satype)
 		return IPPROTO_ESP;
 	case SADB_X_SATYPE_IPCOMP:
 		return IPPROTO_IPCOMP;
+	case SADB_X_SATYPE_TCPSIGNATURE:
+		return IPPROTO_TCP;
 		break;
 	default:
 		return 0;
@@ -4429,6 +4443,8 @@ key_proto2satype(u_int16_t proto)
 		return SADB_SATYPE_ESP;
 	case IPPROTO_IPCOMP:
 		return SADB_X_SATYPE_IPCOMP;
+	case IPPROTO_TCP:
+		return SADB_X_SATYPE_TCPSIGNATURE;
 		break;
 	default:
 		return 0;
@@ -6757,6 +6773,7 @@ key_parse(struct mbuf *m, struct socket *so)
 	case SADB_SATYPE_AH:
 	case SADB_SATYPE_ESP:
 	case SADB_X_SATYPE_IPCOMP:
+	case SADB_X_SATYPE_TCPSIGNATURE:
 		switch (msg->sadb_msg_type) {
 		case SADB_X_SPDADD:
 		case SADB_X_SPDDELETE:
diff --git a/usr.sbin/setkey/parse.y b/usr.sbin/setkey/parse.y
index f2c1149..5609f37 100644
--- a/usr.sbin/setkey/parse.y
+++ b/usr.sbin/setkey/parse.y
@@ -102,7 +102,7 @@ extern void yyerror(const char *);
 %token EOT
 %token ADD GET DELETE DELETEALL FLUSH DUMP
 %token ADDRESS PREFIX PORT PORTANY
-%token UP_PROTO PR_ESP PR_AH PR_IPCOMP
+%token UP_PROTO PR_ESP PR_AH PR_IPCOMP PR_TCP
 %token F_PROTOCOL F_AUTH F_ENC F_REPLAY F_COMP F_RAWCPI
 %token F_MODE MODE F_REQID
 %token F_EXT EXTENSION NOCYCLICSEQ
@@ -114,7 +114,7 @@ extern void yyerror(const char *);
 %token F_POLICY PL_REQUESTS
 
 %type <num> PORT PREFIX EXTENSION MODE
-%type <num> UP_PROTO PR_ESP PR_AH PR_IPCOMP
+%type <num> UP_PROTO PR_ESP PR_AH PR_IPCOMP PR_TCP
 %type <num> ALG_AUTH ALG_ENC ALG_ENC_DESDERIV ALG_ENC_DES32IV ALG_COMP
 %type <num> DECSTRING
 %type <val> ADDRESS PL_REQUESTS
@@ -230,6 +230,10 @@ protocol_spec
 		{
 			p_satype = SADB_X_SATYPE_IPCOMP;
 		}
+	|	PR_TCP
+		{
+			p_satype = SADB_X_SATYPE_TCPSIGNATURE;
+		}
 	;
 	
 spi
@@ -349,7 +353,11 @@ auth_key
 			p_key_auth_len = $1.len;
 			p_key_auth = pp_key;
 
-			if (ipsec_check_keylen(SADB_EXT_SUPPORTED_AUTH,
+			if (p_alg_auth == SADB_X_AALG_TCP_MD5) {
+				if ((p_key_auth_len < 1) || (p_key_auth_len >
+					80))
+					return -1;
+			} else if (ipsec_check_keylen(SADB_EXT_SUPPORTED_AUTH,
 					p_alg_auth,
 					PFKEY_UNUNIT64(p_key_auth_len)) < 0) {
 				yyerror(ipsec_strerror());
diff --git a/usr.sbin/setkey/setkey.8 b/usr.sbin/setkey/setkey.8
index 84d575d..b72db79 100644
--- a/usr.sbin/setkey/setkey.8
+++ b/usr.sbin/setkey/setkey.8
@@ -220,6 +220,8 @@ AH based on rfc2402
 AH based on rfc1826
 .It Li ipcomp
 IPCOMP
+.It Li tcp
+TCP-MD5 based on rfc2385
 .El
 .\"
 .Pp
@@ -230,6 +232,8 @@ You cannot use the set of SPI values in the range 0 through 255.
 (with
 .Li 0x
 attached).
+TCP-MD5 associations must use 0x1000 and therefore only have per-host
+granularity at this time.
 .\"
 .Pp
 .It Ar extensions
@@ -547,6 +551,7 @@ hmac-sha2-384	384		ah: 96bit ICV (no document)
 		384		ah-old: 128bit ICV (no document)
 hmac-sha2-512	512		ah: 96bit ICV (no document)
 		512		ah-old: 128bit ICV (no document)
+tcp-md5		8 to 640	tcp: rfc2385
 .Ed
 .Pp
 Followings are the list of encryption algorithms that can be used as
@@ -602,6 +607,8 @@ dump esp ;
 spdadd	10.0.11.41/32[21] 10.0.11.33/32[any] any
 		-P out ipsec esp/tunnel/192.168.0.1-192.168.1.2/require ;
 
+add 10.1.10.34 10.1.10.36 tcp 0x1000 -A tcp-md5 "TCP-MD5 BGP secret" ;
+
 .Ed
 .\"
 .Sh DIAGNOSTICS
diff --git a/usr.sbin/setkey/token.l b/usr.sbin/setkey/token.l
index a0f7865..afb8300 100644
--- a/usr.sbin/setkey/token.l
+++ b/usr.sbin/setkey/token.l
@@ -163,6 +163,7 @@ esp		{ PREPROC; yylval.num = 0; return(PR_ESP); }
 ah-old		{ PREPROC; yylval.num = 1; return(PR_AH); }
 esp-old		{ PREPROC; yylval.num = 1; return(PR_ESP); }
 ipcomp		{ PREPROC; yylval.num = 0; return(PR_IPCOMP); }
+tcp		{ PREPROC; yylval.num = 0; return(PR_TCP); }
 
 	/* authentication alogorithm */
 {hyphen}A	{ PREPROC; return(F_AUTH); }
@@ -173,6 +174,7 @@ keyed-sha1	{ PREPROC; yylval.num = SADB_X_AALG_SHA; return(ALG_AUTH); }
 hmac-sha2-256	{ PREPROC; yylval.num = SADB_X_AALG_SHA2_256; return(ALG_AUTH); }
 hmac-sha2-384	{ PREPROC; yylval.num = SADB_X_AALG_SHA2_384; return(ALG_AUTH); }
 hmac-sha2-512	{ PREPROC; yylval.num = SADB_X_AALG_SHA2_512; return(ALG_AUTH); }
+tcp-md5	{ PREPROC; yylval.num = SADB_X_AALG_TCP_MD5; return(ALG_AUTH); }
 null		{ PREPROC; yylval.num = SADB_X_AALG_NULL; return(ALG_AUTH); }
 
 	/* encryption alogorithm */
-- 
1.6.6.2


--BwCQnh7xodEAoBMC--



[Date Prev][Date Next]  [Thread Prev][Thread Next]  [Date Index][Thread Index]