aboutsummaryrefslogtreecommitdiff
path: root/sys/netinet
diff options
context:
space:
mode:
authorBruce M Simpson <bms@FreeBSD.org>2004-02-11 04:26:04 +0000
committerBruce M Simpson <bms@FreeBSD.org>2004-02-11 04:26:04 +0000
commit1cfd4b5326854e594bfcd5f31dec0f2b52ccaa71 (patch)
tree7a14054c69fb5681c86f54e9b66a9128c1e16003 /sys/netinet
parent33febf93d6e69967393353136a9e3a0829e1bd10 (diff)
downloadsrc-1cfd4b5326854e594bfcd5f31dec0f2b52ccaa71.tar.gz
src-1cfd4b5326854e594bfcd5f31dec0f2b52ccaa71.zip
Initial import of RFC 2385 (TCP-MD5) digest support.
This is the first of two commits; bringing in the kernel support first. This can be enabled by compiling a kernel with options TCP_SIGNATURE and FAST_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. Several parts of the TCP and IP headers, including the segment payload, are digested with MD5, including a shared secret. The PF_KEY interface is used to manage the secrets using security associations in the SADB. There is a limitation here in that as there is no way to map a TCP flow per-port back to an SPI without polluting tcpcb or using the SPD; the code to do the latter is unstable at this time. Therefore this code only supports per-host keying granularity. Whilst FAST_IPSEC is mutually exclusive with KAME IPSEC (and thus IPv6), TCP_SIGNATURE applies only to IPv4. For the vast majority of prospective users of this feature, this will not pose any problem. This implementation is output-only; that is, the option is honoured when responding to a host initiating a TCP session, but no effort is made [yet] to authenticate inbound traffic. This is, however, sufficient to interwork with Cisco equipment. Tested with a Cisco 2501 running IOS 12.0(27), and Quagga 0.96.4 with local patches. Patches for tcpdump to validate TCP-MD5 sessions are also available from me upon request. Sponsored by: sentex.net
Notes
Notes: svn path=/head/; revision=125680
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/ip.h12
-rw-r--r--sys/netinet/ip_output.c1
-rw-r--r--sys/netinet/tcp.h3
-rw-r--r--sys/netinet/tcp_input.c17
-rw-r--r--sys/netinet/tcp_output.c37
-rw-r--r--sys/netinet/tcp_reass.c17
-rw-r--r--sys/netinet/tcp_subr.c114
-rw-r--r--sys/netinet/tcp_syncache.c40
-rw-r--r--sys/netinet/tcp_timewait.c114
-rw-r--r--sys/netinet/tcp_usrreq.c19
-rw-r--r--sys/netinet/tcp_var.h25
11 files changed, 397 insertions, 2 deletions
diff --git a/sys/netinet/ip.h b/sys/netinet/ip.h
index 025ad085d146..aca0432dccf1 100644
--- a/sys/netinet/ip.h
+++ b/sys/netinet/ip.h
@@ -193,4 +193,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 a872c008c3b5..e567936e7503 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -499,6 +499,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 d958aafbae38..1df59b6448ce 100644
--- a/sys/netinet/tcp.h
+++ b/sys/netinet/tcp.h
@@ -102,6 +102,8 @@ struct tcphdr {
#define TCPOLEN_CC_APPA (TCPOLEN_CC+2)
#define TCPOPT_CC_HDR(ccopt) \
(TCPOPT_NOP<<24|TCPOPT_NOP<<16|(ccopt)<<8|TCPOLEN_CC)
+#define TCPOPT_SIGNATURE 19 /* Keyed MD5: RFC 2385 */
+#define TCPOLEN_SIGNATURE 18
/*
* Default maximum segment size for TCP.
@@ -156,6 +158,7 @@ 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
#endif /* !_NETINET_TCP_H_ */
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index 2b7f99af7e8b..6562368832be 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -35,6 +35,7 @@
*/
#include "opt_ipfw.h" /* for ipfw_fwd */
+#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include "opt_mac.h"
@@ -349,7 +350,8 @@ tcp_input(m, off0)
register struct inpcb *inp = NULL;
u_char *optp = NULL;
int optlen = 0;
- int len, tlen, off;
+ int len = 0;
+ int tlen, off;
int drop_hdrlen;
register struct tcpcb *tp = 0;
register int thflags;
@@ -2524,6 +2526,19 @@ tcp_dooptions(to, cp, cnt, is_syn)
(char *)&to->to_ccecho, sizeof(to->to_ccecho));
to->to_ccecho = ntohl(to->to_ccecho);
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 f30d6c31f0af..c868033e39d7 100644
--- a/sys/netinet/tcp_output.c
+++ b/sys/netinet/tcp_output.c
@@ -34,6 +34,7 @@
* $FreeBSD$
*/
+#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include "opt_mac.h"
@@ -115,6 +116,7 @@ tcp_output(struct tcpcb *tp)
struct socket *so = tp->t_inpcb->inp_socket;
long len, recwin, sendwin;
int off, flags, error;
+ int sigoff = 0;
struct mbuf *m;
struct ip *ip = NULL;
struct ipovly *ipov = NULL;
@@ -537,6 +539,32 @@ send:
}
}
+#ifdef TCP_SIGNATURE
+#ifdef INET6
+ if (!isipv6)
+#endif
+ 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 */
+
hdrlen += optlen;
#ifdef INET6
@@ -754,6 +782,15 @@ send:
*/
tp->snd_up = tp->snd_una; /* drag it along */
+#ifdef TCP_SIGNATURE
+#ifdef INET6
+ if (!isipv6)
+#endif
+ 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_reass.c b/sys/netinet/tcp_reass.c
index 2b7f99af7e8b..6562368832be 100644
--- a/sys/netinet/tcp_reass.c
+++ b/sys/netinet/tcp_reass.c
@@ -35,6 +35,7 @@
*/
#include "opt_ipfw.h" /* for ipfw_fwd */
+#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include "opt_mac.h"
@@ -349,7 +350,8 @@ tcp_input(m, off0)
register struct inpcb *inp = NULL;
u_char *optp = NULL;
int optlen = 0;
- int len, tlen, off;
+ int len = 0;
+ int tlen, off;
int drop_hdrlen;
register struct tcpcb *tp = 0;
register int thflags;
@@ -2524,6 +2526,19 @@ tcp_dooptions(to, cp, cnt, is_syn)
(char *)&to->to_ccecho, sizeof(to->to_ccecho));
to->to_ccecho = ntohl(to->to_ccecho);
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_subr.c b/sys/netinet/tcp_subr.c
index 4b56295f4bc4..689d0cdc48b5 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -35,6 +35,7 @@
*/
#include "opt_compat.h"
+#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include "opt_mac.h"
@@ -101,9 +102,11 @@
#ifdef FAST_IPSEC
#include <netipsec/ipsec.h>
+#include <netipsec/xform.h>
#ifdef INET6
#include <netipsec/ipsec6.h>
#endif
+#include <netipsec/key.h>
#define IPSEC
#endif /*FAST_IPSEC*/
@@ -1917,3 +1920,114 @@ tcp_xmit_bandwidth_limit(struct tcpcb *tp, tcp_seq ack_seq)
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 */
+{
+ union sockaddr_union dst;
+ 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 *);
+ bzero(&dst, sizeof(union sockaddr_union));
+ 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;
+ /*
+ * Look up an SADB entry which matches the address found in
+ * the segment.
+ */
+ sav = KEY_ALLOCSA(&dst, IPPROTO_TCP, htonl(TCP_SIG_SPI));
+ if (sav == NULL) {
+ printf("%s: SADB lookup failed for %s\n", __func__,
+ inet_ntoa(dst.sin.sin_addr));
+ 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 9343bf4f361b..14eb315b6f86 100644
--- a/sys/netinet/tcp_syncache.c
+++ b/sys/netinet/tcp_syncache.c
@@ -34,6 +34,7 @@
* $FreeBSD$
*/
+#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include "opt_mac.h"
@@ -695,6 +696,10 @@ syncache_socket(sc, lso, m)
tp->cc_send = sc->sc_cc_send;
tp->cc_recv = sc->sc_cc_recv;
}
+#ifdef TCP_SIGNATURE
+ if (sc->sc_flags & SCF_SIGNATURE)
+ tp->t_flags |= TF_SIGNATURE;
+#endif /* TCP_SIGNATURE */
/*
* Set up MSS and get cached values from tcp_hostcache.
@@ -970,6 +975,17 @@ syncache_add(inc, to, th, sop, m)
}
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 */
/*
* XXX
@@ -1083,6 +1099,10 @@ syncache_respond(sc, m)
((sc->sc_flags & SCF_WINSCALE) ? 4 : 0) +
((sc->sc_flags & SCF_TIMESTAMP) ? TCPOLEN_TSTAMP_APPA : 0) +
((sc->sc_flags & SCF_CC) ? TCPOLEN_CC_APPA * 2 : 0);
+#ifdef TCP_SIGNATURE
+ optlen += ((sc->sc_flags & SCF_SIGNATURE) ?
+ (TCPOLEN_SIGNATURE + 2) : 0);
+#endif /* TCP_SIGNATURE */
}
tlen = hlen + sizeof(struct tcphdr) + optlen;
@@ -1200,6 +1220,26 @@ syncache_respond(sc, m)
*lp = htonl(sc->sc_cc_recv);
optp += TCPOLEN_CC_APPA * 2;
}
+
+#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 */
}
#ifdef INET6
diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c
index 4b56295f4bc4..689d0cdc48b5 100644
--- a/sys/netinet/tcp_timewait.c
+++ b/sys/netinet/tcp_timewait.c
@@ -35,6 +35,7 @@
*/
#include "opt_compat.h"
+#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include "opt_mac.h"
@@ -101,9 +102,11 @@
#ifdef FAST_IPSEC
#include <netipsec/ipsec.h>
+#include <netipsec/xform.h>
#ifdef INET6
#include <netipsec/ipsec6.h>
#endif
+#include <netipsec/key.h>
#define IPSEC
#endif /*FAST_IPSEC*/
@@ -1917,3 +1920,114 @@ tcp_xmit_bandwidth_limit(struct tcpcb *tp, tcp_seq ack_seq)
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 */
+{
+ union sockaddr_union dst;
+ 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 *);
+ bzero(&dst, sizeof(union sockaddr_union));
+ 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;
+ /*
+ * Look up an SADB entry which matches the address found in
+ * the segment.
+ */
+ sav = KEY_ALLOCSA(&dst, IPPROTO_TCP, htonl(TCP_SIG_SPI));
+ if (sav == NULL) {
+ printf("%s: SADB lookup failed for %s\n", __func__,
+ inet_ntoa(dst.sin.sin_addr));
+ 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_usrreq.c b/sys/netinet/tcp_usrreq.c
index 212ccd2a82aa..6d4e540bd981 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -35,6 +35,7 @@
*/
#include "opt_ipsec.h"
+#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_tcpdebug.h"
@@ -1065,6 +1066,19 @@ tcp_ctloutput(so, sopt)
switch (sopt->sopt_dir) {
case SOPT_SET:
switch (sopt->sopt_name) {
+#ifdef TCP_SIGNATURE
+ case TCP_SIGNATURE_ENABLE:
+ error = sooptcopyin(sopt, &optval, sizeof optval,
+ sizeof optval);
+ if (error)
+ break;
+
+ if (optval > 0)
+ tp->t_flags |= TF_SIGNATURE;
+ else
+ tp->t_flags &= ~TF_SIGNATURE;
+ break;
+#endif /* TCP_SIGNATURE */
case TCP_NODELAY:
case TCP_NOOPT:
error = sooptcopyin(sopt, &optval, sizeof optval,
@@ -1125,6 +1139,11 @@ tcp_ctloutput(so, 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 33353b47c2f9..0390f1ea4eec 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -105,6 +105,7 @@ struct tcpcb {
#define TF_RXWIN0SENT 0x080000 /* sent a receiver win 0 in response */
#define TF_FASTRECOVERY 0x100000 /* in NewReno Fast Recovery */
#define TF_WASFRECOVERY 0x200000 /* was in NewReno Fast Recovery */
+#define TF_SIGNATURE 0x400000 /* require MD5 digests (RFC2385) */
int t_force; /* 1 if forcing out a byte */
tcp_seq snd_una; /* send unacknowledged */
@@ -189,6 +190,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 */
+
/*
* Structure to hold TCP options that are only used during segment
* processing (in tcp_input), but not held in the tcpcb.
@@ -203,6 +219,8 @@ struct tcpopt {
#define TOF_CCECHO 0x0008
#define TOF_MSS 0x0010
#define TOF_SCALE 0x0020
+#define TOF_SIGNATURE 0x0040 /* signature option present */
+#define TOF_SIGLEN 0x0080 /* sigature length valid (RFC2385) */
u_int32_t to_tsval;
u_int32_t to_tsecr;
tcp_cc to_cc; /* holds CC or CCnew */
@@ -234,6 +252,7 @@ struct syncache {
#define SCF_TIMESTAMP 0x04 /* negotiated timestamps */
#define SCF_CC 0x08 /* negotiated CC */
#define SCF_UNREACH 0x10 /* icmp unreachable received */
+#define SCF_SIGNATURE 0x20 /* send MD5 digests */
TAILQ_ENTRY(syncache) sc_hash;
TAILQ_ENTRY(syncache) sc_timerq;
};
@@ -549,6 +568,12 @@ void tcp_hc_updatetao(struct in_conninfo *, int, tcp_cc, u_short);
#define TCP_HC_TAO_CCSENT 0x2
#define TCP_HC_TAO_MSSOPT 0x3
+#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;
extern u_long tcp_recvspace;