14954f17dSDmitry Safonov // SPDX-License-Identifier: GPL-2.0-or-later 24954f17dSDmitry Safonov /* 34954f17dSDmitry Safonov * INET An implementation of the TCP Authentication Option (TCP-AO). 44954f17dSDmitry Safonov * See RFC5925. 54954f17dSDmitry Safonov * 64954f17dSDmitry Safonov * Authors: Dmitry Safonov <dima@arista.com> 74954f17dSDmitry Safonov * Francesco Ruggeri <fruggeri@arista.com> 84954f17dSDmitry Safonov * Salam Noureddine <noureddine@arista.com> 94954f17dSDmitry Safonov */ 104954f17dSDmitry Safonov #define pr_fmt(fmt) "TCP: " fmt 114954f17dSDmitry Safonov 124954f17dSDmitry Safonov #include <crypto/hash.h> 134954f17dSDmitry Safonov #include <linux/inetdevice.h> 144954f17dSDmitry Safonov #include <linux/tcp.h> 154954f17dSDmitry Safonov 164954f17dSDmitry Safonov #include <net/tcp.h> 174954f17dSDmitry Safonov #include <net/ipv6.h> 184954f17dSDmitry Safonov 19*7c2ffaf2SDmitry Safonov int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, 20*7c2ffaf2SDmitry Safonov unsigned int len, struct tcp_sigpool *hp) 21*7c2ffaf2SDmitry Safonov { 22*7c2ffaf2SDmitry Safonov struct scatterlist sg; 23*7c2ffaf2SDmitry Safonov int ret; 24*7c2ffaf2SDmitry Safonov 25*7c2ffaf2SDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp->req), 26*7c2ffaf2SDmitry Safonov mkt->key, mkt->keylen)) 27*7c2ffaf2SDmitry Safonov goto clear_hash; 28*7c2ffaf2SDmitry Safonov 29*7c2ffaf2SDmitry Safonov ret = crypto_ahash_init(hp->req); 30*7c2ffaf2SDmitry Safonov if (ret) 31*7c2ffaf2SDmitry Safonov goto clear_hash; 32*7c2ffaf2SDmitry Safonov 33*7c2ffaf2SDmitry Safonov sg_init_one(&sg, ctx, len); 34*7c2ffaf2SDmitry Safonov ahash_request_set_crypt(hp->req, &sg, key, len); 35*7c2ffaf2SDmitry Safonov crypto_ahash_update(hp->req); 36*7c2ffaf2SDmitry Safonov 37*7c2ffaf2SDmitry Safonov ret = crypto_ahash_final(hp->req); 38*7c2ffaf2SDmitry Safonov if (ret) 39*7c2ffaf2SDmitry Safonov goto clear_hash; 40*7c2ffaf2SDmitry Safonov 41*7c2ffaf2SDmitry Safonov return 0; 42*7c2ffaf2SDmitry Safonov clear_hash: 43*7c2ffaf2SDmitry Safonov memset(key, 0, tcp_ao_digest_size(mkt)); 44*7c2ffaf2SDmitry Safonov return 1; 45*7c2ffaf2SDmitry Safonov } 46*7c2ffaf2SDmitry Safonov 474954f17dSDmitry Safonov /* Optimized version of tcp_ao_do_lookup(): only for sockets for which 484954f17dSDmitry Safonov * it's known that the keys in ao_info are matching peer's 494954f17dSDmitry Safonov * family/address/VRF/etc. 504954f17dSDmitry Safonov */ 514954f17dSDmitry Safonov static struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao, 524954f17dSDmitry Safonov int sndid, int rcvid) 534954f17dSDmitry Safonov { 544954f17dSDmitry Safonov struct tcp_ao_key *key; 554954f17dSDmitry Safonov 564954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 574954f17dSDmitry Safonov if ((sndid >= 0 && key->sndid != sndid) || 584954f17dSDmitry Safonov (rcvid >= 0 && key->rcvid != rcvid)) 594954f17dSDmitry Safonov continue; 604954f17dSDmitry Safonov return key; 614954f17dSDmitry Safonov } 624954f17dSDmitry Safonov 634954f17dSDmitry Safonov return NULL; 644954f17dSDmitry Safonov } 654954f17dSDmitry Safonov 664954f17dSDmitry Safonov static int ipv4_prefix_cmp(const struct in_addr *addr1, 674954f17dSDmitry Safonov const struct in_addr *addr2, 684954f17dSDmitry Safonov unsigned int prefixlen) 694954f17dSDmitry Safonov { 704954f17dSDmitry Safonov __be32 mask = inet_make_mask(prefixlen); 714954f17dSDmitry Safonov __be32 a1 = addr1->s_addr & mask; 724954f17dSDmitry Safonov __be32 a2 = addr2->s_addr & mask; 734954f17dSDmitry Safonov 744954f17dSDmitry Safonov if (a1 == a2) 754954f17dSDmitry Safonov return 0; 764954f17dSDmitry Safonov return memcmp(&a1, &a2, sizeof(a1)); 774954f17dSDmitry Safonov } 784954f17dSDmitry Safonov 794954f17dSDmitry Safonov static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, 804954f17dSDmitry Safonov const union tcp_ao_addr *addr, u8 prefixlen, 814954f17dSDmitry Safonov int family, int sndid, int rcvid) 824954f17dSDmitry Safonov { 834954f17dSDmitry Safonov if (sndid >= 0 && key->sndid != sndid) 844954f17dSDmitry Safonov return (key->sndid > sndid) ? 1 : -1; 854954f17dSDmitry Safonov if (rcvid >= 0 && key->rcvid != rcvid) 864954f17dSDmitry Safonov return (key->rcvid > rcvid) ? 1 : -1; 874954f17dSDmitry Safonov 884954f17dSDmitry Safonov if (family == AF_UNSPEC) 894954f17dSDmitry Safonov return 0; 904954f17dSDmitry Safonov if (key->family != family) 914954f17dSDmitry Safonov return (key->family > family) ? 1 : -1; 924954f17dSDmitry Safonov 934954f17dSDmitry Safonov if (family == AF_INET) { 944954f17dSDmitry Safonov if (ntohl(key->addr.a4.s_addr) == INADDR_ANY) 954954f17dSDmitry Safonov return 0; 964954f17dSDmitry Safonov if (ntohl(addr->a4.s_addr) == INADDR_ANY) 974954f17dSDmitry Safonov return 0; 984954f17dSDmitry Safonov return ipv4_prefix_cmp(&key->addr.a4, &addr->a4, prefixlen); 994954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 1004954f17dSDmitry Safonov } else { 1014954f17dSDmitry Safonov if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6)) 1024954f17dSDmitry Safonov return 0; 1034954f17dSDmitry Safonov if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen)) 1044954f17dSDmitry Safonov return 0; 1054954f17dSDmitry Safonov return memcmp(&key->addr.a6, &addr->a6, sizeof(addr->a6)); 1064954f17dSDmitry Safonov #endif 1074954f17dSDmitry Safonov } 1084954f17dSDmitry Safonov return -1; 1094954f17dSDmitry Safonov } 1104954f17dSDmitry Safonov 1114954f17dSDmitry Safonov static int tcp_ao_key_cmp(const struct tcp_ao_key *key, 1124954f17dSDmitry Safonov const union tcp_ao_addr *addr, u8 prefixlen, 1134954f17dSDmitry Safonov int family, int sndid, int rcvid) 1144954f17dSDmitry Safonov { 1154954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 1164954f17dSDmitry Safonov if (family == AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) { 1174954f17dSDmitry Safonov __be32 addr4 = addr->a6.s6_addr32[3]; 1184954f17dSDmitry Safonov 1194954f17dSDmitry Safonov return __tcp_ao_key_cmp(key, (union tcp_ao_addr *)&addr4, 1204954f17dSDmitry Safonov prefixlen, AF_INET, sndid, rcvid); 1214954f17dSDmitry Safonov } 1224954f17dSDmitry Safonov #endif 1234954f17dSDmitry Safonov return __tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid); 1244954f17dSDmitry Safonov } 1254954f17dSDmitry Safonov 1264954f17dSDmitry Safonov static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk, 1274954f17dSDmitry Safonov const union tcp_ao_addr *addr, int family, u8 prefix, 1284954f17dSDmitry Safonov int sndid, int rcvid) 1294954f17dSDmitry Safonov { 1304954f17dSDmitry Safonov struct tcp_ao_key *key; 1314954f17dSDmitry Safonov struct tcp_ao_info *ao; 1324954f17dSDmitry Safonov 1334954f17dSDmitry Safonov ao = rcu_dereference_check(tcp_sk(sk)->ao_info, 1344954f17dSDmitry Safonov lockdep_sock_is_held(sk)); 1354954f17dSDmitry Safonov if (!ao) 1364954f17dSDmitry Safonov return NULL; 1374954f17dSDmitry Safonov 1384954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 1394954f17dSDmitry Safonov u8 prefixlen = min(prefix, key->prefixlen); 1404954f17dSDmitry Safonov 1414954f17dSDmitry Safonov if (!tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid)) 1424954f17dSDmitry Safonov return key; 1434954f17dSDmitry Safonov } 1444954f17dSDmitry Safonov return NULL; 1454954f17dSDmitry Safonov } 1464954f17dSDmitry Safonov 1470aadc739SDmitry Safonov struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, 1480aadc739SDmitry Safonov const union tcp_ao_addr *addr, 1490aadc739SDmitry Safonov int family, int sndid, int rcvid) 1500aadc739SDmitry Safonov { 1510aadc739SDmitry Safonov return __tcp_ao_do_lookup(sk, addr, family, U8_MAX, sndid, rcvid); 1520aadc739SDmitry Safonov } 1530aadc739SDmitry Safonov 1544954f17dSDmitry Safonov static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags) 1554954f17dSDmitry Safonov { 1564954f17dSDmitry Safonov struct tcp_ao_info *ao; 1574954f17dSDmitry Safonov 1584954f17dSDmitry Safonov ao = kzalloc(sizeof(*ao), flags); 1594954f17dSDmitry Safonov if (!ao) 1604954f17dSDmitry Safonov return NULL; 1614954f17dSDmitry Safonov INIT_HLIST_HEAD(&ao->head); 1624954f17dSDmitry Safonov 1634954f17dSDmitry Safonov return ao; 1644954f17dSDmitry Safonov } 1654954f17dSDmitry Safonov 1664954f17dSDmitry Safonov static void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt) 1674954f17dSDmitry Safonov { 1684954f17dSDmitry Safonov hlist_add_head_rcu(&mkt->node, &ao->head); 1694954f17dSDmitry Safonov } 1704954f17dSDmitry Safonov 1714954f17dSDmitry Safonov static void tcp_ao_key_free_rcu(struct rcu_head *head) 1724954f17dSDmitry Safonov { 1734954f17dSDmitry Safonov struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu); 1744954f17dSDmitry Safonov 1754954f17dSDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 1764954f17dSDmitry Safonov kfree_sensitive(key); 1774954f17dSDmitry Safonov } 1784954f17dSDmitry Safonov 1794954f17dSDmitry Safonov void tcp_ao_destroy_sock(struct sock *sk) 1804954f17dSDmitry Safonov { 1814954f17dSDmitry Safonov struct tcp_ao_info *ao; 1824954f17dSDmitry Safonov struct tcp_ao_key *key; 1834954f17dSDmitry Safonov struct hlist_node *n; 1844954f17dSDmitry Safonov 1854954f17dSDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1); 1864954f17dSDmitry Safonov tcp_sk(sk)->ao_info = NULL; 1874954f17dSDmitry Safonov 1884954f17dSDmitry Safonov if (!ao) 1894954f17dSDmitry Safonov return; 1904954f17dSDmitry Safonov 1914954f17dSDmitry Safonov hlist_for_each_entry_safe(key, n, &ao->head, node) { 1924954f17dSDmitry Safonov hlist_del_rcu(&key->node); 1934954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 1944954f17dSDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 1954954f17dSDmitry Safonov } 1964954f17dSDmitry Safonov 1974954f17dSDmitry Safonov kfree_rcu(ao, rcu); 1984954f17dSDmitry Safonov } 1994954f17dSDmitry Safonov 200*7c2ffaf2SDmitry Safonov /* 4 tuple and ISNs are expected in NBO */ 201*7c2ffaf2SDmitry Safonov static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, 202*7c2ffaf2SDmitry Safonov __be32 saddr, __be32 daddr, 203*7c2ffaf2SDmitry Safonov __be16 sport, __be16 dport, 204*7c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn) 205*7c2ffaf2SDmitry Safonov { 206*7c2ffaf2SDmitry Safonov /* See RFC5926 3.1.1 */ 207*7c2ffaf2SDmitry Safonov struct kdf_input_block { 208*7c2ffaf2SDmitry Safonov u8 counter; 209*7c2ffaf2SDmitry Safonov u8 label[6]; 210*7c2ffaf2SDmitry Safonov struct tcp4_ao_context ctx; 211*7c2ffaf2SDmitry Safonov __be16 outlen; 212*7c2ffaf2SDmitry Safonov } __packed * tmp; 213*7c2ffaf2SDmitry Safonov struct tcp_sigpool hp; 214*7c2ffaf2SDmitry Safonov int err; 215*7c2ffaf2SDmitry Safonov 216*7c2ffaf2SDmitry Safonov err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp); 217*7c2ffaf2SDmitry Safonov if (err) 218*7c2ffaf2SDmitry Safonov return err; 219*7c2ffaf2SDmitry Safonov 220*7c2ffaf2SDmitry Safonov tmp = hp.scratch; 221*7c2ffaf2SDmitry Safonov tmp->counter = 1; 222*7c2ffaf2SDmitry Safonov memcpy(tmp->label, "TCP-AO", 6); 223*7c2ffaf2SDmitry Safonov tmp->ctx.saddr = saddr; 224*7c2ffaf2SDmitry Safonov tmp->ctx.daddr = daddr; 225*7c2ffaf2SDmitry Safonov tmp->ctx.sport = sport; 226*7c2ffaf2SDmitry Safonov tmp->ctx.dport = dport; 227*7c2ffaf2SDmitry Safonov tmp->ctx.sisn = sisn; 228*7c2ffaf2SDmitry Safonov tmp->ctx.disn = disn; 229*7c2ffaf2SDmitry Safonov tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ 230*7c2ffaf2SDmitry Safonov 231*7c2ffaf2SDmitry Safonov err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp); 232*7c2ffaf2SDmitry Safonov tcp_sigpool_end(&hp); 233*7c2ffaf2SDmitry Safonov 234*7c2ffaf2SDmitry Safonov return err; 235*7c2ffaf2SDmitry Safonov } 236*7c2ffaf2SDmitry Safonov 237*7c2ffaf2SDmitry Safonov int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, 238*7c2ffaf2SDmitry Safonov const struct sock *sk, 239*7c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn, bool send) 240*7c2ffaf2SDmitry Safonov { 241*7c2ffaf2SDmitry Safonov if (send) 242*7c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr, 243*7c2ffaf2SDmitry Safonov sk->sk_daddr, htons(sk->sk_num), 244*7c2ffaf2SDmitry Safonov sk->sk_dport, sisn, disn); 245*7c2ffaf2SDmitry Safonov else 246*7c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr, 247*7c2ffaf2SDmitry Safonov sk->sk_rcv_saddr, sk->sk_dport, 248*7c2ffaf2SDmitry Safonov htons(sk->sk_num), disn, sisn); 249*7c2ffaf2SDmitry Safonov } 250*7c2ffaf2SDmitry Safonov 251*7c2ffaf2SDmitry Safonov static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, 252*7c2ffaf2SDmitry Safonov const struct sock *sk, 253*7c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn, bool send) 254*7c2ffaf2SDmitry Safonov { 255*7c2ffaf2SDmitry Safonov if (mkt->family == AF_INET) 256*7c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); 257*7c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 258*7c2ffaf2SDmitry Safonov else if (mkt->family == AF_INET6) 259*7c2ffaf2SDmitry Safonov return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); 260*7c2ffaf2SDmitry Safonov #endif 261*7c2ffaf2SDmitry Safonov else 262*7c2ffaf2SDmitry Safonov return -EOPNOTSUPP; 263*7c2ffaf2SDmitry Safonov } 264*7c2ffaf2SDmitry Safonov 2650aadc739SDmitry Safonov struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, 2660aadc739SDmitry Safonov int sndid, int rcvid) 2670aadc739SDmitry Safonov { 2680aadc739SDmitry Safonov union tcp_ao_addr *addr = (union tcp_ao_addr *)&addr_sk->sk_daddr; 2690aadc739SDmitry Safonov 2700aadc739SDmitry Safonov return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid); 2710aadc739SDmitry Safonov } 2720aadc739SDmitry Safonov 273*7c2ffaf2SDmitry Safonov static int tcp_ao_cache_traffic_keys(const struct sock *sk, 274*7c2ffaf2SDmitry Safonov struct tcp_ao_info *ao, 275*7c2ffaf2SDmitry Safonov struct tcp_ao_key *ao_key) 276*7c2ffaf2SDmitry Safonov { 277*7c2ffaf2SDmitry Safonov u8 *traffic_key = snd_other_key(ao_key); 278*7c2ffaf2SDmitry Safonov int ret; 279*7c2ffaf2SDmitry Safonov 280*7c2ffaf2SDmitry Safonov ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk, 281*7c2ffaf2SDmitry Safonov ao->lisn, ao->risn, true); 282*7c2ffaf2SDmitry Safonov if (ret) 283*7c2ffaf2SDmitry Safonov return ret; 284*7c2ffaf2SDmitry Safonov 285*7c2ffaf2SDmitry Safonov traffic_key = rcv_other_key(ao_key); 286*7c2ffaf2SDmitry Safonov ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk, 287*7c2ffaf2SDmitry Safonov ao->lisn, ao->risn, false); 288*7c2ffaf2SDmitry Safonov return ret; 289*7c2ffaf2SDmitry Safonov } 290*7c2ffaf2SDmitry Safonov 291*7c2ffaf2SDmitry Safonov void tcp_ao_connect_init(struct sock *sk) 292*7c2ffaf2SDmitry Safonov { 293*7c2ffaf2SDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 294*7c2ffaf2SDmitry Safonov struct tcp_ao_info *ao_info; 295*7c2ffaf2SDmitry Safonov union tcp_ao_addr *addr; 296*7c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 297*7c2ffaf2SDmitry Safonov int family; 298*7c2ffaf2SDmitry Safonov 299*7c2ffaf2SDmitry Safonov ao_info = rcu_dereference_protected(tp->ao_info, 300*7c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 301*7c2ffaf2SDmitry Safonov if (!ao_info) 302*7c2ffaf2SDmitry Safonov return; 303*7c2ffaf2SDmitry Safonov 304*7c2ffaf2SDmitry Safonov /* Remove all keys that don't match the peer */ 305*7c2ffaf2SDmitry Safonov family = sk->sk_family; 306*7c2ffaf2SDmitry Safonov if (family == AF_INET) 307*7c2ffaf2SDmitry Safonov addr = (union tcp_ao_addr *)&sk->sk_daddr; 308*7c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 309*7c2ffaf2SDmitry Safonov else if (family == AF_INET6) 310*7c2ffaf2SDmitry Safonov addr = (union tcp_ao_addr *)&sk->sk_v6_daddr; 311*7c2ffaf2SDmitry Safonov #endif 312*7c2ffaf2SDmitry Safonov else 313*7c2ffaf2SDmitry Safonov return; 314*7c2ffaf2SDmitry Safonov 315*7c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 316*7c2ffaf2SDmitry Safonov if (!tcp_ao_key_cmp(key, addr, key->prefixlen, family, -1, -1)) 317*7c2ffaf2SDmitry Safonov continue; 318*7c2ffaf2SDmitry Safonov 319*7c2ffaf2SDmitry Safonov if (key == ao_info->current_key) 320*7c2ffaf2SDmitry Safonov ao_info->current_key = NULL; 321*7c2ffaf2SDmitry Safonov if (key == ao_info->rnext_key) 322*7c2ffaf2SDmitry Safonov ao_info->rnext_key = NULL; 323*7c2ffaf2SDmitry Safonov hlist_del_rcu(&key->node); 324*7c2ffaf2SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 325*7c2ffaf2SDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 326*7c2ffaf2SDmitry Safonov } 327*7c2ffaf2SDmitry Safonov 328*7c2ffaf2SDmitry Safonov key = tp->af_specific->ao_lookup(sk, sk, -1, -1); 329*7c2ffaf2SDmitry Safonov if (key) { 330*7c2ffaf2SDmitry Safonov /* if current_key or rnext_key were not provided, 331*7c2ffaf2SDmitry Safonov * use the first key matching the peer 332*7c2ffaf2SDmitry Safonov */ 333*7c2ffaf2SDmitry Safonov if (!ao_info->current_key) 334*7c2ffaf2SDmitry Safonov ao_info->current_key = key; 335*7c2ffaf2SDmitry Safonov if (!ao_info->rnext_key) 336*7c2ffaf2SDmitry Safonov ao_info->rnext_key = key; 337*7c2ffaf2SDmitry Safonov tp->tcp_header_len += tcp_ao_len(key); 338*7c2ffaf2SDmitry Safonov 339*7c2ffaf2SDmitry Safonov ao_info->lisn = htonl(tp->write_seq); 340*7c2ffaf2SDmitry Safonov } else { 341*7c2ffaf2SDmitry Safonov /* Can't happen: tcp_connect() verifies that there's 342*7c2ffaf2SDmitry Safonov * at least one tcp-ao key that matches the remote peer. 343*7c2ffaf2SDmitry Safonov */ 344*7c2ffaf2SDmitry Safonov WARN_ON_ONCE(1); 345*7c2ffaf2SDmitry Safonov rcu_assign_pointer(tp->ao_info, NULL); 346*7c2ffaf2SDmitry Safonov kfree(ao_info); 347*7c2ffaf2SDmitry Safonov } 348*7c2ffaf2SDmitry Safonov } 349*7c2ffaf2SDmitry Safonov 350*7c2ffaf2SDmitry Safonov void tcp_ao_established(struct sock *sk) 351*7c2ffaf2SDmitry Safonov { 352*7c2ffaf2SDmitry Safonov struct tcp_ao_info *ao; 353*7c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 354*7c2ffaf2SDmitry Safonov 355*7c2ffaf2SDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 356*7c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 357*7c2ffaf2SDmitry Safonov if (!ao) 358*7c2ffaf2SDmitry Safonov return; 359*7c2ffaf2SDmitry Safonov 360*7c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 361*7c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 362*7c2ffaf2SDmitry Safonov } 363*7c2ffaf2SDmitry Safonov 364*7c2ffaf2SDmitry Safonov void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb) 365*7c2ffaf2SDmitry Safonov { 366*7c2ffaf2SDmitry Safonov struct tcp_ao_info *ao; 367*7c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 368*7c2ffaf2SDmitry Safonov 369*7c2ffaf2SDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 370*7c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 371*7c2ffaf2SDmitry Safonov if (!ao) 372*7c2ffaf2SDmitry Safonov return; 373*7c2ffaf2SDmitry Safonov 374*7c2ffaf2SDmitry Safonov WRITE_ONCE(ao->risn, tcp_hdr(skb)->seq); 375*7c2ffaf2SDmitry Safonov 376*7c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 377*7c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 378*7c2ffaf2SDmitry Safonov } 379*7c2ffaf2SDmitry Safonov 3804954f17dSDmitry Safonov static bool tcp_ao_can_set_current_rnext(struct sock *sk) 3814954f17dSDmitry Safonov { 3824954f17dSDmitry Safonov /* There aren't current/rnext keys on TCP_LISTEN sockets */ 3834954f17dSDmitry Safonov if (sk->sk_state == TCP_LISTEN) 3844954f17dSDmitry Safonov return false; 3854954f17dSDmitry Safonov return true; 3864954f17dSDmitry Safonov } 3874954f17dSDmitry Safonov 3884954f17dSDmitry Safonov static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao_add *cmd, 3894954f17dSDmitry Safonov union tcp_ao_addr **addr) 3904954f17dSDmitry Safonov { 3914954f17dSDmitry Safonov struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->addr; 3924954f17dSDmitry Safonov struct inet_sock *inet = inet_sk(sk); 3934954f17dSDmitry Safonov 3944954f17dSDmitry Safonov if (sin->sin_family != AF_INET) 3954954f17dSDmitry Safonov return -EINVAL; 3964954f17dSDmitry Safonov 3974954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 3984954f17dSDmitry Safonov if (sin->sin_port != 0) 3994954f17dSDmitry Safonov return -EINVAL; 4004954f17dSDmitry Safonov 4014954f17dSDmitry Safonov /* Check prefix and trailing 0's in addr */ 4024954f17dSDmitry Safonov if (cmd->prefix != 0) { 4034954f17dSDmitry Safonov __be32 mask; 4044954f17dSDmitry Safonov 4054954f17dSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY) 4064954f17dSDmitry Safonov return -EINVAL; 4074954f17dSDmitry Safonov if (cmd->prefix > 32) 4084954f17dSDmitry Safonov return -EINVAL; 4094954f17dSDmitry Safonov 4104954f17dSDmitry Safonov mask = inet_make_mask(cmd->prefix); 4114954f17dSDmitry Safonov if (sin->sin_addr.s_addr & ~mask) 4124954f17dSDmitry Safonov return -EINVAL; 4134954f17dSDmitry Safonov 4144954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 4154954f17dSDmitry Safonov if (ntohl(inet->inet_daddr) != INADDR_ANY && 4164954f17dSDmitry Safonov (inet->inet_daddr & mask) != sin->sin_addr.s_addr) 4174954f17dSDmitry Safonov return -EINVAL; 4184954f17dSDmitry Safonov } else { 4194954f17dSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) != INADDR_ANY) 4204954f17dSDmitry Safonov return -EINVAL; 4214954f17dSDmitry Safonov } 4224954f17dSDmitry Safonov 4234954f17dSDmitry Safonov *addr = (union tcp_ao_addr *)&sin->sin_addr; 4244954f17dSDmitry Safonov return 0; 4254954f17dSDmitry Safonov } 4264954f17dSDmitry Safonov 4274954f17dSDmitry Safonov static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key) 4284954f17dSDmitry Safonov { 4294954f17dSDmitry Safonov unsigned int syn_tcp_option_space; 4304954f17dSDmitry Safonov bool is_kdf_aes_128_cmac = false; 4314954f17dSDmitry Safonov struct crypto_ahash *tfm; 4324954f17dSDmitry Safonov struct tcp_sigpool hp; 4334954f17dSDmitry Safonov void *tmp_key = NULL; 4344954f17dSDmitry Safonov int err; 4354954f17dSDmitry Safonov 4364954f17dSDmitry Safonov /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 4374954f17dSDmitry Safonov if (!strcmp("cmac(aes128)", cmd->alg_name)) { 4384954f17dSDmitry Safonov strscpy(cmd->alg_name, "cmac(aes)", sizeof(cmd->alg_name)); 4394954f17dSDmitry Safonov is_kdf_aes_128_cmac = (cmd->keylen != 16); 4404954f17dSDmitry Safonov tmp_key = kmalloc(cmd->keylen, GFP_KERNEL); 4414954f17dSDmitry Safonov if (!tmp_key) 4424954f17dSDmitry Safonov return -ENOMEM; 4434954f17dSDmitry Safonov } 4444954f17dSDmitry Safonov 4454954f17dSDmitry Safonov key->maclen = cmd->maclen ?: 12; /* 12 is the default in RFC5925 */ 4464954f17dSDmitry Safonov 4474954f17dSDmitry Safonov /* Check: maclen + tcp-ao header <= (MAX_TCP_OPTION_SPACE - mss 4484954f17dSDmitry Safonov * - tstamp - wscale - sackperm), 4494954f17dSDmitry Safonov * see tcp_syn_options(), tcp_synack_options(), commit 33ad798c924b. 4504954f17dSDmitry Safonov * 4514954f17dSDmitry Safonov * In order to allow D-SACK with TCP-AO, the header size should be: 4524954f17dSDmitry Safonov * (MAX_TCP_OPTION_SPACE - TCPOLEN_TSTAMP_ALIGNED 4534954f17dSDmitry Safonov * - TCPOLEN_SACK_BASE_ALIGNED 4544954f17dSDmitry Safonov * - 2 * TCPOLEN_SACK_PERBLOCK) = 8 (maclen = 4), 4554954f17dSDmitry Safonov * see tcp_established_options(). 4564954f17dSDmitry Safonov * 4574954f17dSDmitry Safonov * RFC5925, 2.2: 4584954f17dSDmitry Safonov * Typical MACs are 96-128 bits (12-16 bytes), but any length 4594954f17dSDmitry Safonov * that fits in the header of the segment being authenticated 4604954f17dSDmitry Safonov * is allowed. 4614954f17dSDmitry Safonov * 4624954f17dSDmitry Safonov * RFC5925, 7.6: 4634954f17dSDmitry Safonov * TCP-AO continues to consume 16 bytes in non-SYN segments, 4644954f17dSDmitry Safonov * leaving a total of 24 bytes for other options, of which 4654954f17dSDmitry Safonov * the timestamp consumes 10. This leaves 14 bytes, of which 10 4664954f17dSDmitry Safonov * are used for a single SACK block. When two SACK blocks are used, 4674954f17dSDmitry Safonov * such as to handle D-SACK, a smaller TCP-AO MAC would be required 4684954f17dSDmitry Safonov * to make room for the additional SACK block (i.e., to leave 18 4694954f17dSDmitry Safonov * bytes for the D-SACK variant of the SACK option) [RFC2883]. 4704954f17dSDmitry Safonov * Note that D-SACK is not supportable in TCP MD5 in the presence 4714954f17dSDmitry Safonov * of timestamps, because TCP MD5’s MAC length is fixed and too 4724954f17dSDmitry Safonov * large to leave sufficient option space. 4734954f17dSDmitry Safonov */ 4744954f17dSDmitry Safonov syn_tcp_option_space = MAX_TCP_OPTION_SPACE; 4754954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED; 4764954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED; 4774954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_SACKPERM_ALIGNED; 4784954f17dSDmitry Safonov if (tcp_ao_len(key) > syn_tcp_option_space) { 4794954f17dSDmitry Safonov err = -EMSGSIZE; 4804954f17dSDmitry Safonov goto err_kfree; 4814954f17dSDmitry Safonov } 4824954f17dSDmitry Safonov 4834954f17dSDmitry Safonov key->keylen = cmd->keylen; 4844954f17dSDmitry Safonov memcpy(key->key, cmd->key, cmd->keylen); 4854954f17dSDmitry Safonov 4864954f17dSDmitry Safonov err = tcp_sigpool_start(key->tcp_sigpool_id, &hp); 4874954f17dSDmitry Safonov if (err) 4884954f17dSDmitry Safonov goto err_kfree; 4894954f17dSDmitry Safonov 4904954f17dSDmitry Safonov tfm = crypto_ahash_reqtfm(hp.req); 4914954f17dSDmitry Safonov if (is_kdf_aes_128_cmac) { 4924954f17dSDmitry Safonov void *scratch = hp.scratch; 4934954f17dSDmitry Safonov struct scatterlist sg; 4944954f17dSDmitry Safonov 4954954f17dSDmitry Safonov memcpy(tmp_key, cmd->key, cmd->keylen); 4964954f17dSDmitry Safonov sg_init_one(&sg, tmp_key, cmd->keylen); 4974954f17dSDmitry Safonov 4984954f17dSDmitry Safonov /* Using zero-key of 16 bytes as described in RFC5926 */ 4994954f17dSDmitry Safonov memset(scratch, 0, 16); 5004954f17dSDmitry Safonov err = crypto_ahash_setkey(tfm, scratch, 16); 5014954f17dSDmitry Safonov if (err) 5024954f17dSDmitry Safonov goto err_pool_end; 5034954f17dSDmitry Safonov 5044954f17dSDmitry Safonov err = crypto_ahash_init(hp.req); 5054954f17dSDmitry Safonov if (err) 5064954f17dSDmitry Safonov goto err_pool_end; 5074954f17dSDmitry Safonov 5084954f17dSDmitry Safonov ahash_request_set_crypt(hp.req, &sg, key->key, cmd->keylen); 5094954f17dSDmitry Safonov err = crypto_ahash_update(hp.req); 5104954f17dSDmitry Safonov if (err) 5114954f17dSDmitry Safonov goto err_pool_end; 5124954f17dSDmitry Safonov 5134954f17dSDmitry Safonov err |= crypto_ahash_final(hp.req); 5144954f17dSDmitry Safonov if (err) 5154954f17dSDmitry Safonov goto err_pool_end; 5164954f17dSDmitry Safonov key->keylen = 16; 5174954f17dSDmitry Safonov } 5184954f17dSDmitry Safonov 5194954f17dSDmitry Safonov err = crypto_ahash_setkey(tfm, key->key, key->keylen); 5204954f17dSDmitry Safonov if (err) 5214954f17dSDmitry Safonov goto err_pool_end; 5224954f17dSDmitry Safonov 5234954f17dSDmitry Safonov tcp_sigpool_end(&hp); 5244954f17dSDmitry Safonov kfree_sensitive(tmp_key); 5254954f17dSDmitry Safonov 5264954f17dSDmitry Safonov if (tcp_ao_maclen(key) > key->digest_size) 5274954f17dSDmitry Safonov return -EINVAL; 5284954f17dSDmitry Safonov 5294954f17dSDmitry Safonov return 0; 5304954f17dSDmitry Safonov 5314954f17dSDmitry Safonov err_pool_end: 5324954f17dSDmitry Safonov tcp_sigpool_end(&hp); 5334954f17dSDmitry Safonov err_kfree: 5344954f17dSDmitry Safonov kfree_sensitive(tmp_key); 5354954f17dSDmitry Safonov return err; 5364954f17dSDmitry Safonov } 5374954f17dSDmitry Safonov 5384954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 5394954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 5404954f17dSDmitry Safonov union tcp_ao_addr **paddr, 5414954f17dSDmitry Safonov unsigned short int *family) 5424954f17dSDmitry Safonov { 5434954f17dSDmitry Safonov struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->addr; 5444954f17dSDmitry Safonov struct in6_addr *addr = &sin6->sin6_addr; 5454954f17dSDmitry Safonov u8 prefix = cmd->prefix; 5464954f17dSDmitry Safonov 5474954f17dSDmitry Safonov if (sin6->sin6_family != AF_INET6) 5484954f17dSDmitry Safonov return -EINVAL; 5494954f17dSDmitry Safonov 5504954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 5514954f17dSDmitry Safonov if (sin6->sin6_port != 0) 5524954f17dSDmitry Safonov return -EINVAL; 5534954f17dSDmitry Safonov 5544954f17dSDmitry Safonov /* Check prefix and trailing 0's in addr */ 5554954f17dSDmitry Safonov if (cmd->prefix != 0 && ipv6_addr_v4mapped(addr)) { 5564954f17dSDmitry Safonov __be32 addr4 = addr->s6_addr32[3]; 5574954f17dSDmitry Safonov __be32 mask; 5584954f17dSDmitry Safonov 5594954f17dSDmitry Safonov if (prefix > 32 || ntohl(addr4) == INADDR_ANY) 5604954f17dSDmitry Safonov return -EINVAL; 5614954f17dSDmitry Safonov 5624954f17dSDmitry Safonov mask = inet_make_mask(prefix); 5634954f17dSDmitry Safonov if (addr4 & ~mask) 5644954f17dSDmitry Safonov return -EINVAL; 5654954f17dSDmitry Safonov 5664954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 5674954f17dSDmitry Safonov if (!ipv6_addr_any(&sk->sk_v6_daddr)) { 5684954f17dSDmitry Safonov __be32 daddr4 = sk->sk_v6_daddr.s6_addr32[3]; 5694954f17dSDmitry Safonov 5704954f17dSDmitry Safonov if (!ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 5714954f17dSDmitry Safonov return -EINVAL; 5724954f17dSDmitry Safonov if ((daddr4 & mask) != addr4) 5734954f17dSDmitry Safonov return -EINVAL; 5744954f17dSDmitry Safonov } 5754954f17dSDmitry Safonov 5764954f17dSDmitry Safonov *paddr = (union tcp_ao_addr *)&addr->s6_addr32[3]; 5774954f17dSDmitry Safonov *family = AF_INET; 5784954f17dSDmitry Safonov return 0; 5794954f17dSDmitry Safonov } else if (cmd->prefix != 0) { 5804954f17dSDmitry Safonov struct in6_addr pfx; 5814954f17dSDmitry Safonov 5824954f17dSDmitry Safonov if (ipv6_addr_any(addr) || prefix > 128) 5834954f17dSDmitry Safonov return -EINVAL; 5844954f17dSDmitry Safonov 5854954f17dSDmitry Safonov ipv6_addr_prefix(&pfx, addr, prefix); 5864954f17dSDmitry Safonov if (ipv6_addr_cmp(&pfx, addr)) 5874954f17dSDmitry Safonov return -EINVAL; 5884954f17dSDmitry Safonov 5894954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 5904954f17dSDmitry Safonov if (!ipv6_addr_any(&sk->sk_v6_daddr) && 5914954f17dSDmitry Safonov !ipv6_prefix_equal(&sk->sk_v6_daddr, addr, prefix)) 5924954f17dSDmitry Safonov 5934954f17dSDmitry Safonov return -EINVAL; 5944954f17dSDmitry Safonov } else { 5954954f17dSDmitry Safonov if (!ipv6_addr_any(addr)) 5964954f17dSDmitry Safonov return -EINVAL; 5974954f17dSDmitry Safonov } 5984954f17dSDmitry Safonov 5994954f17dSDmitry Safonov *paddr = (union tcp_ao_addr *)addr; 6004954f17dSDmitry Safonov return 0; 6014954f17dSDmitry Safonov } 6024954f17dSDmitry Safonov #else 6034954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 6044954f17dSDmitry Safonov union tcp_ao_addr **paddr, 6054954f17dSDmitry Safonov unsigned short int *family) 6064954f17dSDmitry Safonov { 6074954f17dSDmitry Safonov return -EOPNOTSUPP; 6084954f17dSDmitry Safonov } 6094954f17dSDmitry Safonov #endif 6104954f17dSDmitry Safonov 6114954f17dSDmitry Safonov static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) 6124954f17dSDmitry Safonov { 6134954f17dSDmitry Safonov if (sk_fullsock(sk)) { 6144954f17dSDmitry Safonov return rcu_dereference_protected(tcp_sk(sk)->ao_info, 6154954f17dSDmitry Safonov lockdep_sock_is_held(sk)); 6164954f17dSDmitry Safonov } 6174954f17dSDmitry Safonov return ERR_PTR(-ESOCKTNOSUPPORT); 6184954f17dSDmitry Safonov } 6194954f17dSDmitry Safonov 6204954f17dSDmitry Safonov #define TCP_AO_KEYF_ALL (0) 6214954f17dSDmitry Safonov 6224954f17dSDmitry Safonov static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk, 6234954f17dSDmitry Safonov struct tcp_ao_add *cmd) 6244954f17dSDmitry Safonov { 6254954f17dSDmitry Safonov const char *algo = cmd->alg_name; 6264954f17dSDmitry Safonov unsigned int digest_size; 6274954f17dSDmitry Safonov struct crypto_ahash *tfm; 6284954f17dSDmitry Safonov struct tcp_ao_key *key; 6294954f17dSDmitry Safonov struct tcp_sigpool hp; 6304954f17dSDmitry Safonov int err, pool_id; 6314954f17dSDmitry Safonov size_t size; 6324954f17dSDmitry Safonov 6334954f17dSDmitry Safonov /* Force null-termination of alg_name */ 6344954f17dSDmitry Safonov cmd->alg_name[ARRAY_SIZE(cmd->alg_name) - 1] = '\0'; 6354954f17dSDmitry Safonov 6364954f17dSDmitry Safonov /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 6374954f17dSDmitry Safonov if (!strcmp("cmac(aes128)", algo)) 6384954f17dSDmitry Safonov algo = "cmac(aes)"; 6394954f17dSDmitry Safonov 6404954f17dSDmitry Safonov /* Full TCP header (th->doff << 2) should fit into scratch area, 6414954f17dSDmitry Safonov * see tcp_ao_hash_header(). 6424954f17dSDmitry Safonov */ 6434954f17dSDmitry Safonov pool_id = tcp_sigpool_alloc_ahash(algo, 60); 6444954f17dSDmitry Safonov if (pool_id < 0) 6454954f17dSDmitry Safonov return ERR_PTR(pool_id); 6464954f17dSDmitry Safonov 6474954f17dSDmitry Safonov err = tcp_sigpool_start(pool_id, &hp); 6484954f17dSDmitry Safonov if (err) 6494954f17dSDmitry Safonov goto err_free_pool; 6504954f17dSDmitry Safonov 6514954f17dSDmitry Safonov tfm = crypto_ahash_reqtfm(hp.req); 6524954f17dSDmitry Safonov if (crypto_ahash_alignmask(tfm) > TCP_AO_KEY_ALIGN) { 6534954f17dSDmitry Safonov err = -EOPNOTSUPP; 6544954f17dSDmitry Safonov goto err_pool_end; 6554954f17dSDmitry Safonov } 6564954f17dSDmitry Safonov digest_size = crypto_ahash_digestsize(tfm); 6574954f17dSDmitry Safonov tcp_sigpool_end(&hp); 6584954f17dSDmitry Safonov 6594954f17dSDmitry Safonov size = sizeof(struct tcp_ao_key) + (digest_size << 1); 6604954f17dSDmitry Safonov key = sock_kmalloc(sk, size, GFP_KERNEL); 6614954f17dSDmitry Safonov if (!key) { 6624954f17dSDmitry Safonov err = -ENOMEM; 6634954f17dSDmitry Safonov goto err_free_pool; 6644954f17dSDmitry Safonov } 6654954f17dSDmitry Safonov 6664954f17dSDmitry Safonov key->tcp_sigpool_id = pool_id; 6674954f17dSDmitry Safonov key->digest_size = digest_size; 6684954f17dSDmitry Safonov return key; 6694954f17dSDmitry Safonov 6704954f17dSDmitry Safonov err_pool_end: 6714954f17dSDmitry Safonov tcp_sigpool_end(&hp); 6724954f17dSDmitry Safonov err_free_pool: 6734954f17dSDmitry Safonov tcp_sigpool_release(pool_id); 6744954f17dSDmitry Safonov return ERR_PTR(err); 6754954f17dSDmitry Safonov } 6764954f17dSDmitry Safonov 6774954f17dSDmitry Safonov static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, 6784954f17dSDmitry Safonov sockptr_t optval, int optlen) 6794954f17dSDmitry Safonov { 6804954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 6814954f17dSDmitry Safonov union tcp_ao_addr *addr; 6824954f17dSDmitry Safonov struct tcp_ao_key *key; 6834954f17dSDmitry Safonov struct tcp_ao_add cmd; 6844954f17dSDmitry Safonov bool first = false; 6854954f17dSDmitry Safonov int ret; 6864954f17dSDmitry Safonov 6874954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 6884954f17dSDmitry Safonov return -EINVAL; 6894954f17dSDmitry Safonov 6904954f17dSDmitry Safonov ret = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 6914954f17dSDmitry Safonov if (ret) 6924954f17dSDmitry Safonov return ret; 6934954f17dSDmitry Safonov 6944954f17dSDmitry Safonov if (cmd.keylen > TCP_AO_MAXKEYLEN) 6954954f17dSDmitry Safonov return -EINVAL; 6964954f17dSDmitry Safonov 6974954f17dSDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 6984954f17dSDmitry Safonov return -EINVAL; 6994954f17dSDmitry Safonov 7004954f17dSDmitry Safonov if (family == AF_INET) 7014954f17dSDmitry Safonov ret = tcp_ao_verify_ipv4(sk, &cmd, &addr); 7024954f17dSDmitry Safonov else 7034954f17dSDmitry Safonov ret = tcp_ao_verify_ipv6(sk, &cmd, &addr, &family); 7044954f17dSDmitry Safonov if (ret) 7054954f17dSDmitry Safonov return ret; 7064954f17dSDmitry Safonov 7074954f17dSDmitry Safonov if (cmd.keyflags & ~TCP_AO_KEYF_ALL) 7084954f17dSDmitry Safonov return -EINVAL; 7094954f17dSDmitry Safonov 7104954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 7114954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 7124954f17dSDmitry Safonov return -EINVAL; 7134954f17dSDmitry Safonov } 7144954f17dSDmitry Safonov 7150aadc739SDmitry Safonov /* Don't allow keys for peers that have a matching TCP-MD5 key */ 7160aadc739SDmitry Safonov if (tcp_md5_do_lookup_any_l3index(sk, addr, family)) 7170aadc739SDmitry Safonov return -EKEYREJECTED; 7180aadc739SDmitry Safonov 7194954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 7204954f17dSDmitry Safonov if (IS_ERR(ao_info)) 7214954f17dSDmitry Safonov return PTR_ERR(ao_info); 7224954f17dSDmitry Safonov 7234954f17dSDmitry Safonov if (!ao_info) { 7244954f17dSDmitry Safonov ao_info = tcp_ao_alloc_info(GFP_KERNEL); 7254954f17dSDmitry Safonov if (!ao_info) 7264954f17dSDmitry Safonov return -ENOMEM; 7274954f17dSDmitry Safonov first = true; 7284954f17dSDmitry Safonov } else { 7294954f17dSDmitry Safonov /* Check that neither RecvID nor SendID match any 7304954f17dSDmitry Safonov * existing key for the peer, RFC5925 3.1: 7314954f17dSDmitry Safonov * > The IDs of MKTs MUST NOT overlap where their 7324954f17dSDmitry Safonov * > TCP connection identifiers overlap. 7334954f17dSDmitry Safonov */ 7344954f17dSDmitry Safonov if (__tcp_ao_do_lookup(sk, addr, family, 7354954f17dSDmitry Safonov cmd.prefix, -1, cmd.rcvid)) 7364954f17dSDmitry Safonov return -EEXIST; 7374954f17dSDmitry Safonov if (__tcp_ao_do_lookup(sk, addr, family, 7384954f17dSDmitry Safonov cmd.prefix, cmd.sndid, -1)) 7394954f17dSDmitry Safonov return -EEXIST; 7404954f17dSDmitry Safonov } 7414954f17dSDmitry Safonov 7424954f17dSDmitry Safonov key = tcp_ao_key_alloc(sk, &cmd); 7434954f17dSDmitry Safonov if (IS_ERR(key)) { 7444954f17dSDmitry Safonov ret = PTR_ERR(key); 7454954f17dSDmitry Safonov goto err_free_ao; 7464954f17dSDmitry Safonov } 7474954f17dSDmitry Safonov 7484954f17dSDmitry Safonov INIT_HLIST_NODE(&key->node); 7494954f17dSDmitry Safonov memcpy(&key->addr, addr, (family == AF_INET) ? sizeof(struct in_addr) : 7504954f17dSDmitry Safonov sizeof(struct in6_addr)); 7514954f17dSDmitry Safonov key->prefixlen = cmd.prefix; 7524954f17dSDmitry Safonov key->family = family; 7534954f17dSDmitry Safonov key->keyflags = cmd.keyflags; 7544954f17dSDmitry Safonov key->sndid = cmd.sndid; 7554954f17dSDmitry Safonov key->rcvid = cmd.rcvid; 7564954f17dSDmitry Safonov 7574954f17dSDmitry Safonov ret = tcp_ao_parse_crypto(&cmd, key); 7584954f17dSDmitry Safonov if (ret < 0) 7594954f17dSDmitry Safonov goto err_free_sock; 7604954f17dSDmitry Safonov 761*7c2ffaf2SDmitry Safonov /* Change this condition if we allow adding keys in states 762*7c2ffaf2SDmitry Safonov * like close_wait, syn_sent or fin_wait... 763*7c2ffaf2SDmitry Safonov */ 764*7c2ffaf2SDmitry Safonov if (sk->sk_state == TCP_ESTABLISHED) 765*7c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao_info, key); 766*7c2ffaf2SDmitry Safonov 7674954f17dSDmitry Safonov tcp_ao_link_mkt(ao_info, key); 7684954f17dSDmitry Safonov if (first) { 7694954f17dSDmitry Safonov sk_gso_disable(sk); 7704954f17dSDmitry Safonov rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 7714954f17dSDmitry Safonov } 7724954f17dSDmitry Safonov 7734954f17dSDmitry Safonov if (cmd.set_current) 7744954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, key); 7754954f17dSDmitry Safonov if (cmd.set_rnext) 7764954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, key); 7774954f17dSDmitry Safonov return 0; 7784954f17dSDmitry Safonov 7794954f17dSDmitry Safonov err_free_sock: 7804954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 7814954f17dSDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 7824954f17dSDmitry Safonov kfree_sensitive(key); 7834954f17dSDmitry Safonov err_free_ao: 7844954f17dSDmitry Safonov if (first) 7854954f17dSDmitry Safonov kfree(ao_info); 7864954f17dSDmitry Safonov return ret; 7874954f17dSDmitry Safonov } 7884954f17dSDmitry Safonov 7894954f17dSDmitry Safonov static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, 7904954f17dSDmitry Safonov struct tcp_ao_key *key, 7914954f17dSDmitry Safonov struct tcp_ao_key *new_current, 7924954f17dSDmitry Safonov struct tcp_ao_key *new_rnext) 7934954f17dSDmitry Safonov { 7944954f17dSDmitry Safonov int err; 7954954f17dSDmitry Safonov 7964954f17dSDmitry Safonov hlist_del_rcu(&key->node); 7974954f17dSDmitry Safonov 7984954f17dSDmitry Safonov /* At this moment another CPU could have looked this key up 7994954f17dSDmitry Safonov * while it was unlinked from the list. Wait for RCU grace period, 8004954f17dSDmitry Safonov * after which the key is off-list and can't be looked up again; 8014954f17dSDmitry Safonov * the rx path [just before RCU came] might have used it and set it 8024954f17dSDmitry Safonov * as current_key (very unlikely). 8034954f17dSDmitry Safonov */ 8044954f17dSDmitry Safonov synchronize_rcu(); 8054954f17dSDmitry Safonov if (new_current) 8064954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, new_current); 8074954f17dSDmitry Safonov if (new_rnext) 8084954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, new_rnext); 8094954f17dSDmitry Safonov 8104954f17dSDmitry Safonov if (unlikely(READ_ONCE(ao_info->current_key) == key || 8114954f17dSDmitry Safonov READ_ONCE(ao_info->rnext_key) == key)) { 8124954f17dSDmitry Safonov err = -EBUSY; 8134954f17dSDmitry Safonov goto add_key; 8144954f17dSDmitry Safonov } 8154954f17dSDmitry Safonov 8164954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 8174954f17dSDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 8184954f17dSDmitry Safonov 8194954f17dSDmitry Safonov return 0; 8204954f17dSDmitry Safonov add_key: 8214954f17dSDmitry Safonov hlist_add_head_rcu(&key->node, &ao_info->head); 8224954f17dSDmitry Safonov return err; 8234954f17dSDmitry Safonov } 8244954f17dSDmitry Safonov 8254954f17dSDmitry Safonov static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, 8264954f17dSDmitry Safonov sockptr_t optval, int optlen) 8274954f17dSDmitry Safonov { 8284954f17dSDmitry Safonov struct tcp_ao_key *key, *new_current = NULL, *new_rnext = NULL; 8294954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 8304954f17dSDmitry Safonov union tcp_ao_addr *addr; 8314954f17dSDmitry Safonov struct tcp_ao_del cmd; 8324954f17dSDmitry Safonov int addr_len; 8334954f17dSDmitry Safonov __u8 prefix; 8344954f17dSDmitry Safonov u16 port; 8354954f17dSDmitry Safonov int err; 8364954f17dSDmitry Safonov 8374954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 8384954f17dSDmitry Safonov return -EINVAL; 8394954f17dSDmitry Safonov 8404954f17dSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 8414954f17dSDmitry Safonov if (err) 8424954f17dSDmitry Safonov return err; 8434954f17dSDmitry Safonov 8444954f17dSDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 8454954f17dSDmitry Safonov return -EINVAL; 8464954f17dSDmitry Safonov 8474954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 8484954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 8494954f17dSDmitry Safonov return -EINVAL; 8504954f17dSDmitry Safonov } 8514954f17dSDmitry Safonov 8524954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 8534954f17dSDmitry Safonov if (IS_ERR(ao_info)) 8544954f17dSDmitry Safonov return PTR_ERR(ao_info); 8554954f17dSDmitry Safonov if (!ao_info) 8564954f17dSDmitry Safonov return -ENOENT; 8574954f17dSDmitry Safonov 8584954f17dSDmitry Safonov /* For sockets in TCP_CLOSED it's possible set keys that aren't 8594954f17dSDmitry Safonov * matching the future peer (address/VRF/etc), 8604954f17dSDmitry Safonov * tcp_ao_connect_init() will choose a correct matching MKT 8614954f17dSDmitry Safonov * if there's any. 8624954f17dSDmitry Safonov */ 8634954f17dSDmitry Safonov if (cmd.set_current) { 8644954f17dSDmitry Safonov new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 8654954f17dSDmitry Safonov if (!new_current) 8664954f17dSDmitry Safonov return -ENOENT; 8674954f17dSDmitry Safonov } 8684954f17dSDmitry Safonov if (cmd.set_rnext) { 8694954f17dSDmitry Safonov new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 8704954f17dSDmitry Safonov if (!new_rnext) 8714954f17dSDmitry Safonov return -ENOENT; 8724954f17dSDmitry Safonov } 8734954f17dSDmitry Safonov 8744954f17dSDmitry Safonov if (family == AF_INET) { 8754954f17dSDmitry Safonov struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.addr; 8764954f17dSDmitry Safonov 8774954f17dSDmitry Safonov addr = (union tcp_ao_addr *)&sin->sin_addr; 8784954f17dSDmitry Safonov addr_len = sizeof(struct in_addr); 8794954f17dSDmitry Safonov port = ntohs(sin->sin_port); 8804954f17dSDmitry Safonov } else { 8814954f17dSDmitry Safonov struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.addr; 8824954f17dSDmitry Safonov struct in6_addr *addr6 = &sin6->sin6_addr; 8834954f17dSDmitry Safonov 8844954f17dSDmitry Safonov if (ipv6_addr_v4mapped(addr6)) { 8854954f17dSDmitry Safonov addr = (union tcp_ao_addr *)&addr6->s6_addr32[3]; 8864954f17dSDmitry Safonov addr_len = sizeof(struct in_addr); 8874954f17dSDmitry Safonov family = AF_INET; 8884954f17dSDmitry Safonov } else { 8894954f17dSDmitry Safonov addr = (union tcp_ao_addr *)addr6; 8904954f17dSDmitry Safonov addr_len = sizeof(struct in6_addr); 8914954f17dSDmitry Safonov } 8924954f17dSDmitry Safonov port = ntohs(sin6->sin6_port); 8934954f17dSDmitry Safonov } 8944954f17dSDmitry Safonov prefix = cmd.prefix; 8954954f17dSDmitry Safonov 8964954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 8974954f17dSDmitry Safonov if (port != 0) 8984954f17dSDmitry Safonov return -EINVAL; 8994954f17dSDmitry Safonov 9004954f17dSDmitry Safonov /* We could choose random present key here for current/rnext 9014954f17dSDmitry Safonov * but that's less predictable. Let's be strict and don't 9024954f17dSDmitry Safonov * allow removing a key that's in use. RFC5925 doesn't 9034954f17dSDmitry Safonov * specify how-to coordinate key removal, but says: 9044954f17dSDmitry Safonov * "It is presumed that an MKT affecting a particular 9054954f17dSDmitry Safonov * connection cannot be destroyed during an active connection" 9064954f17dSDmitry Safonov */ 9074954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 9084954f17dSDmitry Safonov if (cmd.sndid != key->sndid || 9094954f17dSDmitry Safonov cmd.rcvid != key->rcvid) 9104954f17dSDmitry Safonov continue; 9114954f17dSDmitry Safonov 9124954f17dSDmitry Safonov if (family != key->family || 9134954f17dSDmitry Safonov prefix != key->prefixlen || 9144954f17dSDmitry Safonov memcmp(addr, &key->addr, addr_len)) 9154954f17dSDmitry Safonov continue; 9164954f17dSDmitry Safonov 9174954f17dSDmitry Safonov if (key == new_current || key == new_rnext) 9184954f17dSDmitry Safonov continue; 9194954f17dSDmitry Safonov 9204954f17dSDmitry Safonov return tcp_ao_delete_key(sk, ao_info, key, 9214954f17dSDmitry Safonov new_current, new_rnext); 9224954f17dSDmitry Safonov } 9234954f17dSDmitry Safonov return -ENOENT; 9244954f17dSDmitry Safonov } 9254954f17dSDmitry Safonov 9260aadc739SDmitry Safonov /* cmd.ao_required makes a socket TCP-AO only. 9270aadc739SDmitry Safonov * Don't allow any md5 keys for any l3intf on the socket together with it. 9280aadc739SDmitry Safonov * Restricting it early in setsockopt() removes a check for 9290aadc739SDmitry Safonov * ao_info->ao_required on inbound tcp segment fast-path. 9300aadc739SDmitry Safonov */ 9310aadc739SDmitry Safonov static int tcp_ao_required_verify(struct sock *sk) 9320aadc739SDmitry Safonov { 9330aadc739SDmitry Safonov #ifdef CONFIG_TCP_MD5SIG 9340aadc739SDmitry Safonov const struct tcp_md5sig_info *md5sig; 9350aadc739SDmitry Safonov 9360aadc739SDmitry Safonov if (!static_branch_unlikely(&tcp_md5_needed.key)) 9370aadc739SDmitry Safonov return 0; 9380aadc739SDmitry Safonov 9390aadc739SDmitry Safonov md5sig = rcu_dereference_check(tcp_sk(sk)->md5sig_info, 9400aadc739SDmitry Safonov lockdep_sock_is_held(sk)); 9410aadc739SDmitry Safonov if (!md5sig) 9420aadc739SDmitry Safonov return 0; 9430aadc739SDmitry Safonov 9440aadc739SDmitry Safonov if (rcu_dereference_check(hlist_first_rcu(&md5sig->head), 9450aadc739SDmitry Safonov lockdep_sock_is_held(sk))) 9460aadc739SDmitry Safonov return 1; 9470aadc739SDmitry Safonov #endif 9480aadc739SDmitry Safonov return 0; 9490aadc739SDmitry Safonov } 9500aadc739SDmitry Safonov 9514954f17dSDmitry Safonov static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, 9524954f17dSDmitry Safonov sockptr_t optval, int optlen) 9534954f17dSDmitry Safonov { 9544954f17dSDmitry Safonov struct tcp_ao_key *new_current = NULL, *new_rnext = NULL; 9554954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 9564954f17dSDmitry Safonov struct tcp_ao_info_opt cmd; 9574954f17dSDmitry Safonov bool first = false; 9584954f17dSDmitry Safonov int err; 9594954f17dSDmitry Safonov 9604954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 9614954f17dSDmitry Safonov return -EINVAL; 9624954f17dSDmitry Safonov 9634954f17dSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 9644954f17dSDmitry Safonov if (err) 9654954f17dSDmitry Safonov return err; 9664954f17dSDmitry Safonov 9674954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 9684954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 9694954f17dSDmitry Safonov return -EINVAL; 9704954f17dSDmitry Safonov } 9714954f17dSDmitry Safonov 9724954f17dSDmitry Safonov if (cmd.reserved != 0) 9734954f17dSDmitry Safonov return -EINVAL; 9744954f17dSDmitry Safonov 9754954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 9764954f17dSDmitry Safonov if (IS_ERR(ao_info)) 9774954f17dSDmitry Safonov return PTR_ERR(ao_info); 9784954f17dSDmitry Safonov if (!ao_info) { 9794954f17dSDmitry Safonov ao_info = tcp_ao_alloc_info(GFP_KERNEL); 9804954f17dSDmitry Safonov if (!ao_info) 9814954f17dSDmitry Safonov return -ENOMEM; 9824954f17dSDmitry Safonov first = true; 9834954f17dSDmitry Safonov } 9844954f17dSDmitry Safonov 9850aadc739SDmitry Safonov if (cmd.ao_required && tcp_ao_required_verify(sk)) 9860aadc739SDmitry Safonov return -EKEYREJECTED; 9870aadc739SDmitry Safonov 9884954f17dSDmitry Safonov /* For sockets in TCP_CLOSED it's possible set keys that aren't 9894954f17dSDmitry Safonov * matching the future peer (address/port/VRF/etc), 9904954f17dSDmitry Safonov * tcp_ao_connect_init() will choose a correct matching MKT 9914954f17dSDmitry Safonov * if there's any. 9924954f17dSDmitry Safonov */ 9934954f17dSDmitry Safonov if (cmd.set_current) { 9944954f17dSDmitry Safonov new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 9954954f17dSDmitry Safonov if (!new_current) { 9964954f17dSDmitry Safonov err = -ENOENT; 9974954f17dSDmitry Safonov goto out; 9984954f17dSDmitry Safonov } 9994954f17dSDmitry Safonov } 10004954f17dSDmitry Safonov if (cmd.set_rnext) { 10014954f17dSDmitry Safonov new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 10024954f17dSDmitry Safonov if (!new_rnext) { 10034954f17dSDmitry Safonov err = -ENOENT; 10044954f17dSDmitry Safonov goto out; 10054954f17dSDmitry Safonov } 10064954f17dSDmitry Safonov } 10074954f17dSDmitry Safonov 10084954f17dSDmitry Safonov ao_info->ao_required = cmd.ao_required; 10094954f17dSDmitry Safonov if (new_current) 10104954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, new_current); 10114954f17dSDmitry Safonov if (new_rnext) 10124954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, new_rnext); 10134954f17dSDmitry Safonov if (first) { 10144954f17dSDmitry Safonov sk_gso_disable(sk); 10154954f17dSDmitry Safonov rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 10164954f17dSDmitry Safonov } 10174954f17dSDmitry Safonov return 0; 10184954f17dSDmitry Safonov out: 10194954f17dSDmitry Safonov if (first) 10204954f17dSDmitry Safonov kfree(ao_info); 10214954f17dSDmitry Safonov return err; 10224954f17dSDmitry Safonov } 10234954f17dSDmitry Safonov 10244954f17dSDmitry Safonov int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, 10254954f17dSDmitry Safonov sockptr_t optval, int optlen) 10264954f17dSDmitry Safonov { 10274954f17dSDmitry Safonov if (WARN_ON_ONCE(family != AF_INET && family != AF_INET6)) 10284954f17dSDmitry Safonov return -EAFNOSUPPORT; 10294954f17dSDmitry Safonov 10304954f17dSDmitry Safonov switch (cmd) { 10314954f17dSDmitry Safonov case TCP_AO_ADD_KEY: 10324954f17dSDmitry Safonov return tcp_ao_add_cmd(sk, family, optval, optlen); 10334954f17dSDmitry Safonov case TCP_AO_DEL_KEY: 10344954f17dSDmitry Safonov return tcp_ao_del_cmd(sk, family, optval, optlen); 10354954f17dSDmitry Safonov case TCP_AO_INFO: 10364954f17dSDmitry Safonov return tcp_ao_info_cmd(sk, family, optval, optlen); 10374954f17dSDmitry Safonov default: 10384954f17dSDmitry Safonov WARN_ON_ONCE(1); 10394954f17dSDmitry Safonov return -EINVAL; 10404954f17dSDmitry Safonov } 10414954f17dSDmitry Safonov } 10424954f17dSDmitry Safonov 10434954f17dSDmitry Safonov int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) 10444954f17dSDmitry Safonov { 10454954f17dSDmitry Safonov return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen); 10464954f17dSDmitry Safonov } 10474954f17dSDmitry Safonov 1048