svn commit: r257866 - user/andre/tcp-ao/sys/netinet
Andre Oppermann
andre at FreeBSD.org
Fri Nov 8 21:03:49 UTC 2013
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 <sys/jail.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/route.h>
#include <net/vnet.h>
@@ -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 <sys/md5.h>
+#include <crypto/sha1.h>
+#include <crypto/cmac/cmac.h>
+#include <crypto/hmac/hmac.h>
+
/*
* 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 <netinet6/tcp6_var.h>
#include <netinet/tcpip.h>
#include <netinet/tcp_syncache.h>
+#include <netinet/tcp_ao.h>
#ifdef TCPDEBUG
#include <netinet/tcp_debug.h>
#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 <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcpip.h>
+#include <netinet/tcp_ao.h>
#ifdef TCPDEBUG
#include <netinet/tcp_debug.h>
#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 <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_syncache.h>
+#include <netinet/tcp_ao.h>
#ifdef INET6
#include <netinet6/tcp6_var.h>
#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 <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_syncache.h>
+#include <netinet/tcp_ao.h>
#ifdef INET6
#include <netinet6/tcp6_var.h>
#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 ***
More information about the svn-src-user
mailing list