From owner-svn-src-user@FreeBSD.ORG Fri Nov 8 21:03:49 2013 Return-Path: Delivered-To: svn-src-user@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id 02B5327F; Fri, 8 Nov 2013 21:03:49 +0000 (UTC) (envelope-from andre@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id E21292105; Fri, 8 Nov 2013 21:03:48 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id rA8L3mh2050253; Fri, 8 Nov 2013 21:03:48 GMT (envelope-from andre@svn.freebsd.org) Received: (from andre@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id rA8L3lp6050244; Fri, 8 Nov 2013 21:03:47 GMT (envelope-from andre@svn.freebsd.org) Message-Id: <201311082103.rA8L3lp6050244@svn.freebsd.org> From: Andre Oppermann Date: Fri, 8 Nov 2013 21:03:47 +0000 (UTC) To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r257866 - user/andre/tcp-ao/sys/netinet X-SVN-Group: user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-user@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the experimental " user" src tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 08 Nov 2013 21:03:49 -0000 Author: andre Date: Fri Nov 8 21:03:47 2013 New Revision: 257866 URL: http://svnweb.freebsd.org/changeset/base/257866 Log: Bring TCP-AO closer to functional completion. Remaining rough edges will be fixed in upcoming commits. Not yet implemented are SNE, tcp options option, and key rollover. Sponsored by: Juniper Networks Modified: user/andre/tcp-ao/sys/netinet/tcp_ao.c user/andre/tcp-ao/sys/netinet/tcp_ao.h user/andre/tcp-ao/sys/netinet/tcp_input.c user/andre/tcp-ao/sys/netinet/tcp_output.c user/andre/tcp-ao/sys/netinet/tcp_subr.c user/andre/tcp-ao/sys/netinet/tcp_syncache.c user/andre/tcp-ao/sys/netinet/tcp_syncache.h user/andre/tcp-ao/sys/netinet/tcp_usrreq.c Modified: user/andre/tcp-ao/sys/netinet/tcp_ao.c ============================================================================== --- user/andre/tcp-ao/sys/netinet/tcp_ao.c Fri Nov 8 20:11:15 2013 (r257865) +++ user/andre/tcp-ao/sys/netinet/tcp_ao.c Fri Nov 8 21:03:47 2013 (r257866) @@ -39,16 +39,15 @@ * 3. key management in the kernel and the exposed userland API. * 4. test programs to verify the correct operation. * - * TODO: - * all of the above. - * - * Discussion: - * the key management can be done in two ways: via the ipsec key interface - * or through the setsockopt() api. Analyse which one is better to handle - * in the kernel and for userspace applications. The setsockopt() API is - * the winner and will be used. + * The key management is done through the setsockopt() api because the + * keys are connection specific. * - * legacy tcp-md5 can be brought and integrated into the tcp-ao framework. + * TODO: + * sequence number extension + * tcp options verification option + * key rollover + * bring legacy tcp-md5 into the tcp-ao framework. + * throughout testing */ #include "opt_inet.h" @@ -68,6 +67,7 @@ #include #include +#include #include #include @@ -101,22 +101,28 @@ MALLOC_DEFINE(M_TCPAO, "tcp_ao", "TCP-AO peer and key structures"); -int tcp_ao_ctl(struct tcpcb *, struct tcp_ao_sopt *, int); - -struct tcp_ao_peer *tcp_ao_peer_find(struct tcp_ao_cb *, struct sockaddr *); -struct tcp_ao_peer *tcp_ao_peer_add(struct tcp_ao_cb *, struct sockaddr *); -int tcp_ao_peer_del(struct tcp_ao_cb *, struct tcp_ao_peer *); -void tcp_ao_peer_flush(struct tcp_ao_cb *); - -struct tcp_ao_key *tcp_ao_key_find(struct tcp_ao_peer *, uint8_t); -struct tcp_ao_key *tcp_ao_key_add(struct tcp_ao_peer *, struct tcp_ao_sopt *, uint8_t); -int tcp_ao_key_del(struct tcp_ao_peer *, uint8_t); -void tcp_ao_key_flush(struct tcp_ao_peer *); - -int tcp_ao_peer_clone(struct tcpcb *, struct tcpcb *, struct sockaddr *); -struct tcp_ao_cb *tcp_ao_cb_alloc(void); -void tcp_ao_cb_free(struct tcpcb *); +static struct tcp_ao_peer + *tcp_ao_peer_find(struct tcp_ao_cb *, struct sockaddr *); +static struct tcp_ao_peer + *tcp_ao_peer_add(struct tcp_ao_cb *, struct sockaddr *); +static int tcp_ao_peer_del(struct tcp_ao_cb *, struct tcp_ao_peer *); +static void tcp_ao_peer_flush(struct tcp_ao_cb *); + +static struct tcp_ao_key + *tcp_ao_key_find(struct tcp_ao_peer *, uint8_t); +static struct tcp_ao_key + *tcp_ao_key_add(struct tcp_ao_peer *, struct tcp_ao_sopt *, uint8_t); +static int tcp_ao_key_del(struct tcp_ao_peer *, uint8_t); +static void tcp_ao_key_flush(struct tcp_ao_peer *); + +static int tcp_ao_peer_clone(struct tcpcb *, struct tcpcb *, + struct sockaddr *); +static struct tcp_ao_cb + *tcp_ao_cb_alloc(void); +/* + * Perform key management when called from setsockopt. + */ int tcp_ao_ctl(struct tcpcb *tp, struct tcp_ao_sopt *tao, int tao_len) { @@ -143,6 +149,13 @@ tcp_ao_ctl(struct tcpcb *tp, struct tcp_ if (error) goto out; + if (tp->t_ao == NULL) { + if ((tp->t_ao = tcp_ao_cb_alloc()) == NULL) { + error = ENOMEM; + goto out; + } + tp->t_flags |= TF_AO; + } c = tp->t_ao; switch (tao->tao_cmd) { @@ -157,11 +170,12 @@ tcp_ao_ctl(struct tcpcb *tp, struct tcp_ goto out; } - /* Insert or overwrite */ + /* Find existing peer or allocate new one. */ if ((p = tcp_ao_peer_add(c, sa)) == NULL) { error = EINVAL; break; } + /* Can't overwrite active key on an active session. */ if (tp->t_state > TCPS_LISTEN && p->tap_activekey == tao->tao_keyidx) { error = EINVAL; @@ -173,11 +187,11 @@ tcp_ao_ctl(struct tcpcb *tp, struct tcp_ break; case TAO_CMD_DELIDX: - /* Can't remove active index */ if ((p = tcp_ao_peer_find(c, sa)) == NULL) { error = EINVAL; break; } + /* Can't remove active key index. */ if (tp->t_state > TCPS_LISTEN && p->tap_activekey == tao->tao_keyidx) { error = EINVAL; @@ -189,6 +203,7 @@ tcp_ao_ctl(struct tcpcb *tp, struct tcp_ break; case TAO_CMD_DELPEER: + /* Can't remove active peer. */ if (tp->t_state > TCPS_LISTEN) break; @@ -200,6 +215,7 @@ tcp_ao_ctl(struct tcpcb *tp, struct tcp_ break; case TAO_CMD_FLUSH: + /* Can't remove active peer. */ if (tp->t_state > TCPS_LISTEN) break; @@ -215,7 +231,7 @@ out: return (error); } -struct tcp_ao_peer * +static struct tcp_ao_peer * tcp_ao_peer_find(struct tcp_ao_cb *tac, struct sockaddr *sa) { struct tcp_ao_peer *p; @@ -229,7 +245,7 @@ tcp_ao_peer_find(struct tcp_ao_cb *tac, return (NULL); } -struct tcp_ao_peer * +static struct tcp_ao_peer * tcp_ao_peer_add(struct tcp_ao_cb *tac, struct sockaddr *sa) { struct tcp_ao_peer *p; @@ -246,7 +262,7 @@ tcp_ao_peer_add(struct tcp_ao_cb *tac, s return (p); } -int +static int tcp_ao_peer_del(struct tcp_ao_cb *tac, struct tcp_ao_peer *tap) { @@ -256,7 +272,7 @@ tcp_ao_peer_del(struct tcp_ao_cb *tac, s return (0); } -void +static void tcp_ao_peer_flush(struct tcp_ao_cb *tac) { struct tcp_ao_peer *p, *p2; @@ -268,7 +284,7 @@ tcp_ao_peer_flush(struct tcp_ao_cb *tac) LIST_INIT(&tac->tac_peers); } -struct tcp_ao_key * +static struct tcp_ao_key * tcp_ao_key_find(struct tcp_ao_peer *tap, uint8_t idx) { struct tcp_ao_key *k; @@ -280,7 +296,7 @@ tcp_ao_key_find(struct tcp_ao_peer *tap, return (NULL); } -struct tcp_ao_key * +static struct tcp_ao_key * tcp_ao_key_add(struct tcp_ao_peer *tap, struct tcp_ao_sopt *tao, uint8_t keylen) { struct tcp_ao_key *k; @@ -302,7 +318,7 @@ tcp_ao_key_add(struct tcp_ao_peer *tap, return (k); } -int +static int tcp_ao_key_del(struct tcp_ao_peer *tap, uint8_t keyidx) { struct tcp_ao_key *k, *k2; @@ -317,7 +333,7 @@ tcp_ao_key_del(struct tcp_ao_peer *tap, return (ENOENT); } -void +static void tcp_ao_key_flush(struct tcp_ao_peer *tap) { struct tcp_ao_key *k, *k2; @@ -327,7 +343,7 @@ tcp_ao_key_flush(struct tcp_ao_peer *tap SLIST_INIT(&tap->tap_keys); } -int +static int tcp_ao_peer_clone(struct tcpcb *tp1, struct tcpcb *tp2, struct sockaddr *sa) { struct tcp_ao_peer *p1, *p2; @@ -408,6 +424,8 @@ struct tcp_ao_kdf_ctx { #define ip4_ctx tuple.ip4 #define ip6_ctx tuple.ip6 +static int tcp_ao_kdf(struct tcp_ao_key *tak, struct in_conninfo *inc, + struct tcphdr *th, uint8_t *out, int outlen, int dir); static int tcp_ao_kdf_hmac(struct tcp_ao_key *tak, uint8_t *out, int outlen, struct tcp_ao_kdf_ctx *tctx); static int tcp_ao_kdf_cmac(struct tcp_ao_key *tak, uint8_t *out, int outlen, @@ -420,11 +438,10 @@ static int tcp_ao_kdf_md5(struct tcp_ao_ * Return values: * 0 = success * EINVAL = invalid input, typically insufficient length - * other = key derivation failed */ -int -tcp_ao_kdf(struct in_conninfo *inc, struct tcphdr *th, uint8_t *out, - int outlen, struct tcp_ao_key *tak) +static int +tcp_ao_kdf(struct tcp_ao_key *tak, struct in_conninfo *inc, struct tcphdr *th, + uint8_t *out, int outlen, int dir) { int error = 0; struct tcp_ao_kdf_ctx ctx; @@ -432,15 +449,33 @@ tcp_ao_kdf(struct in_conninfo *inc, stru /* Fill in context for traffic keys. */ switch (inc->inc_flags & INC_ISIPV6) { case 0: - ctx.ip4_ctx.src = inc->inc_ie.ie_faddr; - ctx.ip4_ctx.dst = inc->inc_ie.ie_laddr; + if (dir == TCP_AO_OUT) { + ctx.ip4_ctx.src = inc->inc_ie.ie_laddr; + ctx.ip4_ctx.dst = inc->inc_ie.ie_faddr; + ctx.ip4_ctx.sport = inc->inc_ie.ie_lport; + ctx.ip4_ctx.dport = inc->inc_ie.ie_fport; + } else { + ctx.ip4_ctx.src = inc->inc_ie.ie_faddr; + ctx.ip4_ctx.dst = inc->inc_ie.ie_laddr; + ctx.ip4_ctx.sport = inc->inc_ie.ie_fport; + ctx.ip4_ctx.dport = inc->inc_ie.ie_lport; + } ctx.ip4_ctx.irs = htonl(th->th_ack); ctx.ip4_ctx.iss = htonl(th->th_seq); ctx.len = sizeof(ctx.ip4_ctx); break; case INC_ISIPV6: - ctx.ip6_ctx.src = inc->inc_ie.ie6_faddr; - ctx.ip6_ctx.dst = inc->inc_ie.ie6_laddr; + if (dir == TCP_AO_OUT) { + ctx.ip6_ctx.src = inc->inc_ie.ie6_laddr; + ctx.ip6_ctx.dst = inc->inc_ie.ie6_faddr; + ctx.ip6_ctx.sport = inc->inc_ie.ie_lport; + ctx.ip6_ctx.dport = inc->inc_ie.ie_fport; + } else { + ctx.ip6_ctx.src = inc->inc_ie.ie6_faddr; + ctx.ip6_ctx.dst = inc->inc_ie.ie6_laddr; + ctx.ip6_ctx.sport = inc->inc_ie.ie_fport; + ctx.ip6_ctx.dport = inc->inc_ie.ie_lport; + } ctx.ip6_ctx.irs = htonl(th->th_ack); ctx.ip6_ctx.iss = htonl(th->th_seq); ctx.len = sizeof(ctx.ip6_ctx); @@ -541,7 +576,7 @@ tcp_ao_kdf_md5(struct tcp_ao_key *tak, u /* - * The hash over the header has to be pieced together and a couple of + * The hash over the header has to be stiched together and a couple of * fields need manipulation, like zero'ing and byte order conversion. * Instead of doing it in-place and calling the hash update function * for each we just copy everything into one place in the right order. @@ -568,19 +603,14 @@ struct tcp_ao_pseudo { #define tap4 tap.tap_ip #define tap6 tap.tap_ip6 -#if 0 -/* Convenient functions not yet in existence. */ -ip_hdr2pseudo(struct ip *ip, struct ippseudo *ipp); -ip6_hdr2pseudo(struct ip6_hdr *ip6, struct ip6pseudo *ipp6); -ip_inc2pseudo(struct in_conninfo *inc, struct ippseudo *ipp); -ip6_inc2pseudo(struct in_conninfo *inc, struct ip6pseudo *ipp6); -#endif - -static int tcp_ao_sha1(struct tcp_ao_cb *tac, struct tcp_ao_pseudo *ph, +static int tcp_ao_mac(struct in_conninfo *inc, struct tcphdr *th, + struct tcpopt *to, struct mbuf *m, int tlen, uint32_t sne, int algo, + uint8_t *key, uint8_t *hash, int dir); +static int tcp_ao_sha1(uint8_t *key, struct tcp_ao_pseudo *ph, struct mbuf *m, int moff, int mlen, uint8_t *hash); -static int tcp_ao_cmac(struct tcp_ao_cb *tac, struct tcp_ao_pseudo *ph, +static int tcp_ao_cmac(uint8_t *key, struct tcp_ao_pseudo *ph, struct mbuf *m, int moff, int mlen, uint8_t *hash); -static int tcp_ao_md5(struct tcp_ao_cb *tac, struct tcp_ao_pseudo *ph, +static int tcp_ao_md5(uint8_t *key, struct tcp_ao_pseudo *ph, struct mbuf *m, int moff, int mlen, uint8_t *hash); /* @@ -590,46 +620,58 @@ static int tcp_ao_md5(struct tcp_ao_cb * * EAUTH = authentication failed * other = authentication failed */ -int -tcp_ao_mac(struct tcpcb *tp, struct tcp_ao_cb *tac, struct in_conninfo *inc, - struct tcphdr *th, struct tcpopt *to, struct mbuf *m) +static int +tcp_ao_mac(struct in_conninfo *inc, struct tcphdr *th, struct tcpopt *to, + struct mbuf *m, int tlen, uint32_t sne, int algo, uint8_t *key, + uint8_t *hash, int dir) { int moff, mlen, thlen; struct tcp_ao_pseudo ph; struct tcp_ao_thopt *tho; - uint8_t hash[100]; int error; - /* - * Set up the virtual sequence number extension that is part of - * the authentication hash. - */ - if (tp != NULL) - ph.tap_sne = tp->t_ao->tac_sne; - else - ph.tap_sne = 0; + ph.tap_sne = sne; + + /* Fill in tcpheader including options. */ + thlen = th->th_off << 2; /* Fill in pseudo headers. */ switch(inc->inc_flags & INC_ISIPV6) { case 0: - /* ip_inc2pseudo(inc, &ph.tap4.tap_ph4); */ - ph.tap4.tap_ph4.ippseudo_src = inc->inc_faddr; - ph.tap4.tap_ph4.ippseudo_dst = inc->inc_laddr; + tho = &ph.tap4.tap_th; + if (dir == TCP_AO_OUT) { + ph.tap4.tap_ph4.ippseudo_src = inc->inc_laddr; + ph.tap4.tap_ph4.ippseudo_dst = inc->inc_faddr; + tho->th.th_sport = htons(th->th_sport); + tho->th.th_dport = htons(th->th_dport); + } else { + ph.tap4.tap_ph4.ippseudo_src = inc->inc_faddr; + ph.tap4.tap_ph4.ippseudo_dst = inc->inc_laddr; + tho->th.th_sport = htons(th->th_dport); + tho->th.th_dport = htons(th->th_sport); + } ph.tap4.tap_ph4.ippseudo_pad = 0; ph.tap4.tap_ph4.ippseudo_p = IPPROTO_TCP; - ph.tap4.tap_ph4.ippseudo_len = m->m_pkthdr.len; + ph.tap4.tap_ph4.ippseudo_len = htons(tlen); ph.tap_len += sizeof(ph.tap4.tap_ph4); - tho = &ph.tap4.tap_th; break; case INC_ISIPV6: - /* ip6_hdr2pseudo(inc, &ph.tap6.tap_ph6); */ - ph.tap6.tap_ph6.ip6pseudo_src = inc->inc6_faddr; - ph.tap6.tap_ph6.ip6pseudo_dst = inc->inc6_laddr; - ph.tap6.tap_ph6.ip6pseudo_len = m->m_pkthdr.len; + tho = &ph.tap6.tap_th; + if (dir == TCP_AO_OUT) { + ph.tap6.tap_ph6.ip6pseudo_src = inc->inc6_laddr; + ph.tap6.tap_ph6.ip6pseudo_dst = inc->inc6_faddr; + tho->th.th_sport = htons(th->th_sport); + tho->th.th_dport = htons(th->th_dport); + } else { + ph.tap6.tap_ph6.ip6pseudo_src = inc->inc6_faddr; + ph.tap6.tap_ph6.ip6pseudo_dst = inc->inc6_laddr; + tho->th.th_sport = htons(th->th_dport); + tho->th.th_dport = htons(th->th_sport); + } + ph.tap6.tap_ph6.ip6pseudo_len = htons(tlen); ph.tap6.tap_ph6.ip6pseudo_pad = 0; ph.tap6.tap_ph6.ip6pseudo_p = IPPROTO_TCP; ph.tap_len += sizeof(ph.tap6.tap_ph6); - tho = &ph.tap6.tap_th; break; default: error = EINVAL; @@ -637,29 +679,27 @@ tcp_ao_mac(struct tcpcb *tp, struct tcp_ } ph.tap_len += sizeof(ph.tap_sne); - /* Fill in tcpheader including options. */ - thlen = th->th_off << 2; - bcopy(th, tho, thlen); - ph.tap_len += thlen; - - /* Zero out checksum and mac field and swap to network byte order. */ - tho->th.th_sum = 0; +// bcopy(th, tho, thlen); + tho->th.th_sum = 0; /* Zero out checksum */ bzero(tho->tho + (to->to_signature - (u_char *)(th + 1)), to->to_siglen); - /* tcp_fields_to_net(&tho); */ + tho->th.th_seq = htonl(th->th_seq); + tho->th.th_ack = htonl(th->th_ack); + tho->th.th_win = htons(th->th_win); + tho->th.th_urp = htons(th->th_urp); /* Set up the mbuf length fields. */ moff = thlen; mlen = m_length(m, NULL) - thlen; - switch(tac->tac_algo) { + switch(algo) { case TAO_ALGO_HMAC_SHA_1_96: - error = tcp_ao_sha1(tac, &ph, m, moff, mlen, hash); + error = tcp_ao_sha1(key, &ph, m, moff, mlen, hash); break; case TAO_ALGO_AES_128_CMAC_96: - error = tcp_ao_cmac(tac, &ph, m, moff, mlen, hash); + error = tcp_ao_cmac(key, &ph, m, moff, mlen, hash); break; case TAO_ALGO_MD5SIG: - error = tcp_ao_md5(tac, &ph, m, moff, mlen, hash); + error = tcp_ao_md5(key, &ph, m, moff, mlen, hash); break; default: error = EINVAL; @@ -668,15 +708,223 @@ tcp_ao_mac(struct tcpcb *tp, struct tcp_ if (error) goto out; - /* Compare result to segment signature. */ +out: + return (error); +} + +/* + * We have three cases to verify: + * a) LISTEN; + * - SYN; create RX and TX traffic keys on the fly; + * - ACK; create and store RX and TX traffic keys; + * b) SYN SENT; create traffic TX traffic key; + * - SYN; create TX traffic key; + * - SYN/ACK; create RX traffic key; + * c) ESTABLISHED; verify traffic; + * + * We have two cases to compute a signature: + * a) syncache; on the fly with traffic key derivation; + * b) established; directly use traffic key; + */ +int +tcp_ao_sc_findmkey(struct tcpcb *tp, struct in_conninfo *inc, struct tcpopt *to, + struct tcp_ao_key *tkey) +{ + struct tcp_ao_cb *tac; + struct tcp_ao_peer *tap; + struct tcp_ao_key *tak; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct sockaddr sa; + uint8_t idx = 0; + + tac = tp->t_ao; + + switch (inc->inc_flags & INC_ISIPV6) { + case 0: + sin = (struct sockaddr_in *)&sa; + sin->sin_len = sizeof(struct sockaddr_in); + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr = inc->inc_faddr; + break; + case INC_ISIPV6: + sin6 = (struct sockaddr_in6 *)&sa; + sin6->sin6_len = sizeof(struct sockaddr_in6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + sin6->sin6_addr = inc->inc6_faddr; + break; + default: + return (EINVAL); + break; + } + + if ((tap = tcp_ao_peer_find(tac, &sa)) == NULL) + return (EINVAL); + + if ((tak = tcp_ao_key_find(tap, idx)) == NULL) + return (EINVAL); + + bcopy(tak, tkey, sizeof(struct tcp_ao_key) + tak->keylen); + + return (0); +} + +int +tcp_ao_sc_verify(struct tcp_ao_key *tak, struct in_conninfo *inc, + struct tcphdr *th, struct tcpopt *to, struct mbuf *m, int tlen) +{ + int error; + uint8_t hash[100]; + + /* use master key, derive RX traffic key, and compare. */ + error = tcp_ao_kdf(tak, inc, th, hash, 100, 0); + if (error) + goto out; + + error = tcp_ao_mac(inc, th, to, m, tlen, tak->keyalgo, 0, + (uint8_t *)&hash, hash, 0); + if (error) + goto out; + if (bcmp(hash, to->to_signature, to->to_siglen)) error = EAUTH; +out: + return (error); +} +int +tcp_ao_sc_hash(struct tcp_ao_key *tak, struct in_conninfo *inc, + struct tcphdr *th, struct tcpopt *to, struct mbuf *m, int tlen) +{ + int error; + uint8_t hash[100]; + + /* use master key to derive TX traffic key and put into segment. */ + error = tcp_ao_kdf(tak, inc, th, hash, 100, 1); + if (error) + goto out; + + error = tcp_ao_mac(inc, th, to, m, tlen, tak->keyalgo, 0, + (uint8_t *)&hash, hash, 1); + if (error) + goto out; + + bcopy(hash, to->to_signature, to->to_siglen); +out: + return (error); +} + +int +tcp_ao_sc_copy(struct tcpcb *ltp, struct tcpcb *tp, struct in_conninfo *inc) +{ + int error; + + /* allocate a tcp_ao_cb and copy this peer's structure and keys */ + error = tcp_ao_peer_clone(ltp, tp, NULL); + + return (error); +} + +int +tcp_ao_est_verify(struct tcpcb *tp, struct tcphdr *th, struct tcpopt *to, + struct mbuf *m, int tlen) +{ + struct tcp_ao_cb *tac; + int siglen; + int error; + uint8_t hash[100]; + + tac = tp->t_ao; + + switch (tac->tac_algo) { + case TAO_ALGO_HMAC_SHA_1_96: + siglen = TAO_ALGO_HMAC_SHA_1_96_LEN; + break; + case TAO_ALGO_AES_128_CMAC_96: + siglen = TAO_ALGO_AES_128_CMAC_96_LEN; + break; + case TAO_ALGO_MD5SIG: + siglen = TAO_ALGO_MD5SIG; + break; + default: + error = EAUTH; + goto out; + } + if (to->to_siglen != siglen) { + error = EAUTH; + goto out; + } + + /* use RX traffic key and compare. */ + error = tcp_ao_mac(&tp->t_inpcb->inp_inc, th, to, m, tlen, + tac->tac_algo, tac->tac_rcvsne, (uint8_t *)&tac->tac_rkey, + hash, 0); + if (error) + goto out; + + if (bcmp(hash, to->to_signature, to->to_siglen)) + error = EAUTH; out: return (error); } /* + * Prepare and compute hash for outbound segments. + */ +int +tcp_ao_est_hash(struct tcpcb *tp, struct tcphdr *th, struct tcpopt *to, + struct mbuf *m, int tlen) +{ + struct tcp_ao_cb *tac; + int error; + uint8_t hash[100]; + + tac = tp->t_ao; + + error = tcp_ao_mac(&tp->t_inpcb->inp_inc, th, to, m, tlen, + tac->tac_algo, tac->tac_sndsne, (uint8_t *)&tac->tac_skey, + hash, 1); + + if (!error) + bcopy(hash, to->to_signature, to->to_siglen); + + return (error); +} + +int +tcp_ao_est_opt(struct tcpcb *tp, struct tcpopt *to) +{ + struct tcp_ao_cb *c = tp->t_ao; + struct tcp_ao_peer *p; + int siglen; + + switch (c->tac_algo) { + case TAO_ALGO_HMAC_SHA_1_96: + siglen = TAO_ALGO_HMAC_SHA_1_96_LEN; + break; + case TAO_ALGO_AES_128_CMAC_96: + siglen = TAO_ALGO_AES_128_CMAC_96_LEN; + break; + case TAO_ALGO_MD5SIG: + siglen = TAO_ALGO_MD5SIG; + break; + default: + siglen = 12; /* XXX */ + break; + } + to->to_siglen = siglen; + + p = LIST_FIRST(&c->tac_peers); + + to->to_ao_keyid = p->tap_activekey; + to->to_ao_nextkeyid = p->tap_nextkey; + + return (0); +} + +/* * Note: Can't use cryptodev because of callback based non-inline * processing. Also complexity to set up a crypto session is too high * and requires a couple of malloc's. @@ -695,13 +943,13 @@ HMAC_SHA1_Update_x(void *ctx, void *data } static int -tcp_ao_sha1(struct tcp_ao_cb *tac, struct tcp_ao_pseudo *ph, struct mbuf *m, +tcp_ao_sha1(uint8_t *key, struct tcp_ao_pseudo *ph, struct mbuf *m, int moff, int mlen, uint8_t *hash) { HMAC_SHA1_CTX ctx; int error = 0; - HMAC_SHA1_Init(&ctx, tac->tac_skey.hmac, SHA1_BLOCK_LENGTH); + HMAC_SHA1_Init(&ctx, key, SHA1_BLOCK_LENGTH); /* Pseudo header. */ HMAC_SHA1_Update(&ctx, (uint8_t *)ph, ph->tap_len); @@ -729,14 +977,14 @@ AES_CMAC_Update_x(void *ctx, void *data, } static int -tcp_ao_cmac(struct tcp_ao_cb *tac, struct tcp_ao_pseudo *ph, struct mbuf *m, +tcp_ao_cmac(uint8_t *key, struct tcp_ao_pseudo *ph, struct mbuf *m, int moff, int mlen, uint8_t *hash) { AES_CMAC_CTX ctx; int error = 0; AES_CMAC_Init(&ctx); - AES_CMAC_SetKey(&ctx, tac->tac_skey.cmac); + AES_CMAC_SetKey(&ctx, key); AES_CMAC_Update(&ctx, (uint8_t *)ph, ph->tap_len); @@ -764,7 +1012,7 @@ MD5Update_x(void *ctx, void *data, u_int } static int -tcp_ao_md5(struct tcp_ao_cb *tac, struct tcp_ao_pseudo *ph, struct mbuf *m, +tcp_ao_md5(uint8_t *key, struct tcp_ao_pseudo *ph, struct mbuf *m, int moff, int mlen, uint8_t hash[static MD5_DIGEST_LENGTH]) { MD5_CTX ctx; @@ -780,7 +1028,7 @@ tcp_ao_md5(struct tcp_ao_cb *tac, struct if (error) goto out; - MD5Update(&ctx, tac->tac_skey.md5, MD5_BLOCK_LENGTH); + MD5Update(&ctx, key, MD5_BLOCK_LENGTH); MD5Final(hash, &ctx); out: Modified: user/andre/tcp-ao/sys/netinet/tcp_ao.h ============================================================================== --- user/andre/tcp-ao/sys/netinet/tcp_ao.h Fri Nov 8 20:11:15 2013 (r257865) +++ user/andre/tcp-ao/sys/netinet/tcp_ao.h Fri Nov 8 21:03:47 2013 (r257866) @@ -69,6 +69,11 @@ MALLOC_DECLARE(M_TCPAO); +#include +#include +#include +#include + /* * TCP-AO key interface struct passed to setsockopt(). * Per peer structures referenced from tcp_ao_sopt. @@ -83,7 +88,7 @@ struct tcp_ao_sopt { tao_peer; /* this key applies to ... */ uint8_t tao_key[]; /* base64 key string */ }; -#define TAO_KEY_MAXLEN 128 /* base64 encoded */ +#define TAO_KEY_MAXLEN 128 /* * Commands for the tao_cmd field. @@ -105,44 +110,80 @@ struct tcp_ao_sopt { #define TAO_ALGO_HMAC_SHA_1_96 2 /* RFC5926, Section 2.2 */ #define TAO_ALGO_AES_128_CMAC_96 3 /* RFC5926, Section 2.2 */ +#define TAO_ALGO_MD5SIG_LEN 16 +#define TAO_ALGO_HMAC_SHA_1_96_LEN 12 +#define TAO_ALGO_AES_128_CMAC_96_LEN 12 + +#ifdef _KERNEL +#define TCP_AO_MAXHASH 20 /* max raw hash length */ + /* * In kernel storage of the key information. + * The active session key is stored in tac_skey and tac_rkey. */ struct tcp_ao_cb { + LIST_HEAD(tac_peer, tcp_ao_peer) tac_peers; int tac_algo; + uint32_t tac_sndsne, tac_rcvsne; + /* Pre-computed traffic keys for established sessions. */ union { uint8_t md5[MD5_DIGEST_LENGTH]; uint8_t hmac[SHA1_DIGEST_LENGTH]; uint8_t cmac[AES_CMAC_DIGEST_LENGTH]; } tac_skey, tac_rkey; - uint32_t tac_sne; - LIST_HEAD(tac_peer, tcp_ao_peer) tac_peers; }; +/* + * Per peer information (remote IP). + * A control block can have N peers in LISTEN mode. + * A control block can have 1 peer in SYN_SENT or SYN_RECEIVED mode. + */ struct tcp_ao_peer { LIST_ENTRY(tcp_ao_peer) tap_entry; uint16_t tap_flags; + uint8_t tap_activekey; + uint8_t tap_nextkey; union { struct sockaddr sa; struct sockaddr_in sin4; struct sockaddr_in6 sin6; } tap_peer; - uint8_t tap_activekey; SLIST_HEAD(tap_key, tcp_ao_key) tap_keys; }; +/* + * Per key information. A peer can have N keys, of which one is active. + */ struct tcp_ao_key { SLIST_ENTRY(tcp_ao_key) entry; uint8_t keyidx; uint8_t keyflags; uint8_t keyalgo; uint8_t keylen; - uint8_t key[]; /* after base64_decode */ + uint8_t key[]; }; -int tcp_ao_kdf(struct in_conninfo *, struct tcphdr *, uint8_t *, int, - struct tcp_ao_key *); -int tcp_ao_mac(struct tcpcb *, struct tcp_ao_cb *, struct in_conninfo *, - struct tcphdr *, struct tcpopt *, struct mbuf *); +#define TCP_AO_IN 0 +#define TCP_AO_OUT 1 + +int tcp_ao_ctl(struct tcpcb *tp, struct tcp_ao_sopt *tao, int tao_len); + +int tcp_ao_sc_findmkey(struct tcpcb *tp, struct in_conninfo *inc, + struct tcpopt *to, struct tcp_ao_key *tkey); +int tcp_ao_sc_verify(struct tcp_ao_key *tak, struct in_conninfo *inc, + struct tcphdr *th, struct tcpopt *to, struct mbuf *m, int tlen); +int tcp_ao_sc_hash(struct tcp_ao_key *tak, struct in_conninfo *inc, + struct tcphdr *th, struct tcpopt *to, struct mbuf *m, int tlen); +int tcp_ao_sc_copy(struct tcpcb *ltp, struct tcpcb *tp, + struct in_conninfo *inc); + +int tcp_ao_est_verify(struct tcpcb *tp, struct tcphdr *th, struct tcpopt *to, + struct mbuf *m, int tlen); +int tcp_ao_est_hash(struct tcpcb *tp, struct tcphdr *th, struct tcpopt *to, + struct mbuf *m, int tlen); +int tcp_ao_est_opt(struct tcpcb *tp, struct tcpopt *to); + +void tcp_ao_cb_free(struct tcpcb *tp); +#endif /* _KERNEL */ #endif Modified: user/andre/tcp-ao/sys/netinet/tcp_input.c ============================================================================== --- user/andre/tcp-ao/sys/netinet/tcp_input.c Fri Nov 8 20:11:15 2013 (r257865) +++ user/andre/tcp-ao/sys/netinet/tcp_input.c Fri Nov 8 21:03:47 2013 (r257866) @@ -106,6 +106,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #ifdef TCPDEBUG #include #endif /* TCPDEBUG */ @@ -1556,6 +1557,10 @@ tcp_do_segment(struct mbuf *m, struct tc (th->th_off << 2) - sizeof(struct tcphdr), (thflags & TH_SYN) ? TO_SYN : 0); + if (tp->t_flags & TF_AO) + if (tcp_ao_est_verify(tp, th, &to, m, tlen) != 0) + goto drop; + /* * If echoed timestamp is later than the current time, * fall back to non RFC1323 RTT calculation. Normalize Modified: user/andre/tcp-ao/sys/netinet/tcp_output.c ============================================================================== --- user/andre/tcp-ao/sys/netinet/tcp_output.c Fri Nov 8 20:11:15 2013 (r257865) +++ user/andre/tcp-ao/sys/netinet/tcp_output.c Fri Nov 8 21:03:47 2013 (r257866) @@ -75,6 +75,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #ifdef TCPDEBUG #include #endif @@ -740,8 +741,10 @@ send: to.to_flags |= TOF_SIGNATURE; #endif /* TCP_SIGNATURE */ /* TCP-AO (RFC5925). */ - if (tp->t_flags & TF_AO) + if (tp->t_flags & TF_AO) { to.to_flags |= TOF_AO; + tcp_ao_est_opt(tp, &to); + } /* Processing the options. */ hdrlen += optlen = tcp_addoptions(&to, opt); @@ -1078,6 +1081,8 @@ send: (u_char *)(th + 1) + sigoff, IPSEC_DIR_OUTBOUND); } #endif + if (tp->t_flags & TF_AO) + (void)tcp_ao_est_hash(tp, th, &to, m, len); /* * Put TCP length in extended header, and then @@ -1523,32 +1528,31 @@ tcp_addoptions(struct tcpopt *to, u_char *optp++ = TCPOPT_SIGNATURE; *optp++ = TCPOLEN_SIGNATURE; to->to_signature = optp; + to->to_siglen = siglen; while (siglen--) *optp++ = 0; break; } -#if 0 case TOF_AO: { - int siglen = tcp_ao_siglen(tp); + int siglen = to->to_siglen; while (!optlen || optlen % 4 != 2) { optlen += TCPOLEN_NOP; *optp++ = TCPOPT_NOP; } - if (TCP_MAXOLEN - optlen < TCPOLEN_AO_MIN + siglen) + if (TCP_MAXOLEN - optlen < TCPOLEN_AO_MIN + to->to_siglen) continue; optlen += TCPOLEN_AO_MIN; *optp++ = TCPOPT_AO; - *optp++ = TCPOLEN_AO_MIN + siglen; - *optp++ = tcp_ao_keyid(tp); /* keyid */ - *optp++ = tcp_ao_nextkeyid(tp); /* nextkeyid */ + *optp++ = TCPOLEN_AO_MIN + to->to_siglen; + *optp++ = to->to_ao_keyid; + *optp++ = to->to_ao_nextkeyid; to->to_signature = optp; while (siglen--) *optp++ = 0; break; } -#endif case TOF_SACK: { int sackblks = 0; Modified: user/andre/tcp-ao/sys/netinet/tcp_subr.c ============================================================================== --- user/andre/tcp-ao/sys/netinet/tcp_subr.c Fri Nov 8 20:11:15 2013 (r257865) +++ user/andre/tcp-ao/sys/netinet/tcp_subr.c Fri Nov 8 21:03:47 2013 (r257866) @@ -89,6 +89,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #ifdef INET6 #include #endif @@ -1000,6 +1001,8 @@ tcp_discardcb(struct tcpcb *tp) tcp_free_sackholes(tp); + tcp_ao_cb_free(tp); + /* Allow the CC algorithm to clean up after itself. */ if (CC_ALGO(tp)->cb_destroy != NULL) CC_ALGO(tp)->cb_destroy(tp->ccv); Modified: user/andre/tcp-ao/sys/netinet/tcp_syncache.c ============================================================================== --- user/andre/tcp-ao/sys/netinet/tcp_syncache.c Fri Nov 8 20:11:15 2013 (r257865) +++ user/andre/tcp-ao/sys/netinet/tcp_syncache.c Fri Nov 8 21:03:47 2013 (r257866) @@ -84,6 +84,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #ifdef INET6 #include #endif @@ -669,6 +670,7 @@ static struct socket * syncache_socket(struct syncache *sc, struct socket *lso, struct mbuf *m) { struct inpcb *inp = NULL; + struct inpcb *oinp = NULL; struct socket *so; struct tcpcb *tp; int error; @@ -703,6 +705,7 @@ syncache_socket(struct syncache *sc, str #endif inp = sotoinpcb(so); + oinp = sotoinpcb(lso); inp->inp_inc.inc_fibnum = so->so_fibnum; INP_WLOCK(inp); INP_HASH_WLOCK(&V_tcbinfo); @@ -755,7 +758,6 @@ syncache_socket(struct syncache *sc, str #endif #ifdef INET6 if (sc->sc_inc.inc_flags & INC_ISIPV6) { - struct inpcb *oinp = sotoinpcb(lso); struct in6_addr laddr6; struct sockaddr_in6 sin6; /* @@ -868,6 +870,10 @@ syncache_socket(struct syncache *sc, str if (sc->sc_flags & SCF_SIGNATURE) tp->t_flags |= TF_SIGNATURE; #endif + if (sc->sc_flags & SCF_AO) { + tp->t_flags |= TF_AO; + tcp_ao_sc_copy(sototcpcb(lso), tp, &sc->sc_inc); + } if (sc->sc_flags & SCF_SACK) tp->t_flags |= TF_SACK_PERMIT; } @@ -1115,6 +1121,7 @@ syncache_add(struct in_conninfo *inc, st struct label *maclabel; #endif struct syncache scs; + struct tcp_ao_key tkey; struct ucred *cred; INP_INFO_WLOCK_ASSERT(&V_tcbinfo); @@ -1139,7 +1146,10 @@ syncache_add(struct in_conninfo *inc, st ip_tos = inp->inp_ip_tos; win = sbspace(&so->so_rcv); sb_hiwat = so->so_rcv.sb_hiwat; - ltflags = (tp->t_flags & (TF_NOOPT | TF_SIGNATURE)); + ltflags = (tp->t_flags & (TF_NOOPT | TF_SIGNATURE | TF_AO)); + + /* Find the TCP-AO master key for this peer, if any. */ + (void)tcp_ao_sc_findmkey(tp, inc, to, &tkey); /* By the time we drop the lock these should no longer be used. */ so = NULL; @@ -1156,6 +1166,11 @@ syncache_add(struct in_conninfo *inc, st INP_WUNLOCK(inp); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***