diff options
author | bms <bms@FreeBSD.org> | 2004-02-11 04:26:04 +0000 |
---|---|---|
committer | bms <bms@FreeBSD.org> | 2004-02-11 04:26:04 +0000 |
commit | 903cdeea1a6d0c99fecc1d8aeeab65bdfbab46d7 (patch) | |
tree | 7a14054c69fb5681c86f54e9b66a9128c1e16003 /sys | |
parent | dfc906ec95fc478c611f5ef93b40dc04b3ecb6e8 (diff) | |
download | FreeBSD-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')
-rw-r--r-- | sys/conf/files | 1 | ||||
-rw-r--r-- | sys/conf/options | 1 | ||||
-rw-r--r-- | sys/net/pfkeyv2.h | 6 | ||||
-rw-r--r-- | sys/netinet/ip.h | 12 | ||||
-rw-r--r-- | sys/netinet/ip_output.c | 1 | ||||
-rw-r--r-- | sys/netinet/tcp.h | 3 | ||||
-rw-r--r-- | sys/netinet/tcp_input.c | 17 | ||||
-rw-r--r-- | sys/netinet/tcp_output.c | 37 | ||||
-rw-r--r-- | sys/netinet/tcp_reass.c | 17 | ||||
-rw-r--r-- | sys/netinet/tcp_subr.c | 114 | ||||
-rw-r--r-- | sys/netinet/tcp_syncache.c | 40 | ||||
-rw-r--r-- | sys/netinet/tcp_timewait.c | 114 | ||||
-rw-r--r-- | sys/netinet/tcp_usrreq.c | 19 | ||||
-rw-r--r-- | sys/netinet/tcp_var.h | 25 | ||||
-rw-r--r-- | sys/netinet6/ipsec.h | 2 | ||||
-rw-r--r-- | sys/netipsec/ipsec.h | 1 | ||||
-rw-r--r-- | sys/netipsec/key.c | 18 |
17 files changed, 424 insertions, 4 deletions
diff --git a/sys/conf/files b/sys/conf/files index 233ae14..88a23b5 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1506,6 +1506,7 @@ netipsec/xform_ah.c optional fast_ipsec netipsec/xform_esp.c optional fast_ipsec netipsec/xform_ipcomp.c optional fast_ipsec netipsec/xform_ipip.c optional fast_ipsec +netipsec/xform_tcp.c optional fast_ipsec tcp_signature netipx/ipx.c optional ipx netipx/ipx_cksum.c optional ipx netipx/ipx_input.c optional ipx diff --git a/sys/conf/options b/sys/conf/options index 3b36f81..d9c37d6 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -360,6 +360,7 @@ PPP_FILTER opt_ppp.h RANDOM_IP_ID SLIP_IFF_OPTS opt_slip.h TCPDEBUG +TCP_SIGNATURE opt_inet.h TCP_DROP_SYNFIN opt_tcp_input.h XBONEHACK MBUF_STRESS_TEST opt_mbuf_stress_test.h diff --git a/sys/net/pfkeyv2.h b/sys/net/pfkeyv2.h index 4a3fc84..a164f3b 100644 --- a/sys/net/pfkeyv2.h +++ b/sys/net/pfkeyv2.h @@ -286,7 +286,8 @@ struct sadb_x_ipsecrequest { #define SADB_SATYPE_MIP 8 #define SADB_X_SATYPE_IPCOMP 9 /*#define SADB_X_SATYPE_POLICY 10 obsolete, do not reuse */ -#define SADB_SATYPE_MAX 11 +#define SADB_X_SATYPE_TCPSIGNATURE 11 +#define SADB_SATYPE_MAX 12 #define SADB_SASTATE_LARVAL 0 #define SADB_SASTATE_MATURE 1 @@ -300,7 +301,7 @@ struct sadb_x_ipsecrequest { #define SADB_AALG_NONE 0 #define SADB_AALG_MD5HMAC 2 #define SADB_AALG_SHA1HMAC 3 -#define SADB_AALG_MAX 251 +#define SADB_AALG_MAX 252 /* private allocations - based on RFC2407/IANA assignment */ #define SADB_X_AALG_SHA2_256 5 #define SADB_X_AALG_SHA2_384 6 @@ -311,6 +312,7 @@ struct sadb_x_ipsecrequest { #define SADB_X_AALG_MD5 249 /* Keyed MD5 */ #define SADB_X_AALG_SHA 250 /* Keyed SHA */ #define SADB_X_AALG_NULL 251 /* null authentication */ +#define SADB_X_AALG_TCP_MD5 252 /* Keyed TCP-MD5 (RFC2385) */ /* RFC2367 numbers - meets RFC2407 */ #define SADB_EALG_NONE 0 diff --git a/sys/netinet/ip.h b/sys/netinet/ip.h index 025ad08..aca0432 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 a872c00..e567936 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 d958aaf..1df59b6 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 2b7f99a..6562368 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 f30d6c3..c868033 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 2b7f99a..6562368 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 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 */ diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c index 9343bf4..14eb315 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 4b56295..689d0cd 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 212ccd2..6d4e540 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 33353b4..0390f1e 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; diff --git a/sys/netinet6/ipsec.h b/sys/netinet6/ipsec.h index d74a066..4739ddc 100644 --- a/sys/netinet6/ipsec.h +++ b/sys/netinet6/ipsec.h @@ -163,6 +163,7 @@ struct ipsecaux { #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. @@ -186,6 +187,7 @@ struct ipsecaux { #define IPSEC_POLICY_IPSEC 2 /* pass to 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/netipsec/ipsec.h b/sys/netipsec/ipsec.h index c381afd..b15a919 100644 --- a/sys/netipsec/ipsec.h +++ b/sys/netipsec/ipsec.h @@ -161,6 +161,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. diff --git a/sys/netipsec/key.c b/sys/netipsec/key.c index 7a49007..ad41950 100644 --- a/sys/netipsec/key.c +++ b/sys/netipsec/key.c @@ -3003,6 +3003,7 @@ key_setsaval(sav, m, mhp) 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; @@ -3060,6 +3061,7 @@ key_setsaval(sav, m, mhp) sav->key_enc = NULL; /*just in case*/ break; case SADB_SATYPE_AH: + case SADB_X_SATYPE_TCPSIGNATURE: default: error = EINVAL; break; @@ -3084,6 +3086,9 @@ key_setsaval(sav, m, mhp) case SADB_X_SATYPE_IPCOMP: error = xform_init(sav, XF_IPCOMP); break; + case SADB_X_SATYPE_TCPSIGNATURE: + error = xform_init(sav, XF_TCPSIGNATURE); + break; } if (error) { ipseclog((LOG_DEBUG, "%s: unable to initialize SA type %u.\n", @@ -3216,6 +3221,14 @@ key_mature(struct secasvar *sav) } error = xform_init(sav, XF_IPCOMP); break; + case IPPROTO_TCP: + if (sav->alg_enc != SADB_EALG_NONE) { + ipseclog((LOG_DEBUG, "%s: protocol and algorithm " + "mismated.\n", __func__)); + return(EINVAL); + } + error = xform_init(sav, XF_TCPSIGNATURE); + break; default: ipseclog((LOG_DEBUG, "%s: Invalid satype.\n", __func__)); error = EPROTONOSUPPORT; @@ -4251,6 +4264,8 @@ key_satype2proto(satype) return IPPROTO_ESP; case SADB_X_SATYPE_IPCOMP: return IPPROTO_IPCOMP; + case SADB_X_SATYPE_TCPSIGNATURE: + return IPPROTO_TCP; default: return 0; } @@ -4273,6 +4288,8 @@ key_proto2satype(proto) return SADB_SATYPE_ESP; case IPPROTO_IPCOMP: return SADB_X_SATYPE_IPCOMP; + case IPPROTO_TCP: + return SADB_X_SATYPE_TCPSIGNATURE; default: return 0; } @@ -6674,6 +6691,7 @@ key_parse(m, 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: |