diff --git a/share/man/man4/tcp.4 b/share/man/man4/tcp.4 index fc1a7bb83b6f..dec88700c765 100644 --- a/share/man/man4/tcp.4 +++ b/share/man/man4/tcp.4 @@ -121,7 +121,7 @@ supports a number of socket options which can be set with .Xr setsockopt 2 and tested with .Xr getsockopt 2 : -.Bl -tag -width ".Dv TCP_NODELAY" +.Bl -tag -width ".Dv TCP_SIGNATURE_ENABLE" .It Dv TCP_NODELAY Under most circumstances, .Tn TCP @@ -175,6 +175,31 @@ When this 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 @@ -489,7 +514,8 @@ address. .Xr intro 4 , .Xr ip 4 , .Xr syncache 4 , -.Xr ttcp 4 +.Xr ttcp 4 , +.Xr setkey 8 .Rs .%A "V. Jacobson" .%A "R. Braden" @@ -502,6 +528,11 @@ address. .%T "T/TCP \- TCP Extensions for Transactions" .%O "RFC 1644" .Re +.Rs +.%A "A. Heffernan" +.%T "Protection of BGP Sessions via the TCP MD5 Signature Option" +.%O "RFC 2385" +.Re .Sh HISTORY The .Tn TCP diff --git a/sys/conf/files b/sys/conf/files index 233ae14b900d..88a23b546dc8 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 3b36f81b7e0d..d9c37d6ae3d2 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 4a3fc844de74..a164f3befe52 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 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 @@ ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, 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 @@ tcp_output(struct tcpcb *tp) } } +#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 @@ tcp_output(struct tcpcb *tp) */ 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 +#include #ifdef INET6 #include #endif +#include #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 +#include #ifdef INET6 #include #endif +#include #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; diff --git a/sys/netinet6/ipsec.h b/sys/netinet6/ipsec.h index d74a06693b3f..4739ddc374df 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 c381afd6a15a..b15a9193adf3 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 7a490072cd32..ad41950879e5 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: