summaryrefslogtreecommitdiffstats
path: root/sys/netinet/tcp_subr.c
diff options
context:
space:
mode:
authorbms <bms@FreeBSD.org>2004-02-11 04:26:04 +0000
committerbms <bms@FreeBSD.org>2004-02-11 04:26:04 +0000
commit903cdeea1a6d0c99fecc1d8aeeab65bdfbab46d7 (patch)
tree7a14054c69fb5681c86f54e9b66a9128c1e16003 /sys/netinet/tcp_subr.c
parentdfc906ec95fc478c611f5ef93b40dc04b3ecb6e8 (diff)
downloadFreeBSD-src-903cdeea1a6d0c99fecc1d8aeeab65bdfbab46d7.zip
FreeBSD-src-903cdeea1a6d0c99fecc1d8aeeab65bdfbab46d7.tar.gz
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
Diffstat (limited to 'sys/netinet/tcp_subr.c')
-rw-r--r--sys/netinet/tcp_subr.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index 4b56295..689d0cd 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 */
OpenPOWER on IntegriCloud