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> 18953af8e3SDmitry Safonov #include <net/icmp.h> 194954f17dSDmitry Safonov 20*67fa83f7SDmitry Safonov DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_ao_needed, HZ); 21*67fa83f7SDmitry Safonov 227c2ffaf2SDmitry Safonov int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, 237c2ffaf2SDmitry Safonov unsigned int len, struct tcp_sigpool *hp) 247c2ffaf2SDmitry Safonov { 257c2ffaf2SDmitry Safonov struct scatterlist sg; 267c2ffaf2SDmitry Safonov int ret; 277c2ffaf2SDmitry Safonov 287c2ffaf2SDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp->req), 297c2ffaf2SDmitry Safonov mkt->key, mkt->keylen)) 307c2ffaf2SDmitry Safonov goto clear_hash; 317c2ffaf2SDmitry Safonov 327c2ffaf2SDmitry Safonov ret = crypto_ahash_init(hp->req); 337c2ffaf2SDmitry Safonov if (ret) 347c2ffaf2SDmitry Safonov goto clear_hash; 357c2ffaf2SDmitry Safonov 367c2ffaf2SDmitry Safonov sg_init_one(&sg, ctx, len); 377c2ffaf2SDmitry Safonov ahash_request_set_crypt(hp->req, &sg, key, len); 387c2ffaf2SDmitry Safonov crypto_ahash_update(hp->req); 397c2ffaf2SDmitry Safonov 407c2ffaf2SDmitry Safonov ret = crypto_ahash_final(hp->req); 417c2ffaf2SDmitry Safonov if (ret) 427c2ffaf2SDmitry Safonov goto clear_hash; 437c2ffaf2SDmitry Safonov 447c2ffaf2SDmitry Safonov return 0; 457c2ffaf2SDmitry Safonov clear_hash: 467c2ffaf2SDmitry Safonov memset(key, 0, tcp_ao_digest_size(mkt)); 477c2ffaf2SDmitry Safonov return 1; 487c2ffaf2SDmitry Safonov } 497c2ffaf2SDmitry Safonov 50953af8e3SDmitry Safonov bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code) 51953af8e3SDmitry Safonov { 52953af8e3SDmitry Safonov bool ignore_icmp = false; 53953af8e3SDmitry Safonov struct tcp_ao_info *ao; 54953af8e3SDmitry Safonov 55*67fa83f7SDmitry Safonov if (!static_branch_unlikely(&tcp_ao_needed.key)) 56*67fa83f7SDmitry Safonov return false; 57*67fa83f7SDmitry Safonov 58953af8e3SDmitry Safonov /* RFC5925, 7.8: 59953af8e3SDmitry Safonov * >> A TCP-AO implementation MUST default to ignore incoming ICMPv4 60953af8e3SDmitry Safonov * messages of Type 3 (destination unreachable), Codes 2-4 (protocol 61953af8e3SDmitry Safonov * unreachable, port unreachable, and fragmentation needed -- ’hard 62953af8e3SDmitry Safonov * errors’), and ICMPv6 Type 1 (destination unreachable), Code 1 63953af8e3SDmitry Safonov * (administratively prohibited) and Code 4 (port unreachable) intended 64953af8e3SDmitry Safonov * for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN- 65953af8e3SDmitry Safonov * WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs. 66953af8e3SDmitry Safonov */ 67953af8e3SDmitry Safonov if (family == AF_INET) { 68953af8e3SDmitry Safonov if (type != ICMP_DEST_UNREACH) 69953af8e3SDmitry Safonov return false; 70953af8e3SDmitry Safonov if (code < ICMP_PROT_UNREACH || code > ICMP_FRAG_NEEDED) 71953af8e3SDmitry Safonov return false; 72953af8e3SDmitry Safonov } else { 73953af8e3SDmitry Safonov if (type != ICMPV6_DEST_UNREACH) 74953af8e3SDmitry Safonov return false; 75953af8e3SDmitry Safonov if (code != ICMPV6_ADM_PROHIBITED && code != ICMPV6_PORT_UNREACH) 76953af8e3SDmitry Safonov return false; 77953af8e3SDmitry Safonov } 78953af8e3SDmitry Safonov 79953af8e3SDmitry Safonov rcu_read_lock(); 80953af8e3SDmitry Safonov switch (sk->sk_state) { 81953af8e3SDmitry Safonov case TCP_TIME_WAIT: 82953af8e3SDmitry Safonov ao = rcu_dereference(tcp_twsk(sk)->ao_info); 83953af8e3SDmitry Safonov break; 84953af8e3SDmitry Safonov case TCP_SYN_SENT: 85953af8e3SDmitry Safonov case TCP_SYN_RECV: 86953af8e3SDmitry Safonov case TCP_LISTEN: 87953af8e3SDmitry Safonov case TCP_NEW_SYN_RECV: 88953af8e3SDmitry Safonov /* RFC5925 specifies to ignore ICMPs *only* on connections 89953af8e3SDmitry Safonov * in synchronized states. 90953af8e3SDmitry Safonov */ 91953af8e3SDmitry Safonov rcu_read_unlock(); 92953af8e3SDmitry Safonov return false; 93953af8e3SDmitry Safonov default: 94953af8e3SDmitry Safonov ao = rcu_dereference(tcp_sk(sk)->ao_info); 95953af8e3SDmitry Safonov } 96953af8e3SDmitry Safonov 97953af8e3SDmitry Safonov if (ao && !ao->accept_icmps) { 98953af8e3SDmitry Safonov ignore_icmp = true; 99953af8e3SDmitry Safonov __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAODROPPEDICMPS); 100953af8e3SDmitry Safonov atomic64_inc(&ao->counters.dropped_icmp); 101953af8e3SDmitry Safonov } 102953af8e3SDmitry Safonov rcu_read_unlock(); 103953af8e3SDmitry Safonov 104953af8e3SDmitry Safonov return ignore_icmp; 105953af8e3SDmitry Safonov } 106953af8e3SDmitry Safonov 1074954f17dSDmitry Safonov /* Optimized version of tcp_ao_do_lookup(): only for sockets for which 1084954f17dSDmitry Safonov * it's known that the keys in ao_info are matching peer's 1094954f17dSDmitry Safonov * family/address/VRF/etc. 1104954f17dSDmitry Safonov */ 111ba7783adSDmitry Safonov struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao, 1124954f17dSDmitry Safonov int sndid, int rcvid) 1134954f17dSDmitry Safonov { 1144954f17dSDmitry Safonov struct tcp_ao_key *key; 1154954f17dSDmitry Safonov 1164954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 1174954f17dSDmitry Safonov if ((sndid >= 0 && key->sndid != sndid) || 1184954f17dSDmitry Safonov (rcvid >= 0 && key->rcvid != rcvid)) 1194954f17dSDmitry Safonov continue; 1204954f17dSDmitry Safonov return key; 1214954f17dSDmitry Safonov } 1224954f17dSDmitry Safonov 1234954f17dSDmitry Safonov return NULL; 1244954f17dSDmitry Safonov } 1254954f17dSDmitry Safonov 1264954f17dSDmitry Safonov static int ipv4_prefix_cmp(const struct in_addr *addr1, 1274954f17dSDmitry Safonov const struct in_addr *addr2, 1284954f17dSDmitry Safonov unsigned int prefixlen) 1294954f17dSDmitry Safonov { 1304954f17dSDmitry Safonov __be32 mask = inet_make_mask(prefixlen); 1314954f17dSDmitry Safonov __be32 a1 = addr1->s_addr & mask; 1324954f17dSDmitry Safonov __be32 a2 = addr2->s_addr & mask; 1334954f17dSDmitry Safonov 1344954f17dSDmitry Safonov if (a1 == a2) 1354954f17dSDmitry Safonov return 0; 1364954f17dSDmitry Safonov return memcmp(&a1, &a2, sizeof(a1)); 1374954f17dSDmitry Safonov } 1384954f17dSDmitry Safonov 1394954f17dSDmitry Safonov static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, 1404954f17dSDmitry Safonov const union tcp_ao_addr *addr, u8 prefixlen, 1414954f17dSDmitry Safonov int family, int sndid, int rcvid) 1424954f17dSDmitry Safonov { 1434954f17dSDmitry Safonov if (sndid >= 0 && key->sndid != sndid) 1444954f17dSDmitry Safonov return (key->sndid > sndid) ? 1 : -1; 1454954f17dSDmitry Safonov if (rcvid >= 0 && key->rcvid != rcvid) 1464954f17dSDmitry Safonov return (key->rcvid > rcvid) ? 1 : -1; 1474954f17dSDmitry Safonov 1484954f17dSDmitry Safonov if (family == AF_UNSPEC) 1494954f17dSDmitry Safonov return 0; 1504954f17dSDmitry Safonov if (key->family != family) 1514954f17dSDmitry Safonov return (key->family > family) ? 1 : -1; 1524954f17dSDmitry Safonov 1534954f17dSDmitry Safonov if (family == AF_INET) { 1544954f17dSDmitry Safonov if (ntohl(key->addr.a4.s_addr) == INADDR_ANY) 1554954f17dSDmitry Safonov return 0; 1564954f17dSDmitry Safonov if (ntohl(addr->a4.s_addr) == INADDR_ANY) 1574954f17dSDmitry Safonov return 0; 1584954f17dSDmitry Safonov return ipv4_prefix_cmp(&key->addr.a4, &addr->a4, prefixlen); 1594954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 1604954f17dSDmitry Safonov } else { 1614954f17dSDmitry Safonov if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6)) 1624954f17dSDmitry Safonov return 0; 1634954f17dSDmitry Safonov if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen)) 1644954f17dSDmitry Safonov return 0; 1654954f17dSDmitry Safonov return memcmp(&key->addr.a6, &addr->a6, sizeof(addr->a6)); 1664954f17dSDmitry Safonov #endif 1674954f17dSDmitry Safonov } 1684954f17dSDmitry Safonov return -1; 1694954f17dSDmitry Safonov } 1704954f17dSDmitry Safonov 1714954f17dSDmitry Safonov static int tcp_ao_key_cmp(const struct tcp_ao_key *key, 1724954f17dSDmitry Safonov const union tcp_ao_addr *addr, u8 prefixlen, 1734954f17dSDmitry Safonov int family, int sndid, int rcvid) 1744954f17dSDmitry Safonov { 1754954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 1764954f17dSDmitry Safonov if (family == AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) { 1774954f17dSDmitry Safonov __be32 addr4 = addr->a6.s6_addr32[3]; 1784954f17dSDmitry Safonov 1794954f17dSDmitry Safonov return __tcp_ao_key_cmp(key, (union tcp_ao_addr *)&addr4, 1804954f17dSDmitry Safonov prefixlen, AF_INET, sndid, rcvid); 1814954f17dSDmitry Safonov } 1824954f17dSDmitry Safonov #endif 1834954f17dSDmitry Safonov return __tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid); 1844954f17dSDmitry Safonov } 1854954f17dSDmitry Safonov 1864954f17dSDmitry Safonov static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk, 1874954f17dSDmitry Safonov const union tcp_ao_addr *addr, int family, u8 prefix, 1884954f17dSDmitry Safonov int sndid, int rcvid) 1894954f17dSDmitry Safonov { 1904954f17dSDmitry Safonov struct tcp_ao_key *key; 1914954f17dSDmitry Safonov struct tcp_ao_info *ao; 1924954f17dSDmitry Safonov 193*67fa83f7SDmitry Safonov if (!static_branch_unlikely(&tcp_ao_needed.key)) 194*67fa83f7SDmitry Safonov return NULL; 195*67fa83f7SDmitry Safonov 1964954f17dSDmitry Safonov ao = rcu_dereference_check(tcp_sk(sk)->ao_info, 1974954f17dSDmitry Safonov lockdep_sock_is_held(sk)); 1984954f17dSDmitry Safonov if (!ao) 1994954f17dSDmitry Safonov return NULL; 2004954f17dSDmitry Safonov 2014954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 2024954f17dSDmitry Safonov u8 prefixlen = min(prefix, key->prefixlen); 2034954f17dSDmitry Safonov 2044954f17dSDmitry Safonov if (!tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid)) 2054954f17dSDmitry Safonov return key; 2064954f17dSDmitry Safonov } 2074954f17dSDmitry Safonov return NULL; 2084954f17dSDmitry Safonov } 2094954f17dSDmitry Safonov 2100aadc739SDmitry Safonov struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, 2110aadc739SDmitry Safonov const union tcp_ao_addr *addr, 2120aadc739SDmitry Safonov int family, int sndid, int rcvid) 2130aadc739SDmitry Safonov { 2140aadc739SDmitry Safonov return __tcp_ao_do_lookup(sk, addr, family, U8_MAX, sndid, rcvid); 2150aadc739SDmitry Safonov } 2160aadc739SDmitry Safonov 2174954f17dSDmitry Safonov static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags) 2184954f17dSDmitry Safonov { 2194954f17dSDmitry Safonov struct tcp_ao_info *ao; 2204954f17dSDmitry Safonov 2214954f17dSDmitry Safonov ao = kzalloc(sizeof(*ao), flags); 2224954f17dSDmitry Safonov if (!ao) 2234954f17dSDmitry Safonov return NULL; 2244954f17dSDmitry Safonov INIT_HLIST_HEAD(&ao->head); 225decde258SDmitry Safonov refcount_set(&ao->refcnt, 1); 2264954f17dSDmitry Safonov 2274954f17dSDmitry Safonov return ao; 2284954f17dSDmitry Safonov } 2294954f17dSDmitry Safonov 2304954f17dSDmitry Safonov static void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt) 2314954f17dSDmitry Safonov { 2324954f17dSDmitry Safonov hlist_add_head_rcu(&mkt->node, &ao->head); 2334954f17dSDmitry Safonov } 2344954f17dSDmitry Safonov 23506b22ef2SDmitry Safonov static struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, 23606b22ef2SDmitry Safonov struct tcp_ao_key *key) 23706b22ef2SDmitry Safonov { 23806b22ef2SDmitry Safonov struct tcp_ao_key *new_key; 23906b22ef2SDmitry Safonov 24006b22ef2SDmitry Safonov new_key = sock_kmalloc(sk, tcp_ao_sizeof_key(key), 24106b22ef2SDmitry Safonov GFP_ATOMIC); 24206b22ef2SDmitry Safonov if (!new_key) 24306b22ef2SDmitry Safonov return NULL; 24406b22ef2SDmitry Safonov 24506b22ef2SDmitry Safonov *new_key = *key; 24606b22ef2SDmitry Safonov INIT_HLIST_NODE(&new_key->node); 24706b22ef2SDmitry Safonov tcp_sigpool_get(new_key->tcp_sigpool_id); 248af09a341SDmitry Safonov atomic64_set(&new_key->pkt_good, 0); 249af09a341SDmitry Safonov atomic64_set(&new_key->pkt_bad, 0); 25006b22ef2SDmitry Safonov 25106b22ef2SDmitry Safonov return new_key; 25206b22ef2SDmitry Safonov } 25306b22ef2SDmitry Safonov 2544954f17dSDmitry Safonov static void tcp_ao_key_free_rcu(struct rcu_head *head) 2554954f17dSDmitry Safonov { 2564954f17dSDmitry Safonov struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu); 2574954f17dSDmitry Safonov 2584954f17dSDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 2594954f17dSDmitry Safonov kfree_sensitive(key); 2604954f17dSDmitry Safonov } 2614954f17dSDmitry Safonov 262decde258SDmitry Safonov void tcp_ao_destroy_sock(struct sock *sk, bool twsk) 2634954f17dSDmitry Safonov { 2644954f17dSDmitry Safonov struct tcp_ao_info *ao; 2654954f17dSDmitry Safonov struct tcp_ao_key *key; 2664954f17dSDmitry Safonov struct hlist_node *n; 2674954f17dSDmitry Safonov 268decde258SDmitry Safonov if (twsk) { 269decde258SDmitry Safonov ao = rcu_dereference_protected(tcp_twsk(sk)->ao_info, 1); 270decde258SDmitry Safonov tcp_twsk(sk)->ao_info = NULL; 271decde258SDmitry Safonov } else { 2724954f17dSDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1); 2734954f17dSDmitry Safonov tcp_sk(sk)->ao_info = NULL; 274decde258SDmitry Safonov } 2754954f17dSDmitry Safonov 276decde258SDmitry Safonov if (!ao || !refcount_dec_and_test(&ao->refcnt)) 2774954f17dSDmitry Safonov return; 2784954f17dSDmitry Safonov 2794954f17dSDmitry Safonov hlist_for_each_entry_safe(key, n, &ao->head, node) { 2804954f17dSDmitry Safonov hlist_del_rcu(&key->node); 281decde258SDmitry Safonov if (!twsk) 2824954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 2834954f17dSDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 2844954f17dSDmitry Safonov } 2854954f17dSDmitry Safonov 2864954f17dSDmitry Safonov kfree_rcu(ao, rcu); 287*67fa83f7SDmitry Safonov static_branch_slow_dec_deferred(&tcp_ao_needed); 2884954f17dSDmitry Safonov } 2894954f17dSDmitry Safonov 290decde258SDmitry Safonov void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp) 291decde258SDmitry Safonov { 292decde258SDmitry Safonov struct tcp_ao_info *ao_info = rcu_dereference_protected(tp->ao_info, 1); 293decde258SDmitry Safonov 294decde258SDmitry Safonov if (ao_info) { 295decde258SDmitry Safonov struct tcp_ao_key *key; 296decde258SDmitry Safonov struct hlist_node *n; 297decde258SDmitry Safonov int omem = 0; 298decde258SDmitry Safonov 299decde258SDmitry Safonov hlist_for_each_entry_safe(key, n, &ao_info->head, node) { 300decde258SDmitry Safonov omem += tcp_ao_sizeof_key(key); 301decde258SDmitry Safonov } 302decde258SDmitry Safonov 303decde258SDmitry Safonov refcount_inc(&ao_info->refcnt); 304decde258SDmitry Safonov atomic_sub(omem, &(((struct sock *)tp)->sk_omem_alloc)); 305decde258SDmitry Safonov rcu_assign_pointer(tcptw->ao_info, ao_info); 306decde258SDmitry Safonov } else { 307decde258SDmitry Safonov tcptw->ao_info = NULL; 308decde258SDmitry Safonov } 309decde258SDmitry Safonov } 310decde258SDmitry Safonov 3117c2ffaf2SDmitry Safonov /* 4 tuple and ISNs are expected in NBO */ 3127c2ffaf2SDmitry Safonov static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, 3137c2ffaf2SDmitry Safonov __be32 saddr, __be32 daddr, 3147c2ffaf2SDmitry Safonov __be16 sport, __be16 dport, 3157c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn) 3167c2ffaf2SDmitry Safonov { 3177c2ffaf2SDmitry Safonov /* See RFC5926 3.1.1 */ 3187c2ffaf2SDmitry Safonov struct kdf_input_block { 3197c2ffaf2SDmitry Safonov u8 counter; 3207c2ffaf2SDmitry Safonov u8 label[6]; 3217c2ffaf2SDmitry Safonov struct tcp4_ao_context ctx; 3227c2ffaf2SDmitry Safonov __be16 outlen; 3237c2ffaf2SDmitry Safonov } __packed * tmp; 3247c2ffaf2SDmitry Safonov struct tcp_sigpool hp; 3257c2ffaf2SDmitry Safonov int err; 3267c2ffaf2SDmitry Safonov 3277c2ffaf2SDmitry Safonov err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp); 3287c2ffaf2SDmitry Safonov if (err) 3297c2ffaf2SDmitry Safonov return err; 3307c2ffaf2SDmitry Safonov 3317c2ffaf2SDmitry Safonov tmp = hp.scratch; 3327c2ffaf2SDmitry Safonov tmp->counter = 1; 3337c2ffaf2SDmitry Safonov memcpy(tmp->label, "TCP-AO", 6); 3347c2ffaf2SDmitry Safonov tmp->ctx.saddr = saddr; 3357c2ffaf2SDmitry Safonov tmp->ctx.daddr = daddr; 3367c2ffaf2SDmitry Safonov tmp->ctx.sport = sport; 3377c2ffaf2SDmitry Safonov tmp->ctx.dport = dport; 3387c2ffaf2SDmitry Safonov tmp->ctx.sisn = sisn; 3397c2ffaf2SDmitry Safonov tmp->ctx.disn = disn; 3407c2ffaf2SDmitry Safonov tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ 3417c2ffaf2SDmitry Safonov 3427c2ffaf2SDmitry Safonov err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp); 3437c2ffaf2SDmitry Safonov tcp_sigpool_end(&hp); 3447c2ffaf2SDmitry Safonov 3457c2ffaf2SDmitry Safonov return err; 3467c2ffaf2SDmitry Safonov } 3477c2ffaf2SDmitry Safonov 3487c2ffaf2SDmitry Safonov int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, 3497c2ffaf2SDmitry Safonov const struct sock *sk, 3507c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn, bool send) 3517c2ffaf2SDmitry Safonov { 3527c2ffaf2SDmitry Safonov if (send) 3537c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr, 3547c2ffaf2SDmitry Safonov sk->sk_daddr, htons(sk->sk_num), 3557c2ffaf2SDmitry Safonov sk->sk_dport, sisn, disn); 3567c2ffaf2SDmitry Safonov else 3577c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr, 3587c2ffaf2SDmitry Safonov sk->sk_rcv_saddr, sk->sk_dport, 3597c2ffaf2SDmitry Safonov htons(sk->sk_num), disn, sisn); 3607c2ffaf2SDmitry Safonov } 3617c2ffaf2SDmitry Safonov 3627c2ffaf2SDmitry Safonov static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, 3637c2ffaf2SDmitry Safonov const struct sock *sk, 3647c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn, bool send) 3657c2ffaf2SDmitry Safonov { 3667c2ffaf2SDmitry Safonov if (mkt->family == AF_INET) 3677c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); 3687c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 3697c2ffaf2SDmitry Safonov else if (mkt->family == AF_INET6) 3707c2ffaf2SDmitry Safonov return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); 3717c2ffaf2SDmitry Safonov #endif 3727c2ffaf2SDmitry Safonov else 3737c2ffaf2SDmitry Safonov return -EOPNOTSUPP; 3747c2ffaf2SDmitry Safonov } 3757c2ffaf2SDmitry Safonov 37606b22ef2SDmitry Safonov int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, 37706b22ef2SDmitry Safonov struct request_sock *req) 37806b22ef2SDmitry Safonov { 37906b22ef2SDmitry Safonov struct inet_request_sock *ireq = inet_rsk(req); 38006b22ef2SDmitry Safonov 38106b22ef2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, 38206b22ef2SDmitry Safonov ireq->ir_loc_addr, ireq->ir_rmt_addr, 38306b22ef2SDmitry Safonov htons(ireq->ir_num), ireq->ir_rmt_port, 38406b22ef2SDmitry Safonov htonl(tcp_rsk(req)->snt_isn), 38506b22ef2SDmitry Safonov htonl(tcp_rsk(req)->rcv_isn)); 38606b22ef2SDmitry Safonov } 38706b22ef2SDmitry Safonov 38806b22ef2SDmitry Safonov static int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, 38906b22ef2SDmitry Safonov const struct sk_buff *skb, 39006b22ef2SDmitry Safonov __be32 sisn, __be32 disn) 39106b22ef2SDmitry Safonov { 39206b22ef2SDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 39306b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 39406b22ef2SDmitry Safonov 39506b22ef2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr, 39606b22ef2SDmitry Safonov th->source, th->dest, sisn, disn); 39706b22ef2SDmitry Safonov } 39806b22ef2SDmitry Safonov 39906b22ef2SDmitry Safonov static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, 40006b22ef2SDmitry Safonov const struct sk_buff *skb, 40106b22ef2SDmitry Safonov __be32 sisn, __be32 disn, int family) 40206b22ef2SDmitry Safonov { 40306b22ef2SDmitry Safonov if (family == AF_INET) 40406b22ef2SDmitry Safonov return tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn); 40506b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 40606b22ef2SDmitry Safonov else if (family == AF_INET6) 40706b22ef2SDmitry Safonov return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn); 40806b22ef2SDmitry Safonov #endif 40906b22ef2SDmitry Safonov return -EAFNOSUPPORT; 41006b22ef2SDmitry Safonov } 41106b22ef2SDmitry Safonov 4121e03d32bSDmitry Safonov static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp, 4131e03d32bSDmitry Safonov __be32 daddr, __be32 saddr, 4141e03d32bSDmitry Safonov int nbytes) 4151e03d32bSDmitry Safonov { 4161e03d32bSDmitry Safonov struct tcp4_pseudohdr *bp; 4171e03d32bSDmitry Safonov struct scatterlist sg; 4181e03d32bSDmitry Safonov 4191e03d32bSDmitry Safonov bp = hp->scratch; 4201e03d32bSDmitry Safonov bp->saddr = saddr; 4211e03d32bSDmitry Safonov bp->daddr = daddr; 4221e03d32bSDmitry Safonov bp->pad = 0; 4231e03d32bSDmitry Safonov bp->protocol = IPPROTO_TCP; 4241e03d32bSDmitry Safonov bp->len = cpu_to_be16(nbytes); 4251e03d32bSDmitry Safonov 4261e03d32bSDmitry Safonov sg_init_one(&sg, bp, sizeof(*bp)); 4271e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); 4281e03d32bSDmitry Safonov return crypto_ahash_update(hp->req); 4291e03d32bSDmitry Safonov } 4301e03d32bSDmitry Safonov 4311e03d32bSDmitry Safonov static int tcp_ao_hash_pseudoheader(unsigned short int family, 4321e03d32bSDmitry Safonov const struct sock *sk, 4331e03d32bSDmitry Safonov const struct sk_buff *skb, 4341e03d32bSDmitry Safonov struct tcp_sigpool *hp, int nbytes) 4351e03d32bSDmitry Safonov { 4361e03d32bSDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 4371e03d32bSDmitry Safonov 4381e03d32bSDmitry Safonov /* TODO: Can we rely on checksum being zero to mean outbound pkt? */ 4391e03d32bSDmitry Safonov if (!th->check) { 4401e03d32bSDmitry Safonov if (family == AF_INET) 4411e03d32bSDmitry Safonov return tcp_v4_ao_hash_pseudoheader(hp, sk->sk_daddr, 4421e03d32bSDmitry Safonov sk->sk_rcv_saddr, skb->len); 4431e03d32bSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 4441e03d32bSDmitry Safonov else if (family == AF_INET6) 4451e03d32bSDmitry Safonov return tcp_v6_ao_hash_pseudoheader(hp, &sk->sk_v6_daddr, 4461e03d32bSDmitry Safonov &sk->sk_v6_rcv_saddr, skb->len); 4471e03d32bSDmitry Safonov #endif 4481e03d32bSDmitry Safonov else 4491e03d32bSDmitry Safonov return -EAFNOSUPPORT; 4501e03d32bSDmitry Safonov } 4511e03d32bSDmitry Safonov 4521e03d32bSDmitry Safonov if (family == AF_INET) { 4531e03d32bSDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 4541e03d32bSDmitry Safonov 4551e03d32bSDmitry Safonov return tcp_v4_ao_hash_pseudoheader(hp, iph->daddr, 4561e03d32bSDmitry Safonov iph->saddr, skb->len); 4571e03d32bSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 4581e03d32bSDmitry Safonov } else if (family == AF_INET6) { 4591e03d32bSDmitry Safonov const struct ipv6hdr *iph = ipv6_hdr(skb); 4601e03d32bSDmitry Safonov 4611e03d32bSDmitry Safonov return tcp_v6_ao_hash_pseudoheader(hp, &iph->daddr, 4621e03d32bSDmitry Safonov &iph->saddr, skb->len); 4631e03d32bSDmitry Safonov #endif 4641e03d32bSDmitry Safonov } 4651e03d32bSDmitry Safonov return -EAFNOSUPPORT; 4661e03d32bSDmitry Safonov } 4671e03d32bSDmitry Safonov 46864382c71SDmitry Safonov u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq) 46964382c71SDmitry Safonov { 47064382c71SDmitry Safonov u32 sne = next_sne; 47164382c71SDmitry Safonov 47264382c71SDmitry Safonov if (before(seq, next_seq)) { 47364382c71SDmitry Safonov if (seq > next_seq) 47464382c71SDmitry Safonov sne--; 47564382c71SDmitry Safonov } else { 47664382c71SDmitry Safonov if (seq < next_seq) 47764382c71SDmitry Safonov sne++; 47864382c71SDmitry Safonov } 47964382c71SDmitry Safonov 48064382c71SDmitry Safonov return sne; 48164382c71SDmitry Safonov } 48264382c71SDmitry Safonov 4831e03d32bSDmitry Safonov /* tcp_ao_hash_sne(struct tcp_sigpool *hp) 4841e03d32bSDmitry Safonov * @hp - used for hashing 4851e03d32bSDmitry Safonov * @sne - sne value 4861e03d32bSDmitry Safonov */ 4871e03d32bSDmitry Safonov static int tcp_ao_hash_sne(struct tcp_sigpool *hp, u32 sne) 4881e03d32bSDmitry Safonov { 4891e03d32bSDmitry Safonov struct scatterlist sg; 4901e03d32bSDmitry Safonov __be32 *bp; 4911e03d32bSDmitry Safonov 4921e03d32bSDmitry Safonov bp = (__be32 *)hp->scratch; 4931e03d32bSDmitry Safonov *bp = htonl(sne); 4941e03d32bSDmitry Safonov 4951e03d32bSDmitry Safonov sg_init_one(&sg, bp, sizeof(*bp)); 4961e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); 4971e03d32bSDmitry Safonov return crypto_ahash_update(hp->req); 4981e03d32bSDmitry Safonov } 4991e03d32bSDmitry Safonov 5001e03d32bSDmitry Safonov static int tcp_ao_hash_header(struct tcp_sigpool *hp, 5011e03d32bSDmitry Safonov const struct tcphdr *th, 5021e03d32bSDmitry Safonov bool exclude_options, u8 *hash, 5031e03d32bSDmitry Safonov int hash_offset, int hash_len) 5041e03d32bSDmitry Safonov { 5051e03d32bSDmitry Safonov int err, len = th->doff << 2; 5061e03d32bSDmitry Safonov struct scatterlist sg; 5071e03d32bSDmitry Safonov u8 *hdr = hp->scratch; 5081e03d32bSDmitry Safonov 5091e03d32bSDmitry Safonov /* We are not allowed to change tcphdr, make a local copy */ 5101e03d32bSDmitry Safonov if (exclude_options) { 5111e03d32bSDmitry Safonov len = sizeof(*th) + sizeof(struct tcp_ao_hdr) + hash_len; 5121e03d32bSDmitry Safonov memcpy(hdr, th, sizeof(*th)); 5131e03d32bSDmitry Safonov memcpy(hdr + sizeof(*th), 5141e03d32bSDmitry Safonov (u8 *)th + hash_offset - sizeof(struct tcp_ao_hdr), 5151e03d32bSDmitry Safonov sizeof(struct tcp_ao_hdr)); 5161e03d32bSDmitry Safonov memset(hdr + sizeof(*th) + sizeof(struct tcp_ao_hdr), 5171e03d32bSDmitry Safonov 0, hash_len); 5181e03d32bSDmitry Safonov ((struct tcphdr *)hdr)->check = 0; 5191e03d32bSDmitry Safonov } else { 5201e03d32bSDmitry Safonov len = th->doff << 2; 5211e03d32bSDmitry Safonov memcpy(hdr, th, len); 5221e03d32bSDmitry Safonov /* zero out tcp-ao hash */ 5231e03d32bSDmitry Safonov ((struct tcphdr *)hdr)->check = 0; 5241e03d32bSDmitry Safonov memset(hdr + hash_offset, 0, hash_len); 5251e03d32bSDmitry Safonov } 5261e03d32bSDmitry Safonov 5271e03d32bSDmitry Safonov sg_init_one(&sg, hdr, len); 5281e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, len); 5291e03d32bSDmitry Safonov err = crypto_ahash_update(hp->req); 5301e03d32bSDmitry Safonov WARN_ON_ONCE(err != 0); 5311e03d32bSDmitry Safonov return err; 5321e03d32bSDmitry Safonov } 5331e03d32bSDmitry Safonov 534ba7783adSDmitry Safonov int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash, 535ba7783adSDmitry Safonov struct tcp_ao_key *key, const u8 *tkey, 536ba7783adSDmitry Safonov const union tcp_ao_addr *daddr, 537ba7783adSDmitry Safonov const union tcp_ao_addr *saddr, 538ba7783adSDmitry Safonov const struct tcphdr *th, u32 sne) 539ba7783adSDmitry Safonov { 540ba7783adSDmitry Safonov int tkey_len = tcp_ao_digest_size(key); 541ba7783adSDmitry Safonov int hash_offset = ao_hash - (char *)th; 542ba7783adSDmitry Safonov struct tcp_sigpool hp; 543ba7783adSDmitry Safonov void *hash_buf = NULL; 544ba7783adSDmitry Safonov 545ba7783adSDmitry Safonov hash_buf = kmalloc(tkey_len, GFP_ATOMIC); 546ba7783adSDmitry Safonov if (!hash_buf) 547ba7783adSDmitry Safonov goto clear_hash_noput; 548ba7783adSDmitry Safonov 549ba7783adSDmitry Safonov if (tcp_sigpool_start(key->tcp_sigpool_id, &hp)) 550ba7783adSDmitry Safonov goto clear_hash_noput; 551ba7783adSDmitry Safonov 552ba7783adSDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) 553ba7783adSDmitry Safonov goto clear_hash; 554ba7783adSDmitry Safonov 555ba7783adSDmitry Safonov if (crypto_ahash_init(hp.req)) 556ba7783adSDmitry Safonov goto clear_hash; 557ba7783adSDmitry Safonov 558ba7783adSDmitry Safonov if (tcp_ao_hash_sne(&hp, sne)) 559ba7783adSDmitry Safonov goto clear_hash; 560ba7783adSDmitry Safonov if (family == AF_INET) { 561ba7783adSDmitry Safonov if (tcp_v4_ao_hash_pseudoheader(&hp, daddr->a4.s_addr, 562ba7783adSDmitry Safonov saddr->a4.s_addr, th->doff * 4)) 563ba7783adSDmitry Safonov goto clear_hash; 564ba7783adSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 565ba7783adSDmitry Safonov } else if (family == AF_INET6) { 566ba7783adSDmitry Safonov if (tcp_v6_ao_hash_pseudoheader(&hp, &daddr->a6, 567ba7783adSDmitry Safonov &saddr->a6, th->doff * 4)) 568ba7783adSDmitry Safonov goto clear_hash; 569ba7783adSDmitry Safonov #endif 570ba7783adSDmitry Safonov } else { 571ba7783adSDmitry Safonov WARN_ON_ONCE(1); 572ba7783adSDmitry Safonov goto clear_hash; 573ba7783adSDmitry Safonov } 5747753c2f0SDmitry Safonov if (tcp_ao_hash_header(&hp, th, 5757753c2f0SDmitry Safonov !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), 576ba7783adSDmitry Safonov ao_hash, hash_offset, tcp_ao_maclen(key))) 577ba7783adSDmitry Safonov goto clear_hash; 578ba7783adSDmitry Safonov ahash_request_set_crypt(hp.req, NULL, hash_buf, 0); 579ba7783adSDmitry Safonov if (crypto_ahash_final(hp.req)) 580ba7783adSDmitry Safonov goto clear_hash; 581ba7783adSDmitry Safonov 582ba7783adSDmitry Safonov memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); 583ba7783adSDmitry Safonov tcp_sigpool_end(&hp); 584ba7783adSDmitry Safonov kfree(hash_buf); 585ba7783adSDmitry Safonov return 0; 586ba7783adSDmitry Safonov 587ba7783adSDmitry Safonov clear_hash: 588ba7783adSDmitry Safonov tcp_sigpool_end(&hp); 589ba7783adSDmitry Safonov clear_hash_noput: 590ba7783adSDmitry Safonov memset(ao_hash, 0, tcp_ao_maclen(key)); 591ba7783adSDmitry Safonov kfree(hash_buf); 592ba7783adSDmitry Safonov return 1; 593ba7783adSDmitry Safonov } 594ba7783adSDmitry Safonov 5951e03d32bSDmitry Safonov int tcp_ao_hash_skb(unsigned short int family, 5961e03d32bSDmitry Safonov char *ao_hash, struct tcp_ao_key *key, 5971e03d32bSDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 5981e03d32bSDmitry Safonov const u8 *tkey, int hash_offset, u32 sne) 5991e03d32bSDmitry Safonov { 6001e03d32bSDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 6011e03d32bSDmitry Safonov int tkey_len = tcp_ao_digest_size(key); 6021e03d32bSDmitry Safonov struct tcp_sigpool hp; 6031e03d32bSDmitry Safonov void *hash_buf = NULL; 6041e03d32bSDmitry Safonov 6051e03d32bSDmitry Safonov hash_buf = kmalloc(tkey_len, GFP_ATOMIC); 6061e03d32bSDmitry Safonov if (!hash_buf) 6071e03d32bSDmitry Safonov goto clear_hash_noput; 6081e03d32bSDmitry Safonov 6091e03d32bSDmitry Safonov if (tcp_sigpool_start(key->tcp_sigpool_id, &hp)) 6101e03d32bSDmitry Safonov goto clear_hash_noput; 6111e03d32bSDmitry Safonov 6121e03d32bSDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) 6131e03d32bSDmitry Safonov goto clear_hash; 6141e03d32bSDmitry Safonov 6151e03d32bSDmitry Safonov /* For now use sha1 by default. Depends on alg in tcp_ao_key */ 6161e03d32bSDmitry Safonov if (crypto_ahash_init(hp.req)) 6171e03d32bSDmitry Safonov goto clear_hash; 6181e03d32bSDmitry Safonov 6191e03d32bSDmitry Safonov if (tcp_ao_hash_sne(&hp, sne)) 6201e03d32bSDmitry Safonov goto clear_hash; 6211e03d32bSDmitry Safonov if (tcp_ao_hash_pseudoheader(family, sk, skb, &hp, skb->len)) 6221e03d32bSDmitry Safonov goto clear_hash; 6237753c2f0SDmitry Safonov if (tcp_ao_hash_header(&hp, th, 6247753c2f0SDmitry Safonov !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), 6251e03d32bSDmitry Safonov ao_hash, hash_offset, tcp_ao_maclen(key))) 6261e03d32bSDmitry Safonov goto clear_hash; 6271e03d32bSDmitry Safonov if (tcp_sigpool_hash_skb_data(&hp, skb, th->doff << 2)) 6281e03d32bSDmitry Safonov goto clear_hash; 6291e03d32bSDmitry Safonov ahash_request_set_crypt(hp.req, NULL, hash_buf, 0); 6301e03d32bSDmitry Safonov if (crypto_ahash_final(hp.req)) 6311e03d32bSDmitry Safonov goto clear_hash; 6321e03d32bSDmitry Safonov 6331e03d32bSDmitry Safonov memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); 6341e03d32bSDmitry Safonov tcp_sigpool_end(&hp); 6351e03d32bSDmitry Safonov kfree(hash_buf); 6361e03d32bSDmitry Safonov return 0; 6371e03d32bSDmitry Safonov 6381e03d32bSDmitry Safonov clear_hash: 6391e03d32bSDmitry Safonov tcp_sigpool_end(&hp); 6401e03d32bSDmitry Safonov clear_hash_noput: 6411e03d32bSDmitry Safonov memset(ao_hash, 0, tcp_ao_maclen(key)); 6421e03d32bSDmitry Safonov kfree(hash_buf); 6431e03d32bSDmitry Safonov return 1; 6441e03d32bSDmitry Safonov } 6451e03d32bSDmitry Safonov 6461e03d32bSDmitry Safonov int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, 6471e03d32bSDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 6481e03d32bSDmitry Safonov const u8 *tkey, int hash_offset, u32 sne) 6491e03d32bSDmitry Safonov { 6501e03d32bSDmitry Safonov return tcp_ao_hash_skb(AF_INET, ao_hash, key, sk, skb, 6511e03d32bSDmitry Safonov tkey, hash_offset, sne); 6521e03d32bSDmitry Safonov } 6531e03d32bSDmitry Safonov 6549427c6aaSDmitry Safonov int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key, 6559427c6aaSDmitry Safonov struct request_sock *req, const struct sk_buff *skb, 6569427c6aaSDmitry Safonov int hash_offset, u32 sne) 6579427c6aaSDmitry Safonov { 6589427c6aaSDmitry Safonov void *hash_buf = NULL; 6599427c6aaSDmitry Safonov int err; 6609427c6aaSDmitry Safonov 6619427c6aaSDmitry Safonov hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); 6629427c6aaSDmitry Safonov if (!hash_buf) 6639427c6aaSDmitry Safonov return -ENOMEM; 6649427c6aaSDmitry Safonov 6659427c6aaSDmitry Safonov err = tcp_v4_ao_calc_key_rsk(ao_key, hash_buf, req); 6669427c6aaSDmitry Safonov if (err) 6679427c6aaSDmitry Safonov goto out; 6689427c6aaSDmitry Safonov 6699427c6aaSDmitry Safonov err = tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb, 6709427c6aaSDmitry Safonov hash_buf, hash_offset, sne); 6719427c6aaSDmitry Safonov out: 6729427c6aaSDmitry Safonov kfree(hash_buf); 6739427c6aaSDmitry Safonov return err; 6749427c6aaSDmitry Safonov } 6759427c6aaSDmitry Safonov 67606b22ef2SDmitry Safonov struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk, 67706b22ef2SDmitry Safonov struct request_sock *req, 67806b22ef2SDmitry Safonov int sndid, int rcvid) 67906b22ef2SDmitry Safonov { 68006b22ef2SDmitry Safonov union tcp_ao_addr *addr = 68106b22ef2SDmitry Safonov (union tcp_ao_addr *)&inet_rsk(req)->ir_rmt_addr; 68206b22ef2SDmitry Safonov 68306b22ef2SDmitry Safonov return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid); 68406b22ef2SDmitry Safonov } 68506b22ef2SDmitry Safonov 6860aadc739SDmitry Safonov struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, 6870aadc739SDmitry Safonov int sndid, int rcvid) 6880aadc739SDmitry Safonov { 6890aadc739SDmitry Safonov union tcp_ao_addr *addr = (union tcp_ao_addr *)&addr_sk->sk_daddr; 6900aadc739SDmitry Safonov 6910aadc739SDmitry Safonov return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid); 6920aadc739SDmitry Safonov } 6930aadc739SDmitry Safonov 694ba7783adSDmitry Safonov int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, 69564382c71SDmitry Safonov const struct tcp_ao_hdr *aoh, int l3index, u32 seq, 696ba7783adSDmitry Safonov struct tcp_ao_key **key, char **traffic_key, 697ba7783adSDmitry Safonov bool *allocated_traffic_key, u8 *keyid, u32 *sne) 698ba7783adSDmitry Safonov { 69906b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 700ba7783adSDmitry Safonov struct tcp_ao_info *ao_info; 701ba7783adSDmitry Safonov 702ba7783adSDmitry Safonov *allocated_traffic_key = false; 703ba7783adSDmitry Safonov /* If there's no socket - than initial sisn/disn are unknown. 704ba7783adSDmitry Safonov * Drop the segment. RFC5925 (7.7) advises to require graceful 705ba7783adSDmitry Safonov * restart [RFC4724]. Alternatively, the RFC5925 advises to 706ba7783adSDmitry Safonov * save/restore traffic keys before/after reboot. 707ba7783adSDmitry Safonov * Linux TCP-AO support provides TCP_AO_ADD_KEY and TCP_AO_REPAIR 708ba7783adSDmitry Safonov * options to restore a socket post-reboot. 709ba7783adSDmitry Safonov */ 710ba7783adSDmitry Safonov if (!sk) 711ba7783adSDmitry Safonov return -ENOTCONN; 712ba7783adSDmitry Safonov 713decde258SDmitry Safonov if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { 71406b22ef2SDmitry Safonov unsigned int family = READ_ONCE(sk->sk_family); 71506b22ef2SDmitry Safonov union tcp_ao_addr *addr; 71606b22ef2SDmitry Safonov __be32 disn, sisn; 71706b22ef2SDmitry Safonov 71806b22ef2SDmitry Safonov if (sk->sk_state == TCP_NEW_SYN_RECV) { 71906b22ef2SDmitry Safonov struct request_sock *req = inet_reqsk(sk); 72006b22ef2SDmitry Safonov 72106b22ef2SDmitry Safonov sisn = htonl(tcp_rsk(req)->rcv_isn); 72206b22ef2SDmitry Safonov disn = htonl(tcp_rsk(req)->snt_isn); 72364382c71SDmitry Safonov *sne = tcp_ao_compute_sne(0, tcp_rsk(req)->snt_isn, seq); 72406b22ef2SDmitry Safonov } else { 72506b22ef2SDmitry Safonov sisn = th->seq; 72606b22ef2SDmitry Safonov disn = 0; 72706b22ef2SDmitry Safonov } 72806b22ef2SDmitry Safonov if (IS_ENABLED(CONFIG_IPV6) && family == AF_INET6) 72906b22ef2SDmitry Safonov addr = (union tcp_md5_addr *)&ipv6_hdr(skb)->saddr; 73006b22ef2SDmitry Safonov else 73106b22ef2SDmitry Safonov addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr; 73206b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 73306b22ef2SDmitry Safonov if (family == AF_INET6 && ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 73406b22ef2SDmitry Safonov family = AF_INET; 73506b22ef2SDmitry Safonov #endif 73606b22ef2SDmitry Safonov 73706b22ef2SDmitry Safonov sk = sk_const_to_full_sk(sk); 73806b22ef2SDmitry Safonov ao_info = rcu_dereference(tcp_sk(sk)->ao_info); 73906b22ef2SDmitry Safonov if (!ao_info) 74006b22ef2SDmitry Safonov return -ENOENT; 74106b22ef2SDmitry Safonov *key = tcp_ao_do_lookup(sk, addr, family, -1, aoh->rnext_keyid); 74206b22ef2SDmitry Safonov if (!*key) 74306b22ef2SDmitry Safonov return -ENOENT; 74406b22ef2SDmitry Safonov *traffic_key = kmalloc(tcp_ao_digest_size(*key), GFP_ATOMIC); 74506b22ef2SDmitry Safonov if (!*traffic_key) 74606b22ef2SDmitry Safonov return -ENOMEM; 74706b22ef2SDmitry Safonov *allocated_traffic_key = true; 74806b22ef2SDmitry Safonov if (tcp_ao_calc_key_skb(*key, *traffic_key, skb, 74906b22ef2SDmitry Safonov sisn, disn, family)) 750ba7783adSDmitry Safonov return -1; 75106b22ef2SDmitry Safonov *keyid = (*key)->rcvid; 75206b22ef2SDmitry Safonov } else { 75306b22ef2SDmitry Safonov struct tcp_ao_key *rnext_key; 75464382c71SDmitry Safonov u32 snd_basis; 755ba7783adSDmitry Safonov 75664382c71SDmitry Safonov if (sk->sk_state == TCP_TIME_WAIT) { 757decde258SDmitry Safonov ao_info = rcu_dereference(tcp_twsk(sk)->ao_info); 75864382c71SDmitry Safonov snd_basis = tcp_twsk(sk)->tw_snd_nxt; 75964382c71SDmitry Safonov } else { 760ba7783adSDmitry Safonov ao_info = rcu_dereference(tcp_sk(sk)->ao_info); 76164382c71SDmitry Safonov snd_basis = tcp_sk(sk)->snd_una; 76264382c71SDmitry Safonov } 763ba7783adSDmitry Safonov if (!ao_info) 764ba7783adSDmitry Safonov return -ENOENT; 765ba7783adSDmitry Safonov 766ba7783adSDmitry Safonov *key = tcp_ao_established_key(ao_info, aoh->rnext_keyid, -1); 767ba7783adSDmitry Safonov if (!*key) 768ba7783adSDmitry Safonov return -ENOENT; 769ba7783adSDmitry Safonov *traffic_key = snd_other_key(*key); 770ba7783adSDmitry Safonov rnext_key = READ_ONCE(ao_info->rnext_key); 771ba7783adSDmitry Safonov *keyid = rnext_key->rcvid; 77264382c71SDmitry Safonov *sne = tcp_ao_compute_sne(READ_ONCE(ao_info->snd_sne), 77364382c71SDmitry Safonov snd_basis, seq); 77406b22ef2SDmitry Safonov } 775ba7783adSDmitry Safonov return 0; 776ba7783adSDmitry Safonov } 777ba7783adSDmitry Safonov 7781e03d32bSDmitry Safonov int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, 7791e03d32bSDmitry Safonov struct tcp_ao_key *key, struct tcphdr *th, 7801e03d32bSDmitry Safonov __u8 *hash_location) 7811e03d32bSDmitry Safonov { 7821e03d32bSDmitry Safonov struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); 7831e03d32bSDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 7841e03d32bSDmitry Safonov struct tcp_ao_info *ao; 7851e03d32bSDmitry Safonov void *tkey_buf = NULL; 7861e03d32bSDmitry Safonov u8 *traffic_key; 78764382c71SDmitry Safonov u32 sne; 7881e03d32bSDmitry Safonov 7891e03d32bSDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 7901e03d32bSDmitry Safonov lockdep_sock_is_held(sk)); 7911e03d32bSDmitry Safonov traffic_key = snd_other_key(key); 7921e03d32bSDmitry Safonov if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) { 7931e03d32bSDmitry Safonov __be32 disn; 7941e03d32bSDmitry Safonov 7951e03d32bSDmitry Safonov if (!(tcb->tcp_flags & TCPHDR_ACK)) { 7961e03d32bSDmitry Safonov disn = 0; 7971e03d32bSDmitry Safonov tkey_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 7981e03d32bSDmitry Safonov if (!tkey_buf) 7991e03d32bSDmitry Safonov return -ENOMEM; 8001e03d32bSDmitry Safonov traffic_key = tkey_buf; 8011e03d32bSDmitry Safonov } else { 8021e03d32bSDmitry Safonov disn = ao->risn; 8031e03d32bSDmitry Safonov } 8041e03d32bSDmitry Safonov tp->af_specific->ao_calc_key_sk(key, traffic_key, 8051e03d32bSDmitry Safonov sk, ao->lisn, disn, true); 8061e03d32bSDmitry Safonov } 80764382c71SDmitry Safonov sne = tcp_ao_compute_sne(READ_ONCE(ao->snd_sne), READ_ONCE(tp->snd_una), 80864382c71SDmitry Safonov ntohl(th->seq)); 8091e03d32bSDmitry Safonov tp->af_specific->calc_ao_hash(hash_location, key, sk, skb, traffic_key, 81064382c71SDmitry Safonov hash_location - (u8 *)th, sne); 8111e03d32bSDmitry Safonov kfree(tkey_buf); 8121e03d32bSDmitry Safonov return 0; 8131e03d32bSDmitry Safonov } 8141e03d32bSDmitry Safonov 81506b22ef2SDmitry Safonov static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family, 81606b22ef2SDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 81706b22ef2SDmitry Safonov int sndid, int rcvid) 81806b22ef2SDmitry Safonov { 81906b22ef2SDmitry Safonov if (family == AF_INET) { 82006b22ef2SDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 82106b22ef2SDmitry Safonov 82206b22ef2SDmitry Safonov return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr, 82306b22ef2SDmitry Safonov AF_INET, sndid, rcvid); 82406b22ef2SDmitry Safonov } else { 82506b22ef2SDmitry Safonov const struct ipv6hdr *iph = ipv6_hdr(skb); 82606b22ef2SDmitry Safonov 82706b22ef2SDmitry Safonov return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr, 82806b22ef2SDmitry Safonov AF_INET6, sndid, rcvid); 82906b22ef2SDmitry Safonov } 83006b22ef2SDmitry Safonov } 83106b22ef2SDmitry Safonov 83206b22ef2SDmitry Safonov void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, 83306b22ef2SDmitry Safonov struct tcp_request_sock *treq, 83406b22ef2SDmitry Safonov unsigned short int family) 83506b22ef2SDmitry Safonov { 83606b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 83706b22ef2SDmitry Safonov const struct tcp_ao_hdr *aoh; 83806b22ef2SDmitry Safonov struct tcp_ao_key *key; 83906b22ef2SDmitry Safonov 84006b22ef2SDmitry Safonov treq->maclen = 0; 84106b22ef2SDmitry Safonov 84206b22ef2SDmitry Safonov if (tcp_parse_auth_options(th, NULL, &aoh) || !aoh) 84306b22ef2SDmitry Safonov return; 84406b22ef2SDmitry Safonov 84506b22ef2SDmitry Safonov key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid); 84606b22ef2SDmitry Safonov if (!key) 84706b22ef2SDmitry Safonov /* Key not found, continue without TCP-AO */ 84806b22ef2SDmitry Safonov return; 84906b22ef2SDmitry Safonov 85006b22ef2SDmitry Safonov treq->ao_rcv_next = aoh->keyid; 85106b22ef2SDmitry Safonov treq->ao_keyid = aoh->rnext_keyid; 85206b22ef2SDmitry Safonov treq->maclen = tcp_ao_maclen(key); 85306b22ef2SDmitry Safonov } 85406b22ef2SDmitry Safonov 8550a3a8090SDmitry Safonov static enum skb_drop_reason 8560a3a8090SDmitry Safonov tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, 8570a3a8090SDmitry Safonov unsigned short int family, struct tcp_ao_info *info, 8580a3a8090SDmitry Safonov const struct tcp_ao_hdr *aoh, struct tcp_ao_key *key, 8590a3a8090SDmitry Safonov u8 *traffic_key, u8 *phash, u32 sne) 8600a3a8090SDmitry Safonov { 8610a3a8090SDmitry Safonov u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr); 8620a3a8090SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 8630a3a8090SDmitry Safonov void *hash_buf = NULL; 8640a3a8090SDmitry Safonov 865af09a341SDmitry Safonov if (maclen != tcp_ao_maclen(key)) { 866af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); 867af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_bad); 868af09a341SDmitry Safonov atomic64_inc(&key->pkt_bad); 8692717b5adSDmitry Safonov tcp_hash_fail("AO hash wrong length", family, skb, 8702717b5adSDmitry Safonov "%u != %d", maclen, tcp_ao_maclen(key)); 8710a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 872af09a341SDmitry Safonov } 8730a3a8090SDmitry Safonov 8740a3a8090SDmitry Safonov hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 8750a3a8090SDmitry Safonov if (!hash_buf) 8760a3a8090SDmitry Safonov return SKB_DROP_REASON_NOT_SPECIFIED; 8770a3a8090SDmitry Safonov 8780a3a8090SDmitry Safonov /* XXX: make it per-AF callback? */ 8790a3a8090SDmitry Safonov tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key, 8800a3a8090SDmitry Safonov (phash - (u8 *)th), sne); 8810a3a8090SDmitry Safonov if (memcmp(phash, hash_buf, maclen)) { 882af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); 883af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_bad); 884af09a341SDmitry Safonov atomic64_inc(&key->pkt_bad); 8852717b5adSDmitry Safonov tcp_hash_fail("AO hash mismatch", family, skb, ""); 8860a3a8090SDmitry Safonov kfree(hash_buf); 8870a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 8880a3a8090SDmitry Safonov } 889af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD); 890af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_good); 891af09a341SDmitry Safonov atomic64_inc(&key->pkt_good); 8920a3a8090SDmitry Safonov kfree(hash_buf); 8930a3a8090SDmitry Safonov return SKB_NOT_DROPPED_YET; 8940a3a8090SDmitry Safonov } 8950a3a8090SDmitry Safonov 8960a3a8090SDmitry Safonov enum skb_drop_reason 8970a3a8090SDmitry Safonov tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, 8980a3a8090SDmitry Safonov unsigned short int family, const struct request_sock *req, 8990a3a8090SDmitry Safonov const struct tcp_ao_hdr *aoh) 9000a3a8090SDmitry Safonov { 9010a3a8090SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 9020a3a8090SDmitry Safonov u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */ 9030a3a8090SDmitry Safonov struct tcp_ao_info *info; 9040a3a8090SDmitry Safonov enum skb_drop_reason ret; 9050a3a8090SDmitry Safonov struct tcp_ao_key *key; 9060a3a8090SDmitry Safonov __be32 sisn, disn; 9070a3a8090SDmitry Safonov u8 *traffic_key; 9080a3a8090SDmitry Safonov u32 sne = 0; 9090a3a8090SDmitry Safonov 9100a3a8090SDmitry Safonov info = rcu_dereference(tcp_sk(sk)->ao_info); 911af09a341SDmitry Safonov if (!info) { 912af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); 9132717b5adSDmitry Safonov tcp_hash_fail("AO key not found", family, skb, 9142717b5adSDmitry Safonov "keyid: %u", aoh->keyid); 9150a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOUNEXPECTED; 916af09a341SDmitry Safonov } 9170a3a8090SDmitry Safonov 9180a3a8090SDmitry Safonov if (unlikely(th->syn)) { 9190a3a8090SDmitry Safonov sisn = th->seq; 9200a3a8090SDmitry Safonov disn = 0; 9210a3a8090SDmitry Safonov } 9220a3a8090SDmitry Safonov 9230a3a8090SDmitry Safonov /* Fast-path */ 9240a3a8090SDmitry Safonov if (likely((1 << sk->sk_state) & TCP_AO_ESTABLISHED)) { 9250a3a8090SDmitry Safonov enum skb_drop_reason err; 9260a3a8090SDmitry Safonov struct tcp_ao_key *current_key; 9270a3a8090SDmitry Safonov 9280a3a8090SDmitry Safonov /* Check if this socket's rnext_key matches the keyid in the 9290a3a8090SDmitry Safonov * packet. If not we lookup the key based on the keyid 9300a3a8090SDmitry Safonov * matching the rcvid in the mkt. 9310a3a8090SDmitry Safonov */ 9320a3a8090SDmitry Safonov key = READ_ONCE(info->rnext_key); 9330a3a8090SDmitry Safonov if (key->rcvid != aoh->keyid) { 9340a3a8090SDmitry Safonov key = tcp_ao_established_key(info, -1, aoh->keyid); 9350a3a8090SDmitry Safonov if (!key) 9360a3a8090SDmitry Safonov goto key_not_found; 9370a3a8090SDmitry Safonov } 9380a3a8090SDmitry Safonov 9390a3a8090SDmitry Safonov /* Delayed retransmitted SYN */ 9400a3a8090SDmitry Safonov if (unlikely(th->syn && !th->ack)) 9410a3a8090SDmitry Safonov goto verify_hash; 9420a3a8090SDmitry Safonov 94364382c71SDmitry Safonov sne = tcp_ao_compute_sne(info->rcv_sne, tcp_sk(sk)->rcv_nxt, 94464382c71SDmitry Safonov ntohl(th->seq)); 9450a3a8090SDmitry Safonov /* Established socket, traffic key are cached */ 9460a3a8090SDmitry Safonov traffic_key = rcv_other_key(key); 9470a3a8090SDmitry Safonov err = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, 9480a3a8090SDmitry Safonov traffic_key, phash, sne); 9490a3a8090SDmitry Safonov if (err) 9500a3a8090SDmitry Safonov return err; 9510a3a8090SDmitry Safonov current_key = READ_ONCE(info->current_key); 9520a3a8090SDmitry Safonov /* Key rotation: the peer asks us to use new key (RNext) */ 9530a3a8090SDmitry Safonov if (unlikely(aoh->rnext_keyid != current_key->sndid)) { 9540a3a8090SDmitry Safonov /* If the key is not found we do nothing. */ 9550a3a8090SDmitry Safonov key = tcp_ao_established_key(info, aoh->rnext_keyid, -1); 9560a3a8090SDmitry Safonov if (key) 9570a3a8090SDmitry Safonov /* pairs with tcp_ao_del_cmd */ 9580a3a8090SDmitry Safonov WRITE_ONCE(info->current_key, key); 9590a3a8090SDmitry Safonov } 9600a3a8090SDmitry Safonov return SKB_NOT_DROPPED_YET; 9610a3a8090SDmitry Safonov } 9620a3a8090SDmitry Safonov 9630a3a8090SDmitry Safonov /* Lookup key based on peer address and keyid. 9640a3a8090SDmitry Safonov * current_key and rnext_key must not be used on tcp listen 9650a3a8090SDmitry Safonov * sockets as otherwise: 9660a3a8090SDmitry Safonov * - request sockets would race on those key pointers 9670a3a8090SDmitry Safonov * - tcp_ao_del_cmd() allows async key removal 9680a3a8090SDmitry Safonov */ 9690a3a8090SDmitry Safonov key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid); 9700a3a8090SDmitry Safonov if (!key) 9710a3a8090SDmitry Safonov goto key_not_found; 9720a3a8090SDmitry Safonov 9730a3a8090SDmitry Safonov if (th->syn && !th->ack) 9740a3a8090SDmitry Safonov goto verify_hash; 9750a3a8090SDmitry Safonov 9760a3a8090SDmitry Safonov if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { 9770a3a8090SDmitry Safonov /* Make the initial syn the likely case here */ 9780a3a8090SDmitry Safonov if (unlikely(req)) { 97964382c71SDmitry Safonov sne = tcp_ao_compute_sne(0, tcp_rsk(req)->rcv_isn, 98064382c71SDmitry Safonov ntohl(th->seq)); 9810a3a8090SDmitry Safonov sisn = htonl(tcp_rsk(req)->rcv_isn); 9820a3a8090SDmitry Safonov disn = htonl(tcp_rsk(req)->snt_isn); 9830a3a8090SDmitry Safonov } else if (unlikely(th->ack && !th->syn)) { 9840a3a8090SDmitry Safonov /* Possible syncookie packet */ 9850a3a8090SDmitry Safonov sisn = htonl(ntohl(th->seq) - 1); 9860a3a8090SDmitry Safonov disn = htonl(ntohl(th->ack_seq) - 1); 98764382c71SDmitry Safonov sne = tcp_ao_compute_sne(0, ntohl(sisn), 98864382c71SDmitry Safonov ntohl(th->seq)); 9890a3a8090SDmitry Safonov } else if (unlikely(!th->syn)) { 9900a3a8090SDmitry Safonov /* no way to figure out initial sisn/disn - drop */ 9910a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_FLAGS; 9920a3a8090SDmitry Safonov } 9930a3a8090SDmitry Safonov } else if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { 9940a3a8090SDmitry Safonov disn = info->lisn; 9950a3a8090SDmitry Safonov if (th->syn || th->rst) 9960a3a8090SDmitry Safonov sisn = th->seq; 9970a3a8090SDmitry Safonov else 9980a3a8090SDmitry Safonov sisn = info->risn; 9990a3a8090SDmitry Safonov } else { 10000a3a8090SDmitry Safonov WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", sk->sk_state); 10010a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 10020a3a8090SDmitry Safonov } 10030a3a8090SDmitry Safonov verify_hash: 10040a3a8090SDmitry Safonov traffic_key = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 10050a3a8090SDmitry Safonov if (!traffic_key) 10060a3a8090SDmitry Safonov return SKB_DROP_REASON_NOT_SPECIFIED; 10070a3a8090SDmitry Safonov tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family); 10080a3a8090SDmitry Safonov ret = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, 10090a3a8090SDmitry Safonov traffic_key, phash, sne); 10100a3a8090SDmitry Safonov kfree(traffic_key); 10110a3a8090SDmitry Safonov return ret; 10120a3a8090SDmitry Safonov 10130a3a8090SDmitry Safonov key_not_found: 1014af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); 1015af09a341SDmitry Safonov atomic64_inc(&info->counters.key_not_found); 10162717b5adSDmitry Safonov tcp_hash_fail("Requested by the peer AO key id not found", 10172717b5adSDmitry Safonov family, skb, ""); 10180a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; 10190a3a8090SDmitry Safonov } 10200a3a8090SDmitry Safonov 10217c2ffaf2SDmitry Safonov static int tcp_ao_cache_traffic_keys(const struct sock *sk, 10227c2ffaf2SDmitry Safonov struct tcp_ao_info *ao, 10237c2ffaf2SDmitry Safonov struct tcp_ao_key *ao_key) 10247c2ffaf2SDmitry Safonov { 10257c2ffaf2SDmitry Safonov u8 *traffic_key = snd_other_key(ao_key); 10267c2ffaf2SDmitry Safonov int ret; 10277c2ffaf2SDmitry Safonov 10287c2ffaf2SDmitry Safonov ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk, 10297c2ffaf2SDmitry Safonov ao->lisn, ao->risn, true); 10307c2ffaf2SDmitry Safonov if (ret) 10317c2ffaf2SDmitry Safonov return ret; 10327c2ffaf2SDmitry Safonov 10337c2ffaf2SDmitry Safonov traffic_key = rcv_other_key(ao_key); 10347c2ffaf2SDmitry Safonov ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk, 10357c2ffaf2SDmitry Safonov ao->lisn, ao->risn, false); 10367c2ffaf2SDmitry Safonov return ret; 10377c2ffaf2SDmitry Safonov } 10387c2ffaf2SDmitry Safonov 10397c2ffaf2SDmitry Safonov void tcp_ao_connect_init(struct sock *sk) 10407c2ffaf2SDmitry Safonov { 10417c2ffaf2SDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 10427c2ffaf2SDmitry Safonov struct tcp_ao_info *ao_info; 10437c2ffaf2SDmitry Safonov union tcp_ao_addr *addr; 10447c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 10457c2ffaf2SDmitry Safonov int family; 10467c2ffaf2SDmitry Safonov 10477c2ffaf2SDmitry Safonov ao_info = rcu_dereference_protected(tp->ao_info, 10487c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 10497c2ffaf2SDmitry Safonov if (!ao_info) 10507c2ffaf2SDmitry Safonov return; 10517c2ffaf2SDmitry Safonov 10527c2ffaf2SDmitry Safonov /* Remove all keys that don't match the peer */ 10537c2ffaf2SDmitry Safonov family = sk->sk_family; 10547c2ffaf2SDmitry Safonov if (family == AF_INET) 10557c2ffaf2SDmitry Safonov addr = (union tcp_ao_addr *)&sk->sk_daddr; 10567c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 10577c2ffaf2SDmitry Safonov else if (family == AF_INET6) 10587c2ffaf2SDmitry Safonov addr = (union tcp_ao_addr *)&sk->sk_v6_daddr; 10597c2ffaf2SDmitry Safonov #endif 10607c2ffaf2SDmitry Safonov else 10617c2ffaf2SDmitry Safonov return; 10627c2ffaf2SDmitry Safonov 10637c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 10647c2ffaf2SDmitry Safonov if (!tcp_ao_key_cmp(key, addr, key->prefixlen, family, -1, -1)) 10657c2ffaf2SDmitry Safonov continue; 10667c2ffaf2SDmitry Safonov 10677c2ffaf2SDmitry Safonov if (key == ao_info->current_key) 10687c2ffaf2SDmitry Safonov ao_info->current_key = NULL; 10697c2ffaf2SDmitry Safonov if (key == ao_info->rnext_key) 10707c2ffaf2SDmitry Safonov ao_info->rnext_key = NULL; 10717c2ffaf2SDmitry Safonov hlist_del_rcu(&key->node); 10727c2ffaf2SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 10737c2ffaf2SDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 10747c2ffaf2SDmitry Safonov } 10757c2ffaf2SDmitry Safonov 10767c2ffaf2SDmitry Safonov key = tp->af_specific->ao_lookup(sk, sk, -1, -1); 10777c2ffaf2SDmitry Safonov if (key) { 10787c2ffaf2SDmitry Safonov /* if current_key or rnext_key were not provided, 10797c2ffaf2SDmitry Safonov * use the first key matching the peer 10807c2ffaf2SDmitry Safonov */ 10817c2ffaf2SDmitry Safonov if (!ao_info->current_key) 10827c2ffaf2SDmitry Safonov ao_info->current_key = key; 10837c2ffaf2SDmitry Safonov if (!ao_info->rnext_key) 10847c2ffaf2SDmitry Safonov ao_info->rnext_key = key; 10857c2ffaf2SDmitry Safonov tp->tcp_header_len += tcp_ao_len(key); 10867c2ffaf2SDmitry Safonov 10877c2ffaf2SDmitry Safonov ao_info->lisn = htonl(tp->write_seq); 108864382c71SDmitry Safonov ao_info->snd_sne = 0; 10897c2ffaf2SDmitry Safonov } else { 10907c2ffaf2SDmitry Safonov /* Can't happen: tcp_connect() verifies that there's 10917c2ffaf2SDmitry Safonov * at least one tcp-ao key that matches the remote peer. 10927c2ffaf2SDmitry Safonov */ 10937c2ffaf2SDmitry Safonov WARN_ON_ONCE(1); 10947c2ffaf2SDmitry Safonov rcu_assign_pointer(tp->ao_info, NULL); 10957c2ffaf2SDmitry Safonov kfree(ao_info); 10967c2ffaf2SDmitry Safonov } 10977c2ffaf2SDmitry Safonov } 10987c2ffaf2SDmitry Safonov 10997c2ffaf2SDmitry Safonov void tcp_ao_established(struct sock *sk) 11007c2ffaf2SDmitry Safonov { 11017c2ffaf2SDmitry Safonov struct tcp_ao_info *ao; 11027c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 11037c2ffaf2SDmitry Safonov 11047c2ffaf2SDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 11057c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 11067c2ffaf2SDmitry Safonov if (!ao) 11077c2ffaf2SDmitry Safonov return; 11087c2ffaf2SDmitry Safonov 11097c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 11107c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 11117c2ffaf2SDmitry Safonov } 11127c2ffaf2SDmitry Safonov 11137c2ffaf2SDmitry Safonov void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb) 11147c2ffaf2SDmitry Safonov { 11157c2ffaf2SDmitry Safonov struct tcp_ao_info *ao; 11167c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 11177c2ffaf2SDmitry Safonov 11187c2ffaf2SDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 11197c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 11207c2ffaf2SDmitry Safonov if (!ao) 11217c2ffaf2SDmitry Safonov return; 11227c2ffaf2SDmitry Safonov 11237c2ffaf2SDmitry Safonov WRITE_ONCE(ao->risn, tcp_hdr(skb)->seq); 112464382c71SDmitry Safonov ao->rcv_sne = 0; 11257c2ffaf2SDmitry Safonov 11267c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 11277c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 11287c2ffaf2SDmitry Safonov } 11297c2ffaf2SDmitry Safonov 113006b22ef2SDmitry Safonov int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, 113106b22ef2SDmitry Safonov struct request_sock *req, struct sk_buff *skb, 113206b22ef2SDmitry Safonov int family) 113306b22ef2SDmitry Safonov { 113406b22ef2SDmitry Safonov struct tcp_ao_key *key, *new_key, *first_key; 113506b22ef2SDmitry Safonov struct tcp_ao_info *new_ao, *ao; 113606b22ef2SDmitry Safonov struct hlist_node *key_head; 113706b22ef2SDmitry Safonov union tcp_ao_addr *addr; 113806b22ef2SDmitry Safonov bool match = false; 113906b22ef2SDmitry Safonov int ret = -ENOMEM; 114006b22ef2SDmitry Safonov 114106b22ef2SDmitry Safonov ao = rcu_dereference(tcp_sk(sk)->ao_info); 114206b22ef2SDmitry Safonov if (!ao) 114306b22ef2SDmitry Safonov return 0; 114406b22ef2SDmitry Safonov 114506b22ef2SDmitry Safonov /* New socket without TCP-AO on it */ 114606b22ef2SDmitry Safonov if (!tcp_rsk_used_ao(req)) 114706b22ef2SDmitry Safonov return 0; 114806b22ef2SDmitry Safonov 114906b22ef2SDmitry Safonov new_ao = tcp_ao_alloc_info(GFP_ATOMIC); 115006b22ef2SDmitry Safonov if (!new_ao) 115106b22ef2SDmitry Safonov return -ENOMEM; 115206b22ef2SDmitry Safonov new_ao->lisn = htonl(tcp_rsk(req)->snt_isn); 115306b22ef2SDmitry Safonov new_ao->risn = htonl(tcp_rsk(req)->rcv_isn); 115406b22ef2SDmitry Safonov new_ao->ao_required = ao->ao_required; 1155953af8e3SDmitry Safonov new_ao->accept_icmps = ao->accept_icmps; 115606b22ef2SDmitry Safonov 115706b22ef2SDmitry Safonov if (family == AF_INET) { 115806b22ef2SDmitry Safonov addr = (union tcp_ao_addr *)&newsk->sk_daddr; 115906b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 116006b22ef2SDmitry Safonov } else if (family == AF_INET6) { 116106b22ef2SDmitry Safonov addr = (union tcp_ao_addr *)&newsk->sk_v6_daddr; 116206b22ef2SDmitry Safonov #endif 116306b22ef2SDmitry Safonov } else { 116406b22ef2SDmitry Safonov ret = -EAFNOSUPPORT; 116506b22ef2SDmitry Safonov goto free_ao; 116606b22ef2SDmitry Safonov } 116706b22ef2SDmitry Safonov 116806b22ef2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 116906b22ef2SDmitry Safonov if (tcp_ao_key_cmp(key, addr, key->prefixlen, family, -1, -1)) 117006b22ef2SDmitry Safonov continue; 117106b22ef2SDmitry Safonov 117206b22ef2SDmitry Safonov new_key = tcp_ao_copy_key(newsk, key); 117306b22ef2SDmitry Safonov if (!new_key) 117406b22ef2SDmitry Safonov goto free_and_exit; 117506b22ef2SDmitry Safonov 117606b22ef2SDmitry Safonov tcp_ao_cache_traffic_keys(newsk, new_ao, new_key); 117706b22ef2SDmitry Safonov tcp_ao_link_mkt(new_ao, new_key); 117806b22ef2SDmitry Safonov match = true; 117906b22ef2SDmitry Safonov } 118006b22ef2SDmitry Safonov 118106b22ef2SDmitry Safonov if (!match) { 118206b22ef2SDmitry Safonov /* RFC5925 (7.4.1) specifies that the TCP-AO status 118306b22ef2SDmitry Safonov * of a connection is determined on the initial SYN. 118406b22ef2SDmitry Safonov * At this point the connection was TCP-AO enabled, so 118506b22ef2SDmitry Safonov * it can't switch to being unsigned if peer's key 118606b22ef2SDmitry Safonov * disappears on the listening socket. 118706b22ef2SDmitry Safonov */ 118806b22ef2SDmitry Safonov ret = -EKEYREJECTED; 118906b22ef2SDmitry Safonov goto free_and_exit; 119006b22ef2SDmitry Safonov } 119106b22ef2SDmitry Safonov 1192*67fa83f7SDmitry Safonov if (!static_key_fast_inc_not_disabled(&tcp_ao_needed.key.key)) { 1193*67fa83f7SDmitry Safonov ret = -EUSERS; 1194*67fa83f7SDmitry Safonov goto free_and_exit; 1195*67fa83f7SDmitry Safonov } 1196*67fa83f7SDmitry Safonov 119706b22ef2SDmitry Safonov key_head = rcu_dereference(hlist_first_rcu(&new_ao->head)); 119806b22ef2SDmitry Safonov first_key = hlist_entry_safe(key_head, struct tcp_ao_key, node); 119906b22ef2SDmitry Safonov 120006b22ef2SDmitry Safonov key = tcp_ao_established_key(new_ao, tcp_rsk(req)->ao_keyid, -1); 120106b22ef2SDmitry Safonov if (key) 120206b22ef2SDmitry Safonov new_ao->current_key = key; 120306b22ef2SDmitry Safonov else 120406b22ef2SDmitry Safonov new_ao->current_key = first_key; 120506b22ef2SDmitry Safonov 120606b22ef2SDmitry Safonov /* set rnext_key */ 120706b22ef2SDmitry Safonov key = tcp_ao_established_key(new_ao, -1, tcp_rsk(req)->ao_rcv_next); 120806b22ef2SDmitry Safonov if (key) 120906b22ef2SDmitry Safonov new_ao->rnext_key = key; 121006b22ef2SDmitry Safonov else 121106b22ef2SDmitry Safonov new_ao->rnext_key = first_key; 121206b22ef2SDmitry Safonov 121306b22ef2SDmitry Safonov sk_gso_disable(newsk); 121406b22ef2SDmitry Safonov rcu_assign_pointer(tcp_sk(newsk)->ao_info, new_ao); 121506b22ef2SDmitry Safonov 121606b22ef2SDmitry Safonov return 0; 121706b22ef2SDmitry Safonov 121806b22ef2SDmitry Safonov free_and_exit: 121906b22ef2SDmitry Safonov hlist_for_each_entry_safe(key, key_head, &new_ao->head, node) { 122006b22ef2SDmitry Safonov hlist_del(&key->node); 122106b22ef2SDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 122206b22ef2SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc); 122306b22ef2SDmitry Safonov kfree_sensitive(key); 122406b22ef2SDmitry Safonov } 122506b22ef2SDmitry Safonov free_ao: 122606b22ef2SDmitry Safonov kfree(new_ao); 122706b22ef2SDmitry Safonov return ret; 122806b22ef2SDmitry Safonov } 122906b22ef2SDmitry Safonov 12304954f17dSDmitry Safonov static bool tcp_ao_can_set_current_rnext(struct sock *sk) 12314954f17dSDmitry Safonov { 12324954f17dSDmitry Safonov /* There aren't current/rnext keys on TCP_LISTEN sockets */ 12334954f17dSDmitry Safonov if (sk->sk_state == TCP_LISTEN) 12344954f17dSDmitry Safonov return false; 12354954f17dSDmitry Safonov return true; 12364954f17dSDmitry Safonov } 12374954f17dSDmitry Safonov 12384954f17dSDmitry Safonov static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao_add *cmd, 12394954f17dSDmitry Safonov union tcp_ao_addr **addr) 12404954f17dSDmitry Safonov { 12414954f17dSDmitry Safonov struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->addr; 12424954f17dSDmitry Safonov struct inet_sock *inet = inet_sk(sk); 12434954f17dSDmitry Safonov 12444954f17dSDmitry Safonov if (sin->sin_family != AF_INET) 12454954f17dSDmitry Safonov return -EINVAL; 12464954f17dSDmitry Safonov 12474954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 12484954f17dSDmitry Safonov if (sin->sin_port != 0) 12494954f17dSDmitry Safonov return -EINVAL; 12504954f17dSDmitry Safonov 12514954f17dSDmitry Safonov /* Check prefix and trailing 0's in addr */ 12524954f17dSDmitry Safonov if (cmd->prefix != 0) { 12534954f17dSDmitry Safonov __be32 mask; 12544954f17dSDmitry Safonov 12554954f17dSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY) 12564954f17dSDmitry Safonov return -EINVAL; 12574954f17dSDmitry Safonov if (cmd->prefix > 32) 12584954f17dSDmitry Safonov return -EINVAL; 12594954f17dSDmitry Safonov 12604954f17dSDmitry Safonov mask = inet_make_mask(cmd->prefix); 12614954f17dSDmitry Safonov if (sin->sin_addr.s_addr & ~mask) 12624954f17dSDmitry Safonov return -EINVAL; 12634954f17dSDmitry Safonov 12644954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 12654954f17dSDmitry Safonov if (ntohl(inet->inet_daddr) != INADDR_ANY && 12664954f17dSDmitry Safonov (inet->inet_daddr & mask) != sin->sin_addr.s_addr) 12674954f17dSDmitry Safonov return -EINVAL; 12684954f17dSDmitry Safonov } else { 12694954f17dSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) != INADDR_ANY) 12704954f17dSDmitry Safonov return -EINVAL; 12714954f17dSDmitry Safonov } 12724954f17dSDmitry Safonov 12734954f17dSDmitry Safonov *addr = (union tcp_ao_addr *)&sin->sin_addr; 12744954f17dSDmitry Safonov return 0; 12754954f17dSDmitry Safonov } 12764954f17dSDmitry Safonov 12774954f17dSDmitry Safonov static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key) 12784954f17dSDmitry Safonov { 12794954f17dSDmitry Safonov unsigned int syn_tcp_option_space; 12804954f17dSDmitry Safonov bool is_kdf_aes_128_cmac = false; 12814954f17dSDmitry Safonov struct crypto_ahash *tfm; 12824954f17dSDmitry Safonov struct tcp_sigpool hp; 12834954f17dSDmitry Safonov void *tmp_key = NULL; 12844954f17dSDmitry Safonov int err; 12854954f17dSDmitry Safonov 12864954f17dSDmitry Safonov /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 12874954f17dSDmitry Safonov if (!strcmp("cmac(aes128)", cmd->alg_name)) { 12884954f17dSDmitry Safonov strscpy(cmd->alg_name, "cmac(aes)", sizeof(cmd->alg_name)); 12894954f17dSDmitry Safonov is_kdf_aes_128_cmac = (cmd->keylen != 16); 12904954f17dSDmitry Safonov tmp_key = kmalloc(cmd->keylen, GFP_KERNEL); 12914954f17dSDmitry Safonov if (!tmp_key) 12924954f17dSDmitry Safonov return -ENOMEM; 12934954f17dSDmitry Safonov } 12944954f17dSDmitry Safonov 12954954f17dSDmitry Safonov key->maclen = cmd->maclen ?: 12; /* 12 is the default in RFC5925 */ 12964954f17dSDmitry Safonov 12974954f17dSDmitry Safonov /* Check: maclen + tcp-ao header <= (MAX_TCP_OPTION_SPACE - mss 12984954f17dSDmitry Safonov * - tstamp - wscale - sackperm), 12994954f17dSDmitry Safonov * see tcp_syn_options(), tcp_synack_options(), commit 33ad798c924b. 13004954f17dSDmitry Safonov * 13014954f17dSDmitry Safonov * In order to allow D-SACK with TCP-AO, the header size should be: 13024954f17dSDmitry Safonov * (MAX_TCP_OPTION_SPACE - TCPOLEN_TSTAMP_ALIGNED 13034954f17dSDmitry Safonov * - TCPOLEN_SACK_BASE_ALIGNED 13044954f17dSDmitry Safonov * - 2 * TCPOLEN_SACK_PERBLOCK) = 8 (maclen = 4), 13054954f17dSDmitry Safonov * see tcp_established_options(). 13064954f17dSDmitry Safonov * 13074954f17dSDmitry Safonov * RFC5925, 2.2: 13084954f17dSDmitry Safonov * Typical MACs are 96-128 bits (12-16 bytes), but any length 13094954f17dSDmitry Safonov * that fits in the header of the segment being authenticated 13104954f17dSDmitry Safonov * is allowed. 13114954f17dSDmitry Safonov * 13124954f17dSDmitry Safonov * RFC5925, 7.6: 13134954f17dSDmitry Safonov * TCP-AO continues to consume 16 bytes in non-SYN segments, 13144954f17dSDmitry Safonov * leaving a total of 24 bytes for other options, of which 13154954f17dSDmitry Safonov * the timestamp consumes 10. This leaves 14 bytes, of which 10 13164954f17dSDmitry Safonov * are used for a single SACK block. When two SACK blocks are used, 13174954f17dSDmitry Safonov * such as to handle D-SACK, a smaller TCP-AO MAC would be required 13184954f17dSDmitry Safonov * to make room for the additional SACK block (i.e., to leave 18 13194954f17dSDmitry Safonov * bytes for the D-SACK variant of the SACK option) [RFC2883]. 13204954f17dSDmitry Safonov * Note that D-SACK is not supportable in TCP MD5 in the presence 13214954f17dSDmitry Safonov * of timestamps, because TCP MD5’s MAC length is fixed and too 13224954f17dSDmitry Safonov * large to leave sufficient option space. 13234954f17dSDmitry Safonov */ 13244954f17dSDmitry Safonov syn_tcp_option_space = MAX_TCP_OPTION_SPACE; 13254954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED; 13264954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED; 13274954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_SACKPERM_ALIGNED; 13284954f17dSDmitry Safonov if (tcp_ao_len(key) > syn_tcp_option_space) { 13294954f17dSDmitry Safonov err = -EMSGSIZE; 13304954f17dSDmitry Safonov goto err_kfree; 13314954f17dSDmitry Safonov } 13324954f17dSDmitry Safonov 13334954f17dSDmitry Safonov key->keylen = cmd->keylen; 13344954f17dSDmitry Safonov memcpy(key->key, cmd->key, cmd->keylen); 13354954f17dSDmitry Safonov 13364954f17dSDmitry Safonov err = tcp_sigpool_start(key->tcp_sigpool_id, &hp); 13374954f17dSDmitry Safonov if (err) 13384954f17dSDmitry Safonov goto err_kfree; 13394954f17dSDmitry Safonov 13404954f17dSDmitry Safonov tfm = crypto_ahash_reqtfm(hp.req); 13414954f17dSDmitry Safonov if (is_kdf_aes_128_cmac) { 13424954f17dSDmitry Safonov void *scratch = hp.scratch; 13434954f17dSDmitry Safonov struct scatterlist sg; 13444954f17dSDmitry Safonov 13454954f17dSDmitry Safonov memcpy(tmp_key, cmd->key, cmd->keylen); 13464954f17dSDmitry Safonov sg_init_one(&sg, tmp_key, cmd->keylen); 13474954f17dSDmitry Safonov 13484954f17dSDmitry Safonov /* Using zero-key of 16 bytes as described in RFC5926 */ 13494954f17dSDmitry Safonov memset(scratch, 0, 16); 13504954f17dSDmitry Safonov err = crypto_ahash_setkey(tfm, scratch, 16); 13514954f17dSDmitry Safonov if (err) 13524954f17dSDmitry Safonov goto err_pool_end; 13534954f17dSDmitry Safonov 13544954f17dSDmitry Safonov err = crypto_ahash_init(hp.req); 13554954f17dSDmitry Safonov if (err) 13564954f17dSDmitry Safonov goto err_pool_end; 13574954f17dSDmitry Safonov 13584954f17dSDmitry Safonov ahash_request_set_crypt(hp.req, &sg, key->key, cmd->keylen); 13594954f17dSDmitry Safonov err = crypto_ahash_update(hp.req); 13604954f17dSDmitry Safonov if (err) 13614954f17dSDmitry Safonov goto err_pool_end; 13624954f17dSDmitry Safonov 13634954f17dSDmitry Safonov err |= crypto_ahash_final(hp.req); 13644954f17dSDmitry Safonov if (err) 13654954f17dSDmitry Safonov goto err_pool_end; 13664954f17dSDmitry Safonov key->keylen = 16; 13674954f17dSDmitry Safonov } 13684954f17dSDmitry Safonov 13694954f17dSDmitry Safonov err = crypto_ahash_setkey(tfm, key->key, key->keylen); 13704954f17dSDmitry Safonov if (err) 13714954f17dSDmitry Safonov goto err_pool_end; 13724954f17dSDmitry Safonov 13734954f17dSDmitry Safonov tcp_sigpool_end(&hp); 13744954f17dSDmitry Safonov kfree_sensitive(tmp_key); 13754954f17dSDmitry Safonov 13764954f17dSDmitry Safonov if (tcp_ao_maclen(key) > key->digest_size) 13774954f17dSDmitry Safonov return -EINVAL; 13784954f17dSDmitry Safonov 13794954f17dSDmitry Safonov return 0; 13804954f17dSDmitry Safonov 13814954f17dSDmitry Safonov err_pool_end: 13824954f17dSDmitry Safonov tcp_sigpool_end(&hp); 13834954f17dSDmitry Safonov err_kfree: 13844954f17dSDmitry Safonov kfree_sensitive(tmp_key); 13854954f17dSDmitry Safonov return err; 13864954f17dSDmitry Safonov } 13874954f17dSDmitry Safonov 13884954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 13894954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 13904954f17dSDmitry Safonov union tcp_ao_addr **paddr, 13914954f17dSDmitry Safonov unsigned short int *family) 13924954f17dSDmitry Safonov { 13934954f17dSDmitry Safonov struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->addr; 13944954f17dSDmitry Safonov struct in6_addr *addr = &sin6->sin6_addr; 13954954f17dSDmitry Safonov u8 prefix = cmd->prefix; 13964954f17dSDmitry Safonov 13974954f17dSDmitry Safonov if (sin6->sin6_family != AF_INET6) 13984954f17dSDmitry Safonov return -EINVAL; 13994954f17dSDmitry Safonov 14004954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 14014954f17dSDmitry Safonov if (sin6->sin6_port != 0) 14024954f17dSDmitry Safonov return -EINVAL; 14034954f17dSDmitry Safonov 14044954f17dSDmitry Safonov /* Check prefix and trailing 0's in addr */ 14054954f17dSDmitry Safonov if (cmd->prefix != 0 && ipv6_addr_v4mapped(addr)) { 14064954f17dSDmitry Safonov __be32 addr4 = addr->s6_addr32[3]; 14074954f17dSDmitry Safonov __be32 mask; 14084954f17dSDmitry Safonov 14094954f17dSDmitry Safonov if (prefix > 32 || ntohl(addr4) == INADDR_ANY) 14104954f17dSDmitry Safonov return -EINVAL; 14114954f17dSDmitry Safonov 14124954f17dSDmitry Safonov mask = inet_make_mask(prefix); 14134954f17dSDmitry Safonov if (addr4 & ~mask) 14144954f17dSDmitry Safonov return -EINVAL; 14154954f17dSDmitry Safonov 14164954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 14174954f17dSDmitry Safonov if (!ipv6_addr_any(&sk->sk_v6_daddr)) { 14184954f17dSDmitry Safonov __be32 daddr4 = sk->sk_v6_daddr.s6_addr32[3]; 14194954f17dSDmitry Safonov 14204954f17dSDmitry Safonov if (!ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 14214954f17dSDmitry Safonov return -EINVAL; 14224954f17dSDmitry Safonov if ((daddr4 & mask) != addr4) 14234954f17dSDmitry Safonov return -EINVAL; 14244954f17dSDmitry Safonov } 14254954f17dSDmitry Safonov 14264954f17dSDmitry Safonov *paddr = (union tcp_ao_addr *)&addr->s6_addr32[3]; 14274954f17dSDmitry Safonov *family = AF_INET; 14284954f17dSDmitry Safonov return 0; 14294954f17dSDmitry Safonov } else if (cmd->prefix != 0) { 14304954f17dSDmitry Safonov struct in6_addr pfx; 14314954f17dSDmitry Safonov 14324954f17dSDmitry Safonov if (ipv6_addr_any(addr) || prefix > 128) 14334954f17dSDmitry Safonov return -EINVAL; 14344954f17dSDmitry Safonov 14354954f17dSDmitry Safonov ipv6_addr_prefix(&pfx, addr, prefix); 14364954f17dSDmitry Safonov if (ipv6_addr_cmp(&pfx, addr)) 14374954f17dSDmitry Safonov return -EINVAL; 14384954f17dSDmitry Safonov 14394954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 14404954f17dSDmitry Safonov if (!ipv6_addr_any(&sk->sk_v6_daddr) && 14414954f17dSDmitry Safonov !ipv6_prefix_equal(&sk->sk_v6_daddr, addr, prefix)) 14424954f17dSDmitry Safonov 14434954f17dSDmitry Safonov return -EINVAL; 14444954f17dSDmitry Safonov } else { 14454954f17dSDmitry Safonov if (!ipv6_addr_any(addr)) 14464954f17dSDmitry Safonov return -EINVAL; 14474954f17dSDmitry Safonov } 14484954f17dSDmitry Safonov 14494954f17dSDmitry Safonov *paddr = (union tcp_ao_addr *)addr; 14504954f17dSDmitry Safonov return 0; 14514954f17dSDmitry Safonov } 14524954f17dSDmitry Safonov #else 14534954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 14544954f17dSDmitry Safonov union tcp_ao_addr **paddr, 14554954f17dSDmitry Safonov unsigned short int *family) 14564954f17dSDmitry Safonov { 14574954f17dSDmitry Safonov return -EOPNOTSUPP; 14584954f17dSDmitry Safonov } 14594954f17dSDmitry Safonov #endif 14604954f17dSDmitry Safonov 14614954f17dSDmitry Safonov static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) 14624954f17dSDmitry Safonov { 14634954f17dSDmitry Safonov if (sk_fullsock(sk)) { 14644954f17dSDmitry Safonov return rcu_dereference_protected(tcp_sk(sk)->ao_info, 14654954f17dSDmitry Safonov lockdep_sock_is_held(sk)); 1466decde258SDmitry Safonov } else if (sk->sk_state == TCP_TIME_WAIT) { 1467decde258SDmitry Safonov return rcu_dereference_protected(tcp_twsk(sk)->ao_info, 1468decde258SDmitry Safonov lockdep_sock_is_held(sk)); 14694954f17dSDmitry Safonov } 14704954f17dSDmitry Safonov return ERR_PTR(-ESOCKTNOSUPPORT); 14714954f17dSDmitry Safonov } 14724954f17dSDmitry Safonov 14737753c2f0SDmitry Safonov #define TCP_AO_KEYF_ALL (TCP_AO_KEYF_EXCLUDE_OPT) 14744954f17dSDmitry Safonov 14754954f17dSDmitry Safonov static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk, 14764954f17dSDmitry Safonov struct tcp_ao_add *cmd) 14774954f17dSDmitry Safonov { 14784954f17dSDmitry Safonov const char *algo = cmd->alg_name; 14794954f17dSDmitry Safonov unsigned int digest_size; 14804954f17dSDmitry Safonov struct crypto_ahash *tfm; 14814954f17dSDmitry Safonov struct tcp_ao_key *key; 14824954f17dSDmitry Safonov struct tcp_sigpool hp; 14834954f17dSDmitry Safonov int err, pool_id; 14844954f17dSDmitry Safonov size_t size; 14854954f17dSDmitry Safonov 14864954f17dSDmitry Safonov /* Force null-termination of alg_name */ 14874954f17dSDmitry Safonov cmd->alg_name[ARRAY_SIZE(cmd->alg_name) - 1] = '\0'; 14884954f17dSDmitry Safonov 14894954f17dSDmitry Safonov /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 14904954f17dSDmitry Safonov if (!strcmp("cmac(aes128)", algo)) 14914954f17dSDmitry Safonov algo = "cmac(aes)"; 14924954f17dSDmitry Safonov 14934954f17dSDmitry Safonov /* Full TCP header (th->doff << 2) should fit into scratch area, 14944954f17dSDmitry Safonov * see tcp_ao_hash_header(). 14954954f17dSDmitry Safonov */ 14964954f17dSDmitry Safonov pool_id = tcp_sigpool_alloc_ahash(algo, 60); 14974954f17dSDmitry Safonov if (pool_id < 0) 14984954f17dSDmitry Safonov return ERR_PTR(pool_id); 14994954f17dSDmitry Safonov 15004954f17dSDmitry Safonov err = tcp_sigpool_start(pool_id, &hp); 15014954f17dSDmitry Safonov if (err) 15024954f17dSDmitry Safonov goto err_free_pool; 15034954f17dSDmitry Safonov 15044954f17dSDmitry Safonov tfm = crypto_ahash_reqtfm(hp.req); 15054954f17dSDmitry Safonov if (crypto_ahash_alignmask(tfm) > TCP_AO_KEY_ALIGN) { 15064954f17dSDmitry Safonov err = -EOPNOTSUPP; 15074954f17dSDmitry Safonov goto err_pool_end; 15084954f17dSDmitry Safonov } 15094954f17dSDmitry Safonov digest_size = crypto_ahash_digestsize(tfm); 15104954f17dSDmitry Safonov tcp_sigpool_end(&hp); 15114954f17dSDmitry Safonov 15124954f17dSDmitry Safonov size = sizeof(struct tcp_ao_key) + (digest_size << 1); 15134954f17dSDmitry Safonov key = sock_kmalloc(sk, size, GFP_KERNEL); 15144954f17dSDmitry Safonov if (!key) { 15154954f17dSDmitry Safonov err = -ENOMEM; 15164954f17dSDmitry Safonov goto err_free_pool; 15174954f17dSDmitry Safonov } 15184954f17dSDmitry Safonov 15194954f17dSDmitry Safonov key->tcp_sigpool_id = pool_id; 15204954f17dSDmitry Safonov key->digest_size = digest_size; 15214954f17dSDmitry Safonov return key; 15224954f17dSDmitry Safonov 15234954f17dSDmitry Safonov err_pool_end: 15244954f17dSDmitry Safonov tcp_sigpool_end(&hp); 15254954f17dSDmitry Safonov err_free_pool: 15264954f17dSDmitry Safonov tcp_sigpool_release(pool_id); 15274954f17dSDmitry Safonov return ERR_PTR(err); 15284954f17dSDmitry Safonov } 15294954f17dSDmitry Safonov 15304954f17dSDmitry Safonov static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, 15314954f17dSDmitry Safonov sockptr_t optval, int optlen) 15324954f17dSDmitry Safonov { 15334954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 15344954f17dSDmitry Safonov union tcp_ao_addr *addr; 15354954f17dSDmitry Safonov struct tcp_ao_key *key; 15364954f17dSDmitry Safonov struct tcp_ao_add cmd; 15374954f17dSDmitry Safonov bool first = false; 15384954f17dSDmitry Safonov int ret; 15394954f17dSDmitry Safonov 15404954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 15414954f17dSDmitry Safonov return -EINVAL; 15424954f17dSDmitry Safonov 15434954f17dSDmitry Safonov ret = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 15444954f17dSDmitry Safonov if (ret) 15454954f17dSDmitry Safonov return ret; 15464954f17dSDmitry Safonov 15474954f17dSDmitry Safonov if (cmd.keylen > TCP_AO_MAXKEYLEN) 15484954f17dSDmitry Safonov return -EINVAL; 15494954f17dSDmitry Safonov 15504954f17dSDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 15514954f17dSDmitry Safonov return -EINVAL; 15524954f17dSDmitry Safonov 15534954f17dSDmitry Safonov if (family == AF_INET) 15544954f17dSDmitry Safonov ret = tcp_ao_verify_ipv4(sk, &cmd, &addr); 15554954f17dSDmitry Safonov else 15564954f17dSDmitry Safonov ret = tcp_ao_verify_ipv6(sk, &cmd, &addr, &family); 15574954f17dSDmitry Safonov if (ret) 15584954f17dSDmitry Safonov return ret; 15594954f17dSDmitry Safonov 15604954f17dSDmitry Safonov if (cmd.keyflags & ~TCP_AO_KEYF_ALL) 15614954f17dSDmitry Safonov return -EINVAL; 15624954f17dSDmitry Safonov 15634954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 15644954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 15654954f17dSDmitry Safonov return -EINVAL; 15664954f17dSDmitry Safonov } 15674954f17dSDmitry Safonov 15680aadc739SDmitry Safonov /* Don't allow keys for peers that have a matching TCP-MD5 key */ 15690aadc739SDmitry Safonov if (tcp_md5_do_lookup_any_l3index(sk, addr, family)) 15700aadc739SDmitry Safonov return -EKEYREJECTED; 15710aadc739SDmitry Safonov 15724954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 15734954f17dSDmitry Safonov if (IS_ERR(ao_info)) 15744954f17dSDmitry Safonov return PTR_ERR(ao_info); 15754954f17dSDmitry Safonov 15764954f17dSDmitry Safonov if (!ao_info) { 15774954f17dSDmitry Safonov ao_info = tcp_ao_alloc_info(GFP_KERNEL); 15784954f17dSDmitry Safonov if (!ao_info) 15794954f17dSDmitry Safonov return -ENOMEM; 15804954f17dSDmitry Safonov first = true; 15814954f17dSDmitry Safonov } else { 15824954f17dSDmitry Safonov /* Check that neither RecvID nor SendID match any 15834954f17dSDmitry Safonov * existing key for the peer, RFC5925 3.1: 15844954f17dSDmitry Safonov * > The IDs of MKTs MUST NOT overlap where their 15854954f17dSDmitry Safonov * > TCP connection identifiers overlap. 15864954f17dSDmitry Safonov */ 15874954f17dSDmitry Safonov if (__tcp_ao_do_lookup(sk, addr, family, 15884954f17dSDmitry Safonov cmd.prefix, -1, cmd.rcvid)) 15894954f17dSDmitry Safonov return -EEXIST; 15904954f17dSDmitry Safonov if (__tcp_ao_do_lookup(sk, addr, family, 15914954f17dSDmitry Safonov cmd.prefix, cmd.sndid, -1)) 15924954f17dSDmitry Safonov return -EEXIST; 15934954f17dSDmitry Safonov } 15944954f17dSDmitry Safonov 15954954f17dSDmitry Safonov key = tcp_ao_key_alloc(sk, &cmd); 15964954f17dSDmitry Safonov if (IS_ERR(key)) { 15974954f17dSDmitry Safonov ret = PTR_ERR(key); 15984954f17dSDmitry Safonov goto err_free_ao; 15994954f17dSDmitry Safonov } 16004954f17dSDmitry Safonov 16014954f17dSDmitry Safonov INIT_HLIST_NODE(&key->node); 16024954f17dSDmitry Safonov memcpy(&key->addr, addr, (family == AF_INET) ? sizeof(struct in_addr) : 16034954f17dSDmitry Safonov sizeof(struct in6_addr)); 16044954f17dSDmitry Safonov key->prefixlen = cmd.prefix; 16054954f17dSDmitry Safonov key->family = family; 16064954f17dSDmitry Safonov key->keyflags = cmd.keyflags; 16074954f17dSDmitry Safonov key->sndid = cmd.sndid; 16084954f17dSDmitry Safonov key->rcvid = cmd.rcvid; 1609af09a341SDmitry Safonov atomic64_set(&key->pkt_good, 0); 1610af09a341SDmitry Safonov atomic64_set(&key->pkt_bad, 0); 16114954f17dSDmitry Safonov 16124954f17dSDmitry Safonov ret = tcp_ao_parse_crypto(&cmd, key); 16134954f17dSDmitry Safonov if (ret < 0) 16144954f17dSDmitry Safonov goto err_free_sock; 16154954f17dSDmitry Safonov 16167c2ffaf2SDmitry Safonov /* Change this condition if we allow adding keys in states 16177c2ffaf2SDmitry Safonov * like close_wait, syn_sent or fin_wait... 16187c2ffaf2SDmitry Safonov */ 16197c2ffaf2SDmitry Safonov if (sk->sk_state == TCP_ESTABLISHED) 16207c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao_info, key); 16217c2ffaf2SDmitry Safonov 16224954f17dSDmitry Safonov tcp_ao_link_mkt(ao_info, key); 16234954f17dSDmitry Safonov if (first) { 1624*67fa83f7SDmitry Safonov if (!static_branch_inc(&tcp_ao_needed.key)) { 1625*67fa83f7SDmitry Safonov ret = -EUSERS; 1626*67fa83f7SDmitry Safonov goto err_free_sock; 1627*67fa83f7SDmitry Safonov } 16284954f17dSDmitry Safonov sk_gso_disable(sk); 16294954f17dSDmitry Safonov rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 16304954f17dSDmitry Safonov } 16314954f17dSDmitry Safonov 16324954f17dSDmitry Safonov if (cmd.set_current) 16334954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, key); 16344954f17dSDmitry Safonov if (cmd.set_rnext) 16354954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, key); 16364954f17dSDmitry Safonov return 0; 16374954f17dSDmitry Safonov 16384954f17dSDmitry Safonov err_free_sock: 16394954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 16404954f17dSDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 16414954f17dSDmitry Safonov kfree_sensitive(key); 16424954f17dSDmitry Safonov err_free_ao: 16434954f17dSDmitry Safonov if (first) 16444954f17dSDmitry Safonov kfree(ao_info); 16454954f17dSDmitry Safonov return ret; 16464954f17dSDmitry Safonov } 16474954f17dSDmitry Safonov 16484954f17dSDmitry Safonov static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, 1649d6732b95SDmitry Safonov bool del_async, struct tcp_ao_key *key, 16504954f17dSDmitry Safonov struct tcp_ao_key *new_current, 16514954f17dSDmitry Safonov struct tcp_ao_key *new_rnext) 16524954f17dSDmitry Safonov { 16534954f17dSDmitry Safonov int err; 16544954f17dSDmitry Safonov 16554954f17dSDmitry Safonov hlist_del_rcu(&key->node); 16564954f17dSDmitry Safonov 1657d6732b95SDmitry Safonov /* Support for async delete on listening sockets: as they don't 1658d6732b95SDmitry Safonov * need current_key/rnext_key maintaining, we don't need to check 1659d6732b95SDmitry Safonov * them and we can just free all resources in RCU fashion. 1660d6732b95SDmitry Safonov */ 1661d6732b95SDmitry Safonov if (del_async) { 1662d6732b95SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 1663d6732b95SDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 1664d6732b95SDmitry Safonov return 0; 1665d6732b95SDmitry Safonov } 1666d6732b95SDmitry Safonov 16674954f17dSDmitry Safonov /* At this moment another CPU could have looked this key up 16684954f17dSDmitry Safonov * while it was unlinked from the list. Wait for RCU grace period, 16694954f17dSDmitry Safonov * after which the key is off-list and can't be looked up again; 16704954f17dSDmitry Safonov * the rx path [just before RCU came] might have used it and set it 16714954f17dSDmitry Safonov * as current_key (very unlikely). 1672d6732b95SDmitry Safonov * Free the key with next RCU grace period (in case it was 1673d6732b95SDmitry Safonov * current_key before tcp_ao_current_rnext() might have 1674d6732b95SDmitry Safonov * changed it in forced-delete). 16754954f17dSDmitry Safonov */ 16764954f17dSDmitry Safonov synchronize_rcu(); 16774954f17dSDmitry Safonov if (new_current) 16784954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, new_current); 16794954f17dSDmitry Safonov if (new_rnext) 16804954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, new_rnext); 16814954f17dSDmitry Safonov 16824954f17dSDmitry Safonov if (unlikely(READ_ONCE(ao_info->current_key) == key || 16834954f17dSDmitry Safonov READ_ONCE(ao_info->rnext_key) == key)) { 16844954f17dSDmitry Safonov err = -EBUSY; 16854954f17dSDmitry Safonov goto add_key; 16864954f17dSDmitry Safonov } 16874954f17dSDmitry Safonov 16884954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 16894954f17dSDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 16904954f17dSDmitry Safonov 16914954f17dSDmitry Safonov return 0; 16924954f17dSDmitry Safonov add_key: 16934954f17dSDmitry Safonov hlist_add_head_rcu(&key->node, &ao_info->head); 16944954f17dSDmitry Safonov return err; 16954954f17dSDmitry Safonov } 16964954f17dSDmitry Safonov 16974954f17dSDmitry Safonov static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, 16984954f17dSDmitry Safonov sockptr_t optval, int optlen) 16994954f17dSDmitry Safonov { 17004954f17dSDmitry Safonov struct tcp_ao_key *key, *new_current = NULL, *new_rnext = NULL; 17014954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 17024954f17dSDmitry Safonov union tcp_ao_addr *addr; 17034954f17dSDmitry Safonov struct tcp_ao_del cmd; 17044954f17dSDmitry Safonov int addr_len; 17054954f17dSDmitry Safonov __u8 prefix; 17064954f17dSDmitry Safonov u16 port; 17074954f17dSDmitry Safonov int err; 17084954f17dSDmitry Safonov 17094954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 17104954f17dSDmitry Safonov return -EINVAL; 17114954f17dSDmitry Safonov 17124954f17dSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 17134954f17dSDmitry Safonov if (err) 17144954f17dSDmitry Safonov return err; 17154954f17dSDmitry Safonov 17164954f17dSDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 17174954f17dSDmitry Safonov return -EINVAL; 17184954f17dSDmitry Safonov 17194954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 17204954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 17214954f17dSDmitry Safonov return -EINVAL; 17224954f17dSDmitry Safonov } 17234954f17dSDmitry Safonov 17244954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 17254954f17dSDmitry Safonov if (IS_ERR(ao_info)) 17264954f17dSDmitry Safonov return PTR_ERR(ao_info); 17274954f17dSDmitry Safonov if (!ao_info) 17284954f17dSDmitry Safonov return -ENOENT; 17294954f17dSDmitry Safonov 17304954f17dSDmitry Safonov /* For sockets in TCP_CLOSED it's possible set keys that aren't 17314954f17dSDmitry Safonov * matching the future peer (address/VRF/etc), 17324954f17dSDmitry Safonov * tcp_ao_connect_init() will choose a correct matching MKT 17334954f17dSDmitry Safonov * if there's any. 17344954f17dSDmitry Safonov */ 17354954f17dSDmitry Safonov if (cmd.set_current) { 17364954f17dSDmitry Safonov new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 17374954f17dSDmitry Safonov if (!new_current) 17384954f17dSDmitry Safonov return -ENOENT; 17394954f17dSDmitry Safonov } 17404954f17dSDmitry Safonov if (cmd.set_rnext) { 17414954f17dSDmitry Safonov new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 17424954f17dSDmitry Safonov if (!new_rnext) 17434954f17dSDmitry Safonov return -ENOENT; 17444954f17dSDmitry Safonov } 1745d6732b95SDmitry Safonov if (cmd.del_async && sk->sk_state != TCP_LISTEN) 1746d6732b95SDmitry Safonov return -EINVAL; 17474954f17dSDmitry Safonov 17484954f17dSDmitry Safonov if (family == AF_INET) { 17494954f17dSDmitry Safonov struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.addr; 17504954f17dSDmitry Safonov 17514954f17dSDmitry Safonov addr = (union tcp_ao_addr *)&sin->sin_addr; 17524954f17dSDmitry Safonov addr_len = sizeof(struct in_addr); 17534954f17dSDmitry Safonov port = ntohs(sin->sin_port); 17544954f17dSDmitry Safonov } else { 17554954f17dSDmitry Safonov struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.addr; 17564954f17dSDmitry Safonov struct in6_addr *addr6 = &sin6->sin6_addr; 17574954f17dSDmitry Safonov 17584954f17dSDmitry Safonov if (ipv6_addr_v4mapped(addr6)) { 17594954f17dSDmitry Safonov addr = (union tcp_ao_addr *)&addr6->s6_addr32[3]; 17604954f17dSDmitry Safonov addr_len = sizeof(struct in_addr); 17614954f17dSDmitry Safonov family = AF_INET; 17624954f17dSDmitry Safonov } else { 17634954f17dSDmitry Safonov addr = (union tcp_ao_addr *)addr6; 17644954f17dSDmitry Safonov addr_len = sizeof(struct in6_addr); 17654954f17dSDmitry Safonov } 17664954f17dSDmitry Safonov port = ntohs(sin6->sin6_port); 17674954f17dSDmitry Safonov } 17684954f17dSDmitry Safonov prefix = cmd.prefix; 17694954f17dSDmitry Safonov 17704954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 17714954f17dSDmitry Safonov if (port != 0) 17724954f17dSDmitry Safonov return -EINVAL; 17734954f17dSDmitry Safonov 17744954f17dSDmitry Safonov /* We could choose random present key here for current/rnext 17754954f17dSDmitry Safonov * but that's less predictable. Let's be strict and don't 17764954f17dSDmitry Safonov * allow removing a key that's in use. RFC5925 doesn't 17774954f17dSDmitry Safonov * specify how-to coordinate key removal, but says: 17784954f17dSDmitry Safonov * "It is presumed that an MKT affecting a particular 17794954f17dSDmitry Safonov * connection cannot be destroyed during an active connection" 17804954f17dSDmitry Safonov */ 17814954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 17824954f17dSDmitry Safonov if (cmd.sndid != key->sndid || 17834954f17dSDmitry Safonov cmd.rcvid != key->rcvid) 17844954f17dSDmitry Safonov continue; 17854954f17dSDmitry Safonov 17864954f17dSDmitry Safonov if (family != key->family || 17874954f17dSDmitry Safonov prefix != key->prefixlen || 17884954f17dSDmitry Safonov memcmp(addr, &key->addr, addr_len)) 17894954f17dSDmitry Safonov continue; 17904954f17dSDmitry Safonov 17914954f17dSDmitry Safonov if (key == new_current || key == new_rnext) 17924954f17dSDmitry Safonov continue; 17934954f17dSDmitry Safonov 1794d6732b95SDmitry Safonov return tcp_ao_delete_key(sk, ao_info, cmd.del_async, key, 17954954f17dSDmitry Safonov new_current, new_rnext); 17964954f17dSDmitry Safonov } 17974954f17dSDmitry Safonov return -ENOENT; 17984954f17dSDmitry Safonov } 17994954f17dSDmitry Safonov 18000aadc739SDmitry Safonov /* cmd.ao_required makes a socket TCP-AO only. 18010aadc739SDmitry Safonov * Don't allow any md5 keys for any l3intf on the socket together with it. 18020aadc739SDmitry Safonov * Restricting it early in setsockopt() removes a check for 18030aadc739SDmitry Safonov * ao_info->ao_required on inbound tcp segment fast-path. 18040aadc739SDmitry Safonov */ 18050aadc739SDmitry Safonov static int tcp_ao_required_verify(struct sock *sk) 18060aadc739SDmitry Safonov { 18070aadc739SDmitry Safonov #ifdef CONFIG_TCP_MD5SIG 18080aadc739SDmitry Safonov const struct tcp_md5sig_info *md5sig; 18090aadc739SDmitry Safonov 18100aadc739SDmitry Safonov if (!static_branch_unlikely(&tcp_md5_needed.key)) 18110aadc739SDmitry Safonov return 0; 18120aadc739SDmitry Safonov 18130aadc739SDmitry Safonov md5sig = rcu_dereference_check(tcp_sk(sk)->md5sig_info, 18140aadc739SDmitry Safonov lockdep_sock_is_held(sk)); 18150aadc739SDmitry Safonov if (!md5sig) 18160aadc739SDmitry Safonov return 0; 18170aadc739SDmitry Safonov 18180aadc739SDmitry Safonov if (rcu_dereference_check(hlist_first_rcu(&md5sig->head), 18190aadc739SDmitry Safonov lockdep_sock_is_held(sk))) 18200aadc739SDmitry Safonov return 1; 18210aadc739SDmitry Safonov #endif 18220aadc739SDmitry Safonov return 0; 18230aadc739SDmitry Safonov } 18240aadc739SDmitry Safonov 18254954f17dSDmitry Safonov static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, 18264954f17dSDmitry Safonov sockptr_t optval, int optlen) 18274954f17dSDmitry Safonov { 18284954f17dSDmitry Safonov struct tcp_ao_key *new_current = NULL, *new_rnext = NULL; 18294954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 18304954f17dSDmitry Safonov struct tcp_ao_info_opt cmd; 18314954f17dSDmitry Safonov bool first = false; 18324954f17dSDmitry Safonov int err; 18334954f17dSDmitry Safonov 18344954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 18354954f17dSDmitry Safonov return -EINVAL; 18364954f17dSDmitry Safonov 18374954f17dSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 18384954f17dSDmitry Safonov if (err) 18394954f17dSDmitry Safonov return err; 18404954f17dSDmitry Safonov 18414954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 18424954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 18434954f17dSDmitry Safonov return -EINVAL; 18444954f17dSDmitry Safonov } 18454954f17dSDmitry Safonov 1846af09a341SDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 18474954f17dSDmitry Safonov return -EINVAL; 18484954f17dSDmitry Safonov 18494954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 18504954f17dSDmitry Safonov if (IS_ERR(ao_info)) 18514954f17dSDmitry Safonov return PTR_ERR(ao_info); 18524954f17dSDmitry Safonov if (!ao_info) { 18534954f17dSDmitry Safonov ao_info = tcp_ao_alloc_info(GFP_KERNEL); 18544954f17dSDmitry Safonov if (!ao_info) 18554954f17dSDmitry Safonov return -ENOMEM; 18564954f17dSDmitry Safonov first = true; 18574954f17dSDmitry Safonov } 18584954f17dSDmitry Safonov 18590aadc739SDmitry Safonov if (cmd.ao_required && tcp_ao_required_verify(sk)) 18600aadc739SDmitry Safonov return -EKEYREJECTED; 18610aadc739SDmitry Safonov 18624954f17dSDmitry Safonov /* For sockets in TCP_CLOSED it's possible set keys that aren't 18634954f17dSDmitry Safonov * matching the future peer (address/port/VRF/etc), 18644954f17dSDmitry Safonov * tcp_ao_connect_init() will choose a correct matching MKT 18654954f17dSDmitry Safonov * if there's any. 18664954f17dSDmitry Safonov */ 18674954f17dSDmitry Safonov if (cmd.set_current) { 18684954f17dSDmitry Safonov new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 18694954f17dSDmitry Safonov if (!new_current) { 18704954f17dSDmitry Safonov err = -ENOENT; 18714954f17dSDmitry Safonov goto out; 18724954f17dSDmitry Safonov } 18734954f17dSDmitry Safonov } 18744954f17dSDmitry Safonov if (cmd.set_rnext) { 18754954f17dSDmitry Safonov new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 18764954f17dSDmitry Safonov if (!new_rnext) { 18774954f17dSDmitry Safonov err = -ENOENT; 18784954f17dSDmitry Safonov goto out; 18794954f17dSDmitry Safonov } 18804954f17dSDmitry Safonov } 1881af09a341SDmitry Safonov if (cmd.set_counters) { 1882af09a341SDmitry Safonov atomic64_set(&ao_info->counters.pkt_good, cmd.pkt_good); 1883af09a341SDmitry Safonov atomic64_set(&ao_info->counters.pkt_bad, cmd.pkt_bad); 1884af09a341SDmitry Safonov atomic64_set(&ao_info->counters.key_not_found, cmd.pkt_key_not_found); 1885af09a341SDmitry Safonov atomic64_set(&ao_info->counters.ao_required, cmd.pkt_ao_required); 1886953af8e3SDmitry Safonov atomic64_set(&ao_info->counters.dropped_icmp, cmd.pkt_dropped_icmp); 1887af09a341SDmitry Safonov } 18884954f17dSDmitry Safonov 18894954f17dSDmitry Safonov ao_info->ao_required = cmd.ao_required; 1890953af8e3SDmitry Safonov ao_info->accept_icmps = cmd.accept_icmps; 18914954f17dSDmitry Safonov if (new_current) 18924954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, new_current); 18934954f17dSDmitry Safonov if (new_rnext) 18944954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, new_rnext); 18954954f17dSDmitry Safonov if (first) { 1896*67fa83f7SDmitry Safonov if (!static_branch_inc(&tcp_ao_needed.key)) { 1897*67fa83f7SDmitry Safonov err = -EUSERS; 1898*67fa83f7SDmitry Safonov goto out; 1899*67fa83f7SDmitry Safonov } 19004954f17dSDmitry Safonov sk_gso_disable(sk); 19014954f17dSDmitry Safonov rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 19024954f17dSDmitry Safonov } 19034954f17dSDmitry Safonov return 0; 19044954f17dSDmitry Safonov out: 19054954f17dSDmitry Safonov if (first) 19064954f17dSDmitry Safonov kfree(ao_info); 19074954f17dSDmitry Safonov return err; 19084954f17dSDmitry Safonov } 19094954f17dSDmitry Safonov 19104954f17dSDmitry Safonov int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, 19114954f17dSDmitry Safonov sockptr_t optval, int optlen) 19124954f17dSDmitry Safonov { 19134954f17dSDmitry Safonov if (WARN_ON_ONCE(family != AF_INET && family != AF_INET6)) 19144954f17dSDmitry Safonov return -EAFNOSUPPORT; 19154954f17dSDmitry Safonov 19164954f17dSDmitry Safonov switch (cmd) { 19174954f17dSDmitry Safonov case TCP_AO_ADD_KEY: 19184954f17dSDmitry Safonov return tcp_ao_add_cmd(sk, family, optval, optlen); 19194954f17dSDmitry Safonov case TCP_AO_DEL_KEY: 19204954f17dSDmitry Safonov return tcp_ao_del_cmd(sk, family, optval, optlen); 19214954f17dSDmitry Safonov case TCP_AO_INFO: 19224954f17dSDmitry Safonov return tcp_ao_info_cmd(sk, family, optval, optlen); 19234954f17dSDmitry Safonov default: 19244954f17dSDmitry Safonov WARN_ON_ONCE(1); 19254954f17dSDmitry Safonov return -EINVAL; 19264954f17dSDmitry Safonov } 19274954f17dSDmitry Safonov } 19284954f17dSDmitry Safonov 19294954f17dSDmitry Safonov int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) 19304954f17dSDmitry Safonov { 19314954f17dSDmitry Safonov return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen); 19324954f17dSDmitry Safonov } 19334954f17dSDmitry Safonov 1934ef84703aSDmitry Safonov /* tcp_ao_copy_mkts_to_user(ao_info, optval, optlen) 1935ef84703aSDmitry Safonov * 1936ef84703aSDmitry Safonov * @ao_info: struct tcp_ao_info on the socket that 1937ef84703aSDmitry Safonov * socket getsockopt(TCP_AO_GET_KEYS) is executed on 1938ef84703aSDmitry Safonov * @optval: pointer to array of tcp_ao_getsockopt structures in user space. 1939ef84703aSDmitry Safonov * Must be != NULL. 1940ef84703aSDmitry Safonov * @optlen: pointer to size of tcp_ao_getsockopt structure. 1941ef84703aSDmitry Safonov * Must be != NULL. 1942ef84703aSDmitry Safonov * 1943ef84703aSDmitry Safonov * Return value: 0 on success, a negative error number otherwise. 1944ef84703aSDmitry Safonov * 1945ef84703aSDmitry Safonov * optval points to an array of tcp_ao_getsockopt structures in user space. 1946ef84703aSDmitry Safonov * optval[0] is used as both input and output to getsockopt. It determines 1947ef84703aSDmitry Safonov * which keys are returned by the kernel. 1948ef84703aSDmitry Safonov * optval[0].nkeys is the size of the array in user space. On return it contains 1949ef84703aSDmitry Safonov * the number of keys matching the search criteria. 1950ef84703aSDmitry Safonov * If tcp_ao_getsockopt::get_all is set, then all keys in the socket are 1951ef84703aSDmitry Safonov * returned, otherwise only keys matching <addr, prefix, sndid, rcvid> 1952ef84703aSDmitry Safonov * in optval[0] are returned. 1953ef84703aSDmitry Safonov * optlen is also used as both input and output. The user provides the size 1954ef84703aSDmitry Safonov * of struct tcp_ao_getsockopt in user space, and the kernel returns the size 1955ef84703aSDmitry Safonov * of the structure in kernel space. 1956ef84703aSDmitry Safonov * The size of struct tcp_ao_getsockopt may differ between user and kernel. 1957ef84703aSDmitry Safonov * There are three cases to consider: 1958ef84703aSDmitry Safonov * * If usize == ksize, then keys are copied verbatim. 1959ef84703aSDmitry Safonov * * If usize < ksize, then the userspace has passed an old struct to a 1960ef84703aSDmitry Safonov * newer kernel. The rest of the trailing bytes in optval[0] 1961ef84703aSDmitry Safonov * (ksize - usize) are interpreted as 0 by the kernel. 1962ef84703aSDmitry Safonov * * If usize > ksize, then the userspace has passed a new struct to an 1963ef84703aSDmitry Safonov * older kernel. The trailing bytes unknown to the kernel (usize - ksize) 1964ef84703aSDmitry Safonov * are checked to ensure they are zeroed, otherwise -E2BIG is returned. 1965ef84703aSDmitry Safonov * On return the kernel fills in min(usize, ksize) in each entry of the array. 1966ef84703aSDmitry Safonov * The layout of the fields in the user and kernel structures is expected to 1967ef84703aSDmitry Safonov * be the same (including in the 32bit vs 64bit case). 1968ef84703aSDmitry Safonov */ 1969ef84703aSDmitry Safonov static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, 1970ef84703aSDmitry Safonov sockptr_t optval, sockptr_t optlen) 1971ef84703aSDmitry Safonov { 1972ef84703aSDmitry Safonov struct tcp_ao_getsockopt opt_in, opt_out; 1973ef84703aSDmitry Safonov struct tcp_ao_key *key, *current_key; 1974ef84703aSDmitry Safonov bool do_address_matching = true; 1975ef84703aSDmitry Safonov union tcp_ao_addr *addr = NULL; 1976ef84703aSDmitry Safonov unsigned int max_keys; /* maximum number of keys to copy to user */ 1977ef84703aSDmitry Safonov size_t out_offset = 0; 1978ef84703aSDmitry Safonov size_t bytes_to_write; /* number of bytes to write to user level */ 1979ef84703aSDmitry Safonov int err, user_len; 1980ef84703aSDmitry Safonov u32 matched_keys; /* keys from ao_info matched so far */ 1981ef84703aSDmitry Safonov int optlen_out; 1982ef84703aSDmitry Safonov __be16 port = 0; 1983ef84703aSDmitry Safonov 1984ef84703aSDmitry Safonov if (copy_from_sockptr(&user_len, optlen, sizeof(int))) 1985ef84703aSDmitry Safonov return -EFAULT; 1986ef84703aSDmitry Safonov 1987ef84703aSDmitry Safonov if (user_len <= 0) 1988ef84703aSDmitry Safonov return -EINVAL; 1989ef84703aSDmitry Safonov 1990ef84703aSDmitry Safonov memset(&opt_in, 0, sizeof(struct tcp_ao_getsockopt)); 1991ef84703aSDmitry Safonov err = copy_struct_from_sockptr(&opt_in, sizeof(opt_in), 1992ef84703aSDmitry Safonov optval, user_len); 1993ef84703aSDmitry Safonov if (err < 0) 1994ef84703aSDmitry Safonov return err; 1995ef84703aSDmitry Safonov 1996ef84703aSDmitry Safonov if (opt_in.pkt_good || opt_in.pkt_bad) 1997ef84703aSDmitry Safonov return -EINVAL; 1998ef84703aSDmitry Safonov 1999ef84703aSDmitry Safonov if (opt_in.reserved != 0) 2000ef84703aSDmitry Safonov return -EINVAL; 2001ef84703aSDmitry Safonov 2002ef84703aSDmitry Safonov max_keys = opt_in.nkeys; 2003ef84703aSDmitry Safonov 2004ef84703aSDmitry Safonov if (opt_in.get_all || opt_in.is_current || opt_in.is_rnext) { 2005ef84703aSDmitry Safonov if (opt_in.get_all && (opt_in.is_current || opt_in.is_rnext)) 2006ef84703aSDmitry Safonov return -EINVAL; 2007ef84703aSDmitry Safonov do_address_matching = false; 2008ef84703aSDmitry Safonov } 2009ef84703aSDmitry Safonov 2010ef84703aSDmitry Safonov switch (opt_in.addr.ss_family) { 2011ef84703aSDmitry Safonov case AF_INET: { 2012ef84703aSDmitry Safonov struct sockaddr_in *sin; 2013ef84703aSDmitry Safonov __be32 mask; 2014ef84703aSDmitry Safonov 2015ef84703aSDmitry Safonov sin = (struct sockaddr_in *)&opt_in.addr; 2016ef84703aSDmitry Safonov port = sin->sin_port; 2017ef84703aSDmitry Safonov addr = (union tcp_ao_addr *)&sin->sin_addr; 2018ef84703aSDmitry Safonov 2019ef84703aSDmitry Safonov if (opt_in.prefix > 32) 2020ef84703aSDmitry Safonov return -EINVAL; 2021ef84703aSDmitry Safonov 2022ef84703aSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY && 2023ef84703aSDmitry Safonov opt_in.prefix != 0) 2024ef84703aSDmitry Safonov return -EINVAL; 2025ef84703aSDmitry Safonov 2026ef84703aSDmitry Safonov mask = inet_make_mask(opt_in.prefix); 2027ef84703aSDmitry Safonov if (sin->sin_addr.s_addr & ~mask) 2028ef84703aSDmitry Safonov return -EINVAL; 2029ef84703aSDmitry Safonov 2030ef84703aSDmitry Safonov break; 2031ef84703aSDmitry Safonov } 2032ef84703aSDmitry Safonov case AF_INET6: { 2033ef84703aSDmitry Safonov struct sockaddr_in6 *sin6; 2034ef84703aSDmitry Safonov struct in6_addr *addr6; 2035ef84703aSDmitry Safonov 2036ef84703aSDmitry Safonov sin6 = (struct sockaddr_in6 *)&opt_in.addr; 2037ef84703aSDmitry Safonov addr = (union tcp_ao_addr *)&sin6->sin6_addr; 2038ef84703aSDmitry Safonov addr6 = &sin6->sin6_addr; 2039ef84703aSDmitry Safonov port = sin6->sin6_port; 2040ef84703aSDmitry Safonov 2041ef84703aSDmitry Safonov /* We don't have to change family and @addr here if 2042ef84703aSDmitry Safonov * ipv6_addr_v4mapped() like in key adding: 2043ef84703aSDmitry Safonov * tcp_ao_key_cmp() does it. Do the sanity checks though. 2044ef84703aSDmitry Safonov */ 2045ef84703aSDmitry Safonov if (opt_in.prefix != 0) { 2046ef84703aSDmitry Safonov if (ipv6_addr_v4mapped(addr6)) { 2047ef84703aSDmitry Safonov __be32 mask, addr4 = addr6->s6_addr32[3]; 2048ef84703aSDmitry Safonov 2049ef84703aSDmitry Safonov if (opt_in.prefix > 32 || 2050ef84703aSDmitry Safonov ntohl(addr4) == INADDR_ANY) 2051ef84703aSDmitry Safonov return -EINVAL; 2052ef84703aSDmitry Safonov mask = inet_make_mask(opt_in.prefix); 2053ef84703aSDmitry Safonov if (addr4 & ~mask) 2054ef84703aSDmitry Safonov return -EINVAL; 2055ef84703aSDmitry Safonov } else { 2056ef84703aSDmitry Safonov struct in6_addr pfx; 2057ef84703aSDmitry Safonov 2058ef84703aSDmitry Safonov if (ipv6_addr_any(addr6) || 2059ef84703aSDmitry Safonov opt_in.prefix > 128) 2060ef84703aSDmitry Safonov return -EINVAL; 2061ef84703aSDmitry Safonov 2062ef84703aSDmitry Safonov ipv6_addr_prefix(&pfx, addr6, opt_in.prefix); 2063ef84703aSDmitry Safonov if (ipv6_addr_cmp(&pfx, addr6)) 2064ef84703aSDmitry Safonov return -EINVAL; 2065ef84703aSDmitry Safonov } 2066ef84703aSDmitry Safonov } else if (!ipv6_addr_any(addr6)) { 2067ef84703aSDmitry Safonov return -EINVAL; 2068ef84703aSDmitry Safonov } 2069ef84703aSDmitry Safonov break; 2070ef84703aSDmitry Safonov } 2071ef84703aSDmitry Safonov case 0: 2072ef84703aSDmitry Safonov if (!do_address_matching) 2073ef84703aSDmitry Safonov break; 2074ef84703aSDmitry Safonov fallthrough; 2075ef84703aSDmitry Safonov default: 2076ef84703aSDmitry Safonov return -EAFNOSUPPORT; 2077ef84703aSDmitry Safonov } 2078ef84703aSDmitry Safonov 2079ef84703aSDmitry Safonov if (!do_address_matching) { 2080ef84703aSDmitry Safonov /* We could just ignore those, but let's do stricter checks */ 2081ef84703aSDmitry Safonov if (addr || port) 2082ef84703aSDmitry Safonov return -EINVAL; 2083ef84703aSDmitry Safonov if (opt_in.prefix || opt_in.sndid || opt_in.rcvid) 2084ef84703aSDmitry Safonov return -EINVAL; 2085ef84703aSDmitry Safonov } 2086ef84703aSDmitry Safonov 2087ef84703aSDmitry Safonov bytes_to_write = min_t(int, user_len, sizeof(struct tcp_ao_getsockopt)); 2088ef84703aSDmitry Safonov matched_keys = 0; 2089ef84703aSDmitry Safonov /* May change in RX, while we're dumping, pre-fetch it */ 2090ef84703aSDmitry Safonov current_key = READ_ONCE(ao_info->current_key); 2091ef84703aSDmitry Safonov 2092ef84703aSDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 2093ef84703aSDmitry Safonov if (opt_in.get_all) 2094ef84703aSDmitry Safonov goto match; 2095ef84703aSDmitry Safonov 2096ef84703aSDmitry Safonov if (opt_in.is_current || opt_in.is_rnext) { 2097ef84703aSDmitry Safonov if (opt_in.is_current && key == current_key) 2098ef84703aSDmitry Safonov goto match; 2099ef84703aSDmitry Safonov if (opt_in.is_rnext && key == ao_info->rnext_key) 2100ef84703aSDmitry Safonov goto match; 2101ef84703aSDmitry Safonov continue; 2102ef84703aSDmitry Safonov } 2103ef84703aSDmitry Safonov 2104ef84703aSDmitry Safonov if (tcp_ao_key_cmp(key, addr, opt_in.prefix, 2105ef84703aSDmitry Safonov opt_in.addr.ss_family, 2106ef84703aSDmitry Safonov opt_in.sndid, opt_in.rcvid) != 0) 2107ef84703aSDmitry Safonov continue; 2108ef84703aSDmitry Safonov match: 2109ef84703aSDmitry Safonov matched_keys++; 2110ef84703aSDmitry Safonov if (matched_keys > max_keys) 2111ef84703aSDmitry Safonov continue; 2112ef84703aSDmitry Safonov 2113ef84703aSDmitry Safonov memset(&opt_out, 0, sizeof(struct tcp_ao_getsockopt)); 2114ef84703aSDmitry Safonov 2115ef84703aSDmitry Safonov if (key->family == AF_INET) { 2116ef84703aSDmitry Safonov struct sockaddr_in *sin_out = (struct sockaddr_in *)&opt_out.addr; 2117ef84703aSDmitry Safonov 2118ef84703aSDmitry Safonov sin_out->sin_family = key->family; 2119ef84703aSDmitry Safonov sin_out->sin_port = 0; 2120ef84703aSDmitry Safonov memcpy(&sin_out->sin_addr, &key->addr, sizeof(struct in_addr)); 2121ef84703aSDmitry Safonov } else { 2122ef84703aSDmitry Safonov struct sockaddr_in6 *sin6_out = (struct sockaddr_in6 *)&opt_out.addr; 2123ef84703aSDmitry Safonov 2124ef84703aSDmitry Safonov sin6_out->sin6_family = key->family; 2125ef84703aSDmitry Safonov sin6_out->sin6_port = 0; 2126ef84703aSDmitry Safonov memcpy(&sin6_out->sin6_addr, &key->addr, sizeof(struct in6_addr)); 2127ef84703aSDmitry Safonov } 2128ef84703aSDmitry Safonov opt_out.sndid = key->sndid; 2129ef84703aSDmitry Safonov opt_out.rcvid = key->rcvid; 2130ef84703aSDmitry Safonov opt_out.prefix = key->prefixlen; 2131ef84703aSDmitry Safonov opt_out.keyflags = key->keyflags; 2132ef84703aSDmitry Safonov opt_out.is_current = (key == current_key); 2133ef84703aSDmitry Safonov opt_out.is_rnext = (key == ao_info->rnext_key); 2134ef84703aSDmitry Safonov opt_out.nkeys = 0; 2135ef84703aSDmitry Safonov opt_out.maclen = key->maclen; 2136ef84703aSDmitry Safonov opt_out.keylen = key->keylen; 2137ef84703aSDmitry Safonov opt_out.pkt_good = atomic64_read(&key->pkt_good); 2138ef84703aSDmitry Safonov opt_out.pkt_bad = atomic64_read(&key->pkt_bad); 2139ef84703aSDmitry Safonov memcpy(&opt_out.key, key->key, key->keylen); 2140ef84703aSDmitry Safonov tcp_sigpool_algo(key->tcp_sigpool_id, opt_out.alg_name, 64); 2141ef84703aSDmitry Safonov 2142ef84703aSDmitry Safonov /* Copy key to user */ 2143ef84703aSDmitry Safonov if (copy_to_sockptr_offset(optval, out_offset, 2144ef84703aSDmitry Safonov &opt_out, bytes_to_write)) 2145ef84703aSDmitry Safonov return -EFAULT; 2146ef84703aSDmitry Safonov out_offset += user_len; 2147ef84703aSDmitry Safonov } 2148ef84703aSDmitry Safonov 2149ef84703aSDmitry Safonov optlen_out = (int)sizeof(struct tcp_ao_getsockopt); 2150ef84703aSDmitry Safonov if (copy_to_sockptr(optlen, &optlen_out, sizeof(int))) 2151ef84703aSDmitry Safonov return -EFAULT; 2152ef84703aSDmitry Safonov 2153ef84703aSDmitry Safonov out_offset = offsetof(struct tcp_ao_getsockopt, nkeys); 2154ef84703aSDmitry Safonov if (copy_to_sockptr_offset(optval, out_offset, 2155ef84703aSDmitry Safonov &matched_keys, sizeof(u32))) 2156ef84703aSDmitry Safonov return -EFAULT; 2157ef84703aSDmitry Safonov 2158ef84703aSDmitry Safonov return 0; 2159ef84703aSDmitry Safonov } 2160ef84703aSDmitry Safonov 2161ef84703aSDmitry Safonov int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2162ef84703aSDmitry Safonov { 2163ef84703aSDmitry Safonov struct tcp_ao_info *ao_info; 2164ef84703aSDmitry Safonov 2165ef84703aSDmitry Safonov ao_info = setsockopt_ao_info(sk); 2166ef84703aSDmitry Safonov if (IS_ERR(ao_info)) 2167ef84703aSDmitry Safonov return PTR_ERR(ao_info); 2168ef84703aSDmitry Safonov if (!ao_info) 2169ef84703aSDmitry Safonov return -ENOENT; 2170ef84703aSDmitry Safonov 2171ef84703aSDmitry Safonov return tcp_ao_copy_mkts_to_user(ao_info, optval, optlen); 2172ef84703aSDmitry Safonov } 2173ef84703aSDmitry Safonov 2174ef84703aSDmitry Safonov int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2175ef84703aSDmitry Safonov { 2176ef84703aSDmitry Safonov struct tcp_ao_info_opt out, in = {}; 2177ef84703aSDmitry Safonov struct tcp_ao_key *current_key; 2178ef84703aSDmitry Safonov struct tcp_ao_info *ao; 2179ef84703aSDmitry Safonov int err, len; 2180ef84703aSDmitry Safonov 2181ef84703aSDmitry Safonov if (copy_from_sockptr(&len, optlen, sizeof(int))) 2182ef84703aSDmitry Safonov return -EFAULT; 2183ef84703aSDmitry Safonov 2184ef84703aSDmitry Safonov if (len <= 0) 2185ef84703aSDmitry Safonov return -EINVAL; 2186ef84703aSDmitry Safonov 2187ef84703aSDmitry Safonov /* Copying this "in" only to check ::reserved, ::reserved2, 2188ef84703aSDmitry Safonov * that may be needed to extend (struct tcp_ao_info_opt) and 2189ef84703aSDmitry Safonov * what getsockopt() provides in future. 2190ef84703aSDmitry Safonov */ 2191ef84703aSDmitry Safonov err = copy_struct_from_sockptr(&in, sizeof(in), optval, len); 2192ef84703aSDmitry Safonov if (err) 2193ef84703aSDmitry Safonov return err; 2194ef84703aSDmitry Safonov 2195ef84703aSDmitry Safonov if (in.reserved != 0 || in.reserved2 != 0) 2196ef84703aSDmitry Safonov return -EINVAL; 2197ef84703aSDmitry Safonov 2198ef84703aSDmitry Safonov ao = setsockopt_ao_info(sk); 2199ef84703aSDmitry Safonov if (IS_ERR(ao)) 2200ef84703aSDmitry Safonov return PTR_ERR(ao); 2201ef84703aSDmitry Safonov if (!ao) 2202ef84703aSDmitry Safonov return -ENOENT; 2203ef84703aSDmitry Safonov 2204ef84703aSDmitry Safonov memset(&out, 0, sizeof(out)); 2205ef84703aSDmitry Safonov out.ao_required = ao->ao_required; 2206ef84703aSDmitry Safonov out.accept_icmps = ao->accept_icmps; 2207ef84703aSDmitry Safonov out.pkt_good = atomic64_read(&ao->counters.pkt_good); 2208ef84703aSDmitry Safonov out.pkt_bad = atomic64_read(&ao->counters.pkt_bad); 2209ef84703aSDmitry Safonov out.pkt_key_not_found = atomic64_read(&ao->counters.key_not_found); 2210ef84703aSDmitry Safonov out.pkt_ao_required = atomic64_read(&ao->counters.ao_required); 2211ef84703aSDmitry Safonov out.pkt_dropped_icmp = atomic64_read(&ao->counters.dropped_icmp); 2212ef84703aSDmitry Safonov 2213ef84703aSDmitry Safonov current_key = READ_ONCE(ao->current_key); 2214ef84703aSDmitry Safonov if (current_key) { 2215ef84703aSDmitry Safonov out.set_current = 1; 2216ef84703aSDmitry Safonov out.current_key = current_key->sndid; 2217ef84703aSDmitry Safonov } 2218ef84703aSDmitry Safonov if (ao->rnext_key) { 2219ef84703aSDmitry Safonov out.set_rnext = 1; 2220ef84703aSDmitry Safonov out.rnext = ao->rnext_key->rcvid; 2221ef84703aSDmitry Safonov } 2222ef84703aSDmitry Safonov 2223ef84703aSDmitry Safonov if (copy_to_sockptr(optval, &out, min_t(int, len, sizeof(out)))) 2224ef84703aSDmitry Safonov return -EFAULT; 2225ef84703aSDmitry Safonov 2226ef84703aSDmitry Safonov return 0; 2227ef84703aSDmitry Safonov } 2228ef84703aSDmitry Safonov 2229