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 207c2ffaf2SDmitry Safonov int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, 217c2ffaf2SDmitry Safonov unsigned int len, struct tcp_sigpool *hp) 227c2ffaf2SDmitry Safonov { 237c2ffaf2SDmitry Safonov struct scatterlist sg; 247c2ffaf2SDmitry Safonov int ret; 257c2ffaf2SDmitry Safonov 267c2ffaf2SDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp->req), 277c2ffaf2SDmitry Safonov mkt->key, mkt->keylen)) 287c2ffaf2SDmitry Safonov goto clear_hash; 297c2ffaf2SDmitry Safonov 307c2ffaf2SDmitry Safonov ret = crypto_ahash_init(hp->req); 317c2ffaf2SDmitry Safonov if (ret) 327c2ffaf2SDmitry Safonov goto clear_hash; 337c2ffaf2SDmitry Safonov 347c2ffaf2SDmitry Safonov sg_init_one(&sg, ctx, len); 357c2ffaf2SDmitry Safonov ahash_request_set_crypt(hp->req, &sg, key, len); 367c2ffaf2SDmitry Safonov crypto_ahash_update(hp->req); 377c2ffaf2SDmitry Safonov 387c2ffaf2SDmitry Safonov ret = crypto_ahash_final(hp->req); 397c2ffaf2SDmitry Safonov if (ret) 407c2ffaf2SDmitry Safonov goto clear_hash; 417c2ffaf2SDmitry Safonov 427c2ffaf2SDmitry Safonov return 0; 437c2ffaf2SDmitry Safonov clear_hash: 447c2ffaf2SDmitry Safonov memset(key, 0, tcp_ao_digest_size(mkt)); 457c2ffaf2SDmitry Safonov return 1; 467c2ffaf2SDmitry Safonov } 477c2ffaf2SDmitry Safonov 48953af8e3SDmitry Safonov bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code) 49953af8e3SDmitry Safonov { 50953af8e3SDmitry Safonov bool ignore_icmp = false; 51953af8e3SDmitry Safonov struct tcp_ao_info *ao; 52953af8e3SDmitry Safonov 53953af8e3SDmitry Safonov /* RFC5925, 7.8: 54953af8e3SDmitry Safonov * >> A TCP-AO implementation MUST default to ignore incoming ICMPv4 55953af8e3SDmitry Safonov * messages of Type 3 (destination unreachable), Codes 2-4 (protocol 56953af8e3SDmitry Safonov * unreachable, port unreachable, and fragmentation needed -- ’hard 57953af8e3SDmitry Safonov * errors’), and ICMPv6 Type 1 (destination unreachable), Code 1 58953af8e3SDmitry Safonov * (administratively prohibited) and Code 4 (port unreachable) intended 59953af8e3SDmitry Safonov * for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN- 60953af8e3SDmitry Safonov * WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs. 61953af8e3SDmitry Safonov */ 62953af8e3SDmitry Safonov if (family == AF_INET) { 63953af8e3SDmitry Safonov if (type != ICMP_DEST_UNREACH) 64953af8e3SDmitry Safonov return false; 65953af8e3SDmitry Safonov if (code < ICMP_PROT_UNREACH || code > ICMP_FRAG_NEEDED) 66953af8e3SDmitry Safonov return false; 67953af8e3SDmitry Safonov } else { 68953af8e3SDmitry Safonov if (type != ICMPV6_DEST_UNREACH) 69953af8e3SDmitry Safonov return false; 70953af8e3SDmitry Safonov if (code != ICMPV6_ADM_PROHIBITED && code != ICMPV6_PORT_UNREACH) 71953af8e3SDmitry Safonov return false; 72953af8e3SDmitry Safonov } 73953af8e3SDmitry Safonov 74953af8e3SDmitry Safonov rcu_read_lock(); 75953af8e3SDmitry Safonov switch (sk->sk_state) { 76953af8e3SDmitry Safonov case TCP_TIME_WAIT: 77953af8e3SDmitry Safonov ao = rcu_dereference(tcp_twsk(sk)->ao_info); 78953af8e3SDmitry Safonov break; 79953af8e3SDmitry Safonov case TCP_SYN_SENT: 80953af8e3SDmitry Safonov case TCP_SYN_RECV: 81953af8e3SDmitry Safonov case TCP_LISTEN: 82953af8e3SDmitry Safonov case TCP_NEW_SYN_RECV: 83953af8e3SDmitry Safonov /* RFC5925 specifies to ignore ICMPs *only* on connections 84953af8e3SDmitry Safonov * in synchronized states. 85953af8e3SDmitry Safonov */ 86953af8e3SDmitry Safonov rcu_read_unlock(); 87953af8e3SDmitry Safonov return false; 88953af8e3SDmitry Safonov default: 89953af8e3SDmitry Safonov ao = rcu_dereference(tcp_sk(sk)->ao_info); 90953af8e3SDmitry Safonov } 91953af8e3SDmitry Safonov 92953af8e3SDmitry Safonov if (ao && !ao->accept_icmps) { 93953af8e3SDmitry Safonov ignore_icmp = true; 94953af8e3SDmitry Safonov __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAODROPPEDICMPS); 95953af8e3SDmitry Safonov atomic64_inc(&ao->counters.dropped_icmp); 96953af8e3SDmitry Safonov } 97953af8e3SDmitry Safonov rcu_read_unlock(); 98953af8e3SDmitry Safonov 99953af8e3SDmitry Safonov return ignore_icmp; 100953af8e3SDmitry Safonov } 101953af8e3SDmitry Safonov 1024954f17dSDmitry Safonov /* Optimized version of tcp_ao_do_lookup(): only for sockets for which 1034954f17dSDmitry Safonov * it's known that the keys in ao_info are matching peer's 1044954f17dSDmitry Safonov * family/address/VRF/etc. 1054954f17dSDmitry Safonov */ 106ba7783adSDmitry Safonov struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao, 1074954f17dSDmitry Safonov int sndid, int rcvid) 1084954f17dSDmitry Safonov { 1094954f17dSDmitry Safonov struct tcp_ao_key *key; 1104954f17dSDmitry Safonov 1114954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 1124954f17dSDmitry Safonov if ((sndid >= 0 && key->sndid != sndid) || 1134954f17dSDmitry Safonov (rcvid >= 0 && key->rcvid != rcvid)) 1144954f17dSDmitry Safonov continue; 1154954f17dSDmitry Safonov return key; 1164954f17dSDmitry Safonov } 1174954f17dSDmitry Safonov 1184954f17dSDmitry Safonov return NULL; 1194954f17dSDmitry Safonov } 1204954f17dSDmitry Safonov 1214954f17dSDmitry Safonov static int ipv4_prefix_cmp(const struct in_addr *addr1, 1224954f17dSDmitry Safonov const struct in_addr *addr2, 1234954f17dSDmitry Safonov unsigned int prefixlen) 1244954f17dSDmitry Safonov { 1254954f17dSDmitry Safonov __be32 mask = inet_make_mask(prefixlen); 1264954f17dSDmitry Safonov __be32 a1 = addr1->s_addr & mask; 1274954f17dSDmitry Safonov __be32 a2 = addr2->s_addr & mask; 1284954f17dSDmitry Safonov 1294954f17dSDmitry Safonov if (a1 == a2) 1304954f17dSDmitry Safonov return 0; 1314954f17dSDmitry Safonov return memcmp(&a1, &a2, sizeof(a1)); 1324954f17dSDmitry Safonov } 1334954f17dSDmitry Safonov 1344954f17dSDmitry Safonov static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, 1354954f17dSDmitry Safonov const union tcp_ao_addr *addr, u8 prefixlen, 1364954f17dSDmitry Safonov int family, int sndid, int rcvid) 1374954f17dSDmitry Safonov { 1384954f17dSDmitry Safonov if (sndid >= 0 && key->sndid != sndid) 1394954f17dSDmitry Safonov return (key->sndid > sndid) ? 1 : -1; 1404954f17dSDmitry Safonov if (rcvid >= 0 && key->rcvid != rcvid) 1414954f17dSDmitry Safonov return (key->rcvid > rcvid) ? 1 : -1; 1424954f17dSDmitry Safonov 1434954f17dSDmitry Safonov if (family == AF_UNSPEC) 1444954f17dSDmitry Safonov return 0; 1454954f17dSDmitry Safonov if (key->family != family) 1464954f17dSDmitry Safonov return (key->family > family) ? 1 : -1; 1474954f17dSDmitry Safonov 1484954f17dSDmitry Safonov if (family == AF_INET) { 1494954f17dSDmitry Safonov if (ntohl(key->addr.a4.s_addr) == INADDR_ANY) 1504954f17dSDmitry Safonov return 0; 1514954f17dSDmitry Safonov if (ntohl(addr->a4.s_addr) == INADDR_ANY) 1524954f17dSDmitry Safonov return 0; 1534954f17dSDmitry Safonov return ipv4_prefix_cmp(&key->addr.a4, &addr->a4, prefixlen); 1544954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 1554954f17dSDmitry Safonov } else { 1564954f17dSDmitry Safonov if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6)) 1574954f17dSDmitry Safonov return 0; 1584954f17dSDmitry Safonov if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen)) 1594954f17dSDmitry Safonov return 0; 1604954f17dSDmitry Safonov return memcmp(&key->addr.a6, &addr->a6, sizeof(addr->a6)); 1614954f17dSDmitry Safonov #endif 1624954f17dSDmitry Safonov } 1634954f17dSDmitry Safonov return -1; 1644954f17dSDmitry Safonov } 1654954f17dSDmitry Safonov 1664954f17dSDmitry Safonov static int tcp_ao_key_cmp(const struct tcp_ao_key *key, 1674954f17dSDmitry Safonov const union tcp_ao_addr *addr, u8 prefixlen, 1684954f17dSDmitry Safonov int family, int sndid, int rcvid) 1694954f17dSDmitry Safonov { 1704954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 1714954f17dSDmitry Safonov if (family == AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) { 1724954f17dSDmitry Safonov __be32 addr4 = addr->a6.s6_addr32[3]; 1734954f17dSDmitry Safonov 1744954f17dSDmitry Safonov return __tcp_ao_key_cmp(key, (union tcp_ao_addr *)&addr4, 1754954f17dSDmitry Safonov prefixlen, AF_INET, sndid, rcvid); 1764954f17dSDmitry Safonov } 1774954f17dSDmitry Safonov #endif 1784954f17dSDmitry Safonov return __tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid); 1794954f17dSDmitry Safonov } 1804954f17dSDmitry Safonov 1814954f17dSDmitry Safonov static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk, 1824954f17dSDmitry Safonov const union tcp_ao_addr *addr, int family, u8 prefix, 1834954f17dSDmitry Safonov int sndid, int rcvid) 1844954f17dSDmitry Safonov { 1854954f17dSDmitry Safonov struct tcp_ao_key *key; 1864954f17dSDmitry Safonov struct tcp_ao_info *ao; 1874954f17dSDmitry Safonov 1884954f17dSDmitry Safonov ao = rcu_dereference_check(tcp_sk(sk)->ao_info, 1894954f17dSDmitry Safonov lockdep_sock_is_held(sk)); 1904954f17dSDmitry Safonov if (!ao) 1914954f17dSDmitry Safonov return NULL; 1924954f17dSDmitry Safonov 1934954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 1944954f17dSDmitry Safonov u8 prefixlen = min(prefix, key->prefixlen); 1954954f17dSDmitry Safonov 1964954f17dSDmitry Safonov if (!tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid)) 1974954f17dSDmitry Safonov return key; 1984954f17dSDmitry Safonov } 1994954f17dSDmitry Safonov return NULL; 2004954f17dSDmitry Safonov } 2014954f17dSDmitry Safonov 2020aadc739SDmitry Safonov struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, 2030aadc739SDmitry Safonov const union tcp_ao_addr *addr, 2040aadc739SDmitry Safonov int family, int sndid, int rcvid) 2050aadc739SDmitry Safonov { 2060aadc739SDmitry Safonov return __tcp_ao_do_lookup(sk, addr, family, U8_MAX, sndid, rcvid); 2070aadc739SDmitry Safonov } 2080aadc739SDmitry Safonov 2094954f17dSDmitry Safonov static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags) 2104954f17dSDmitry Safonov { 2114954f17dSDmitry Safonov struct tcp_ao_info *ao; 2124954f17dSDmitry Safonov 2134954f17dSDmitry Safonov ao = kzalloc(sizeof(*ao), flags); 2144954f17dSDmitry Safonov if (!ao) 2154954f17dSDmitry Safonov return NULL; 2164954f17dSDmitry Safonov INIT_HLIST_HEAD(&ao->head); 217decde258SDmitry Safonov refcount_set(&ao->refcnt, 1); 2184954f17dSDmitry Safonov 2194954f17dSDmitry Safonov return ao; 2204954f17dSDmitry Safonov } 2214954f17dSDmitry Safonov 2224954f17dSDmitry Safonov static void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt) 2234954f17dSDmitry Safonov { 2244954f17dSDmitry Safonov hlist_add_head_rcu(&mkt->node, &ao->head); 2254954f17dSDmitry Safonov } 2264954f17dSDmitry Safonov 22706b22ef2SDmitry Safonov static struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, 22806b22ef2SDmitry Safonov struct tcp_ao_key *key) 22906b22ef2SDmitry Safonov { 23006b22ef2SDmitry Safonov struct tcp_ao_key *new_key; 23106b22ef2SDmitry Safonov 23206b22ef2SDmitry Safonov new_key = sock_kmalloc(sk, tcp_ao_sizeof_key(key), 23306b22ef2SDmitry Safonov GFP_ATOMIC); 23406b22ef2SDmitry Safonov if (!new_key) 23506b22ef2SDmitry Safonov return NULL; 23606b22ef2SDmitry Safonov 23706b22ef2SDmitry Safonov *new_key = *key; 23806b22ef2SDmitry Safonov INIT_HLIST_NODE(&new_key->node); 23906b22ef2SDmitry Safonov tcp_sigpool_get(new_key->tcp_sigpool_id); 240af09a341SDmitry Safonov atomic64_set(&new_key->pkt_good, 0); 241af09a341SDmitry Safonov atomic64_set(&new_key->pkt_bad, 0); 24206b22ef2SDmitry Safonov 24306b22ef2SDmitry Safonov return new_key; 24406b22ef2SDmitry Safonov } 24506b22ef2SDmitry Safonov 2464954f17dSDmitry Safonov static void tcp_ao_key_free_rcu(struct rcu_head *head) 2474954f17dSDmitry Safonov { 2484954f17dSDmitry Safonov struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu); 2494954f17dSDmitry Safonov 2504954f17dSDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 2514954f17dSDmitry Safonov kfree_sensitive(key); 2524954f17dSDmitry Safonov } 2534954f17dSDmitry Safonov 254decde258SDmitry Safonov void tcp_ao_destroy_sock(struct sock *sk, bool twsk) 2554954f17dSDmitry Safonov { 2564954f17dSDmitry Safonov struct tcp_ao_info *ao; 2574954f17dSDmitry Safonov struct tcp_ao_key *key; 2584954f17dSDmitry Safonov struct hlist_node *n; 2594954f17dSDmitry Safonov 260decde258SDmitry Safonov if (twsk) { 261decde258SDmitry Safonov ao = rcu_dereference_protected(tcp_twsk(sk)->ao_info, 1); 262decde258SDmitry Safonov tcp_twsk(sk)->ao_info = NULL; 263decde258SDmitry Safonov } else { 2644954f17dSDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1); 2654954f17dSDmitry Safonov tcp_sk(sk)->ao_info = NULL; 266decde258SDmitry Safonov } 2674954f17dSDmitry Safonov 268decde258SDmitry Safonov if (!ao || !refcount_dec_and_test(&ao->refcnt)) 2694954f17dSDmitry Safonov return; 2704954f17dSDmitry Safonov 2714954f17dSDmitry Safonov hlist_for_each_entry_safe(key, n, &ao->head, node) { 2724954f17dSDmitry Safonov hlist_del_rcu(&key->node); 273decde258SDmitry Safonov if (!twsk) 2744954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 2754954f17dSDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 2764954f17dSDmitry Safonov } 2774954f17dSDmitry Safonov 2784954f17dSDmitry Safonov kfree_rcu(ao, rcu); 2794954f17dSDmitry Safonov } 2804954f17dSDmitry Safonov 281decde258SDmitry Safonov void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp) 282decde258SDmitry Safonov { 283decde258SDmitry Safonov struct tcp_ao_info *ao_info = rcu_dereference_protected(tp->ao_info, 1); 284decde258SDmitry Safonov 285decde258SDmitry Safonov if (ao_info) { 286decde258SDmitry Safonov struct tcp_ao_key *key; 287decde258SDmitry Safonov struct hlist_node *n; 288decde258SDmitry Safonov int omem = 0; 289decde258SDmitry Safonov 290decde258SDmitry Safonov hlist_for_each_entry_safe(key, n, &ao_info->head, node) { 291decde258SDmitry Safonov omem += tcp_ao_sizeof_key(key); 292decde258SDmitry Safonov } 293decde258SDmitry Safonov 294decde258SDmitry Safonov refcount_inc(&ao_info->refcnt); 295decde258SDmitry Safonov atomic_sub(omem, &(((struct sock *)tp)->sk_omem_alloc)); 296decde258SDmitry Safonov rcu_assign_pointer(tcptw->ao_info, ao_info); 297decde258SDmitry Safonov } else { 298decde258SDmitry Safonov tcptw->ao_info = NULL; 299decde258SDmitry Safonov } 300decde258SDmitry Safonov } 301decde258SDmitry Safonov 3027c2ffaf2SDmitry Safonov /* 4 tuple and ISNs are expected in NBO */ 3037c2ffaf2SDmitry Safonov static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, 3047c2ffaf2SDmitry Safonov __be32 saddr, __be32 daddr, 3057c2ffaf2SDmitry Safonov __be16 sport, __be16 dport, 3067c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn) 3077c2ffaf2SDmitry Safonov { 3087c2ffaf2SDmitry Safonov /* See RFC5926 3.1.1 */ 3097c2ffaf2SDmitry Safonov struct kdf_input_block { 3107c2ffaf2SDmitry Safonov u8 counter; 3117c2ffaf2SDmitry Safonov u8 label[6]; 3127c2ffaf2SDmitry Safonov struct tcp4_ao_context ctx; 3137c2ffaf2SDmitry Safonov __be16 outlen; 3147c2ffaf2SDmitry Safonov } __packed * tmp; 3157c2ffaf2SDmitry Safonov struct tcp_sigpool hp; 3167c2ffaf2SDmitry Safonov int err; 3177c2ffaf2SDmitry Safonov 3187c2ffaf2SDmitry Safonov err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp); 3197c2ffaf2SDmitry Safonov if (err) 3207c2ffaf2SDmitry Safonov return err; 3217c2ffaf2SDmitry Safonov 3227c2ffaf2SDmitry Safonov tmp = hp.scratch; 3237c2ffaf2SDmitry Safonov tmp->counter = 1; 3247c2ffaf2SDmitry Safonov memcpy(tmp->label, "TCP-AO", 6); 3257c2ffaf2SDmitry Safonov tmp->ctx.saddr = saddr; 3267c2ffaf2SDmitry Safonov tmp->ctx.daddr = daddr; 3277c2ffaf2SDmitry Safonov tmp->ctx.sport = sport; 3287c2ffaf2SDmitry Safonov tmp->ctx.dport = dport; 3297c2ffaf2SDmitry Safonov tmp->ctx.sisn = sisn; 3307c2ffaf2SDmitry Safonov tmp->ctx.disn = disn; 3317c2ffaf2SDmitry Safonov tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ 3327c2ffaf2SDmitry Safonov 3337c2ffaf2SDmitry Safonov err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp); 3347c2ffaf2SDmitry Safonov tcp_sigpool_end(&hp); 3357c2ffaf2SDmitry Safonov 3367c2ffaf2SDmitry Safonov return err; 3377c2ffaf2SDmitry Safonov } 3387c2ffaf2SDmitry Safonov 3397c2ffaf2SDmitry Safonov int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, 3407c2ffaf2SDmitry Safonov const struct sock *sk, 3417c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn, bool send) 3427c2ffaf2SDmitry Safonov { 3437c2ffaf2SDmitry Safonov if (send) 3447c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr, 3457c2ffaf2SDmitry Safonov sk->sk_daddr, htons(sk->sk_num), 3467c2ffaf2SDmitry Safonov sk->sk_dport, sisn, disn); 3477c2ffaf2SDmitry Safonov else 3487c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr, 3497c2ffaf2SDmitry Safonov sk->sk_rcv_saddr, sk->sk_dport, 3507c2ffaf2SDmitry Safonov htons(sk->sk_num), disn, sisn); 3517c2ffaf2SDmitry Safonov } 3527c2ffaf2SDmitry Safonov 3537c2ffaf2SDmitry Safonov static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, 3547c2ffaf2SDmitry Safonov const struct sock *sk, 3557c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn, bool send) 3567c2ffaf2SDmitry Safonov { 3577c2ffaf2SDmitry Safonov if (mkt->family == AF_INET) 3587c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); 3597c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 3607c2ffaf2SDmitry Safonov else if (mkt->family == AF_INET6) 3617c2ffaf2SDmitry Safonov return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); 3627c2ffaf2SDmitry Safonov #endif 3637c2ffaf2SDmitry Safonov else 3647c2ffaf2SDmitry Safonov return -EOPNOTSUPP; 3657c2ffaf2SDmitry Safonov } 3667c2ffaf2SDmitry Safonov 36706b22ef2SDmitry Safonov int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, 36806b22ef2SDmitry Safonov struct request_sock *req) 36906b22ef2SDmitry Safonov { 37006b22ef2SDmitry Safonov struct inet_request_sock *ireq = inet_rsk(req); 37106b22ef2SDmitry Safonov 37206b22ef2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, 37306b22ef2SDmitry Safonov ireq->ir_loc_addr, ireq->ir_rmt_addr, 37406b22ef2SDmitry Safonov htons(ireq->ir_num), ireq->ir_rmt_port, 37506b22ef2SDmitry Safonov htonl(tcp_rsk(req)->snt_isn), 37606b22ef2SDmitry Safonov htonl(tcp_rsk(req)->rcv_isn)); 37706b22ef2SDmitry Safonov } 37806b22ef2SDmitry Safonov 37906b22ef2SDmitry Safonov static int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, 38006b22ef2SDmitry Safonov const struct sk_buff *skb, 38106b22ef2SDmitry Safonov __be32 sisn, __be32 disn) 38206b22ef2SDmitry Safonov { 38306b22ef2SDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 38406b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 38506b22ef2SDmitry Safonov 38606b22ef2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr, 38706b22ef2SDmitry Safonov th->source, th->dest, sisn, disn); 38806b22ef2SDmitry Safonov } 38906b22ef2SDmitry Safonov 39006b22ef2SDmitry Safonov static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, 39106b22ef2SDmitry Safonov const struct sk_buff *skb, 39206b22ef2SDmitry Safonov __be32 sisn, __be32 disn, int family) 39306b22ef2SDmitry Safonov { 39406b22ef2SDmitry Safonov if (family == AF_INET) 39506b22ef2SDmitry Safonov return tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn); 39606b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 39706b22ef2SDmitry Safonov else if (family == AF_INET6) 39806b22ef2SDmitry Safonov return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn); 39906b22ef2SDmitry Safonov #endif 40006b22ef2SDmitry Safonov return -EAFNOSUPPORT; 40106b22ef2SDmitry Safonov } 40206b22ef2SDmitry Safonov 4031e03d32bSDmitry Safonov static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp, 4041e03d32bSDmitry Safonov __be32 daddr, __be32 saddr, 4051e03d32bSDmitry Safonov int nbytes) 4061e03d32bSDmitry Safonov { 4071e03d32bSDmitry Safonov struct tcp4_pseudohdr *bp; 4081e03d32bSDmitry Safonov struct scatterlist sg; 4091e03d32bSDmitry Safonov 4101e03d32bSDmitry Safonov bp = hp->scratch; 4111e03d32bSDmitry Safonov bp->saddr = saddr; 4121e03d32bSDmitry Safonov bp->daddr = daddr; 4131e03d32bSDmitry Safonov bp->pad = 0; 4141e03d32bSDmitry Safonov bp->protocol = IPPROTO_TCP; 4151e03d32bSDmitry Safonov bp->len = cpu_to_be16(nbytes); 4161e03d32bSDmitry Safonov 4171e03d32bSDmitry Safonov sg_init_one(&sg, bp, sizeof(*bp)); 4181e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); 4191e03d32bSDmitry Safonov return crypto_ahash_update(hp->req); 4201e03d32bSDmitry Safonov } 4211e03d32bSDmitry Safonov 4221e03d32bSDmitry Safonov static int tcp_ao_hash_pseudoheader(unsigned short int family, 4231e03d32bSDmitry Safonov const struct sock *sk, 4241e03d32bSDmitry Safonov const struct sk_buff *skb, 4251e03d32bSDmitry Safonov struct tcp_sigpool *hp, int nbytes) 4261e03d32bSDmitry Safonov { 4271e03d32bSDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 4281e03d32bSDmitry Safonov 4291e03d32bSDmitry Safonov /* TODO: Can we rely on checksum being zero to mean outbound pkt? */ 4301e03d32bSDmitry Safonov if (!th->check) { 4311e03d32bSDmitry Safonov if (family == AF_INET) 4321e03d32bSDmitry Safonov return tcp_v4_ao_hash_pseudoheader(hp, sk->sk_daddr, 4331e03d32bSDmitry Safonov sk->sk_rcv_saddr, skb->len); 4341e03d32bSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 4351e03d32bSDmitry Safonov else if (family == AF_INET6) 4361e03d32bSDmitry Safonov return tcp_v6_ao_hash_pseudoheader(hp, &sk->sk_v6_daddr, 4371e03d32bSDmitry Safonov &sk->sk_v6_rcv_saddr, skb->len); 4381e03d32bSDmitry Safonov #endif 4391e03d32bSDmitry Safonov else 4401e03d32bSDmitry Safonov return -EAFNOSUPPORT; 4411e03d32bSDmitry Safonov } 4421e03d32bSDmitry Safonov 4431e03d32bSDmitry Safonov if (family == AF_INET) { 4441e03d32bSDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 4451e03d32bSDmitry Safonov 4461e03d32bSDmitry Safonov return tcp_v4_ao_hash_pseudoheader(hp, iph->daddr, 4471e03d32bSDmitry Safonov iph->saddr, skb->len); 4481e03d32bSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 4491e03d32bSDmitry Safonov } else if (family == AF_INET6) { 4501e03d32bSDmitry Safonov const struct ipv6hdr *iph = ipv6_hdr(skb); 4511e03d32bSDmitry Safonov 4521e03d32bSDmitry Safonov return tcp_v6_ao_hash_pseudoheader(hp, &iph->daddr, 4531e03d32bSDmitry Safonov &iph->saddr, skb->len); 4541e03d32bSDmitry Safonov #endif 4551e03d32bSDmitry Safonov } 4561e03d32bSDmitry Safonov return -EAFNOSUPPORT; 4571e03d32bSDmitry Safonov } 4581e03d32bSDmitry Safonov 45964382c71SDmitry Safonov u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq) 46064382c71SDmitry Safonov { 46164382c71SDmitry Safonov u32 sne = next_sne; 46264382c71SDmitry Safonov 46364382c71SDmitry Safonov if (before(seq, next_seq)) { 46464382c71SDmitry Safonov if (seq > next_seq) 46564382c71SDmitry Safonov sne--; 46664382c71SDmitry Safonov } else { 46764382c71SDmitry Safonov if (seq < next_seq) 46864382c71SDmitry Safonov sne++; 46964382c71SDmitry Safonov } 47064382c71SDmitry Safonov 47164382c71SDmitry Safonov return sne; 47264382c71SDmitry Safonov } 47364382c71SDmitry Safonov 4741e03d32bSDmitry Safonov /* tcp_ao_hash_sne(struct tcp_sigpool *hp) 4751e03d32bSDmitry Safonov * @hp - used for hashing 4761e03d32bSDmitry Safonov * @sne - sne value 4771e03d32bSDmitry Safonov */ 4781e03d32bSDmitry Safonov static int tcp_ao_hash_sne(struct tcp_sigpool *hp, u32 sne) 4791e03d32bSDmitry Safonov { 4801e03d32bSDmitry Safonov struct scatterlist sg; 4811e03d32bSDmitry Safonov __be32 *bp; 4821e03d32bSDmitry Safonov 4831e03d32bSDmitry Safonov bp = (__be32 *)hp->scratch; 4841e03d32bSDmitry Safonov *bp = htonl(sne); 4851e03d32bSDmitry Safonov 4861e03d32bSDmitry Safonov sg_init_one(&sg, bp, sizeof(*bp)); 4871e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); 4881e03d32bSDmitry Safonov return crypto_ahash_update(hp->req); 4891e03d32bSDmitry Safonov } 4901e03d32bSDmitry Safonov 4911e03d32bSDmitry Safonov static int tcp_ao_hash_header(struct tcp_sigpool *hp, 4921e03d32bSDmitry Safonov const struct tcphdr *th, 4931e03d32bSDmitry Safonov bool exclude_options, u8 *hash, 4941e03d32bSDmitry Safonov int hash_offset, int hash_len) 4951e03d32bSDmitry Safonov { 4961e03d32bSDmitry Safonov int err, len = th->doff << 2; 4971e03d32bSDmitry Safonov struct scatterlist sg; 4981e03d32bSDmitry Safonov u8 *hdr = hp->scratch; 4991e03d32bSDmitry Safonov 5001e03d32bSDmitry Safonov /* We are not allowed to change tcphdr, make a local copy */ 5011e03d32bSDmitry Safonov if (exclude_options) { 5021e03d32bSDmitry Safonov len = sizeof(*th) + sizeof(struct tcp_ao_hdr) + hash_len; 5031e03d32bSDmitry Safonov memcpy(hdr, th, sizeof(*th)); 5041e03d32bSDmitry Safonov memcpy(hdr + sizeof(*th), 5051e03d32bSDmitry Safonov (u8 *)th + hash_offset - sizeof(struct tcp_ao_hdr), 5061e03d32bSDmitry Safonov sizeof(struct tcp_ao_hdr)); 5071e03d32bSDmitry Safonov memset(hdr + sizeof(*th) + sizeof(struct tcp_ao_hdr), 5081e03d32bSDmitry Safonov 0, hash_len); 5091e03d32bSDmitry Safonov ((struct tcphdr *)hdr)->check = 0; 5101e03d32bSDmitry Safonov } else { 5111e03d32bSDmitry Safonov len = th->doff << 2; 5121e03d32bSDmitry Safonov memcpy(hdr, th, len); 5131e03d32bSDmitry Safonov /* zero out tcp-ao hash */ 5141e03d32bSDmitry Safonov ((struct tcphdr *)hdr)->check = 0; 5151e03d32bSDmitry Safonov memset(hdr + hash_offset, 0, hash_len); 5161e03d32bSDmitry Safonov } 5171e03d32bSDmitry Safonov 5181e03d32bSDmitry Safonov sg_init_one(&sg, hdr, len); 5191e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, len); 5201e03d32bSDmitry Safonov err = crypto_ahash_update(hp->req); 5211e03d32bSDmitry Safonov WARN_ON_ONCE(err != 0); 5221e03d32bSDmitry Safonov return err; 5231e03d32bSDmitry Safonov } 5241e03d32bSDmitry Safonov 525ba7783adSDmitry Safonov int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash, 526ba7783adSDmitry Safonov struct tcp_ao_key *key, const u8 *tkey, 527ba7783adSDmitry Safonov const union tcp_ao_addr *daddr, 528ba7783adSDmitry Safonov const union tcp_ao_addr *saddr, 529ba7783adSDmitry Safonov const struct tcphdr *th, u32 sne) 530ba7783adSDmitry Safonov { 531ba7783adSDmitry Safonov int tkey_len = tcp_ao_digest_size(key); 532ba7783adSDmitry Safonov int hash_offset = ao_hash - (char *)th; 533ba7783adSDmitry Safonov struct tcp_sigpool hp; 534ba7783adSDmitry Safonov void *hash_buf = NULL; 535ba7783adSDmitry Safonov 536ba7783adSDmitry Safonov hash_buf = kmalloc(tkey_len, GFP_ATOMIC); 537ba7783adSDmitry Safonov if (!hash_buf) 538ba7783adSDmitry Safonov goto clear_hash_noput; 539ba7783adSDmitry Safonov 540ba7783adSDmitry Safonov if (tcp_sigpool_start(key->tcp_sigpool_id, &hp)) 541ba7783adSDmitry Safonov goto clear_hash_noput; 542ba7783adSDmitry Safonov 543ba7783adSDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) 544ba7783adSDmitry Safonov goto clear_hash; 545ba7783adSDmitry Safonov 546ba7783adSDmitry Safonov if (crypto_ahash_init(hp.req)) 547ba7783adSDmitry Safonov goto clear_hash; 548ba7783adSDmitry Safonov 549ba7783adSDmitry Safonov if (tcp_ao_hash_sne(&hp, sne)) 550ba7783adSDmitry Safonov goto clear_hash; 551ba7783adSDmitry Safonov if (family == AF_INET) { 552ba7783adSDmitry Safonov if (tcp_v4_ao_hash_pseudoheader(&hp, daddr->a4.s_addr, 553ba7783adSDmitry Safonov saddr->a4.s_addr, th->doff * 4)) 554ba7783adSDmitry Safonov goto clear_hash; 555ba7783adSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 556ba7783adSDmitry Safonov } else if (family == AF_INET6) { 557ba7783adSDmitry Safonov if (tcp_v6_ao_hash_pseudoheader(&hp, &daddr->a6, 558ba7783adSDmitry Safonov &saddr->a6, th->doff * 4)) 559ba7783adSDmitry Safonov goto clear_hash; 560ba7783adSDmitry Safonov #endif 561ba7783adSDmitry Safonov } else { 562ba7783adSDmitry Safonov WARN_ON_ONCE(1); 563ba7783adSDmitry Safonov goto clear_hash; 564ba7783adSDmitry Safonov } 5657753c2f0SDmitry Safonov if (tcp_ao_hash_header(&hp, th, 5667753c2f0SDmitry Safonov !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), 567ba7783adSDmitry Safonov ao_hash, hash_offset, tcp_ao_maclen(key))) 568ba7783adSDmitry Safonov goto clear_hash; 569ba7783adSDmitry Safonov ahash_request_set_crypt(hp.req, NULL, hash_buf, 0); 570ba7783adSDmitry Safonov if (crypto_ahash_final(hp.req)) 571ba7783adSDmitry Safonov goto clear_hash; 572ba7783adSDmitry Safonov 573ba7783adSDmitry Safonov memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); 574ba7783adSDmitry Safonov tcp_sigpool_end(&hp); 575ba7783adSDmitry Safonov kfree(hash_buf); 576ba7783adSDmitry Safonov return 0; 577ba7783adSDmitry Safonov 578ba7783adSDmitry Safonov clear_hash: 579ba7783adSDmitry Safonov tcp_sigpool_end(&hp); 580ba7783adSDmitry Safonov clear_hash_noput: 581ba7783adSDmitry Safonov memset(ao_hash, 0, tcp_ao_maclen(key)); 582ba7783adSDmitry Safonov kfree(hash_buf); 583ba7783adSDmitry Safonov return 1; 584ba7783adSDmitry Safonov } 585ba7783adSDmitry Safonov 5861e03d32bSDmitry Safonov int tcp_ao_hash_skb(unsigned short int family, 5871e03d32bSDmitry Safonov char *ao_hash, struct tcp_ao_key *key, 5881e03d32bSDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 5891e03d32bSDmitry Safonov const u8 *tkey, int hash_offset, u32 sne) 5901e03d32bSDmitry Safonov { 5911e03d32bSDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 5921e03d32bSDmitry Safonov int tkey_len = tcp_ao_digest_size(key); 5931e03d32bSDmitry Safonov struct tcp_sigpool hp; 5941e03d32bSDmitry Safonov void *hash_buf = NULL; 5951e03d32bSDmitry Safonov 5961e03d32bSDmitry Safonov hash_buf = kmalloc(tkey_len, GFP_ATOMIC); 5971e03d32bSDmitry Safonov if (!hash_buf) 5981e03d32bSDmitry Safonov goto clear_hash_noput; 5991e03d32bSDmitry Safonov 6001e03d32bSDmitry Safonov if (tcp_sigpool_start(key->tcp_sigpool_id, &hp)) 6011e03d32bSDmitry Safonov goto clear_hash_noput; 6021e03d32bSDmitry Safonov 6031e03d32bSDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) 6041e03d32bSDmitry Safonov goto clear_hash; 6051e03d32bSDmitry Safonov 6061e03d32bSDmitry Safonov /* For now use sha1 by default. Depends on alg in tcp_ao_key */ 6071e03d32bSDmitry Safonov if (crypto_ahash_init(hp.req)) 6081e03d32bSDmitry Safonov goto clear_hash; 6091e03d32bSDmitry Safonov 6101e03d32bSDmitry Safonov if (tcp_ao_hash_sne(&hp, sne)) 6111e03d32bSDmitry Safonov goto clear_hash; 6121e03d32bSDmitry Safonov if (tcp_ao_hash_pseudoheader(family, sk, skb, &hp, skb->len)) 6131e03d32bSDmitry Safonov goto clear_hash; 6147753c2f0SDmitry Safonov if (tcp_ao_hash_header(&hp, th, 6157753c2f0SDmitry Safonov !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), 6161e03d32bSDmitry Safonov ao_hash, hash_offset, tcp_ao_maclen(key))) 6171e03d32bSDmitry Safonov goto clear_hash; 6181e03d32bSDmitry Safonov if (tcp_sigpool_hash_skb_data(&hp, skb, th->doff << 2)) 6191e03d32bSDmitry Safonov goto clear_hash; 6201e03d32bSDmitry Safonov ahash_request_set_crypt(hp.req, NULL, hash_buf, 0); 6211e03d32bSDmitry Safonov if (crypto_ahash_final(hp.req)) 6221e03d32bSDmitry Safonov goto clear_hash; 6231e03d32bSDmitry Safonov 6241e03d32bSDmitry Safonov memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); 6251e03d32bSDmitry Safonov tcp_sigpool_end(&hp); 6261e03d32bSDmitry Safonov kfree(hash_buf); 6271e03d32bSDmitry Safonov return 0; 6281e03d32bSDmitry Safonov 6291e03d32bSDmitry Safonov clear_hash: 6301e03d32bSDmitry Safonov tcp_sigpool_end(&hp); 6311e03d32bSDmitry Safonov clear_hash_noput: 6321e03d32bSDmitry Safonov memset(ao_hash, 0, tcp_ao_maclen(key)); 6331e03d32bSDmitry Safonov kfree(hash_buf); 6341e03d32bSDmitry Safonov return 1; 6351e03d32bSDmitry Safonov } 6361e03d32bSDmitry Safonov 6371e03d32bSDmitry Safonov int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, 6381e03d32bSDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 6391e03d32bSDmitry Safonov const u8 *tkey, int hash_offset, u32 sne) 6401e03d32bSDmitry Safonov { 6411e03d32bSDmitry Safonov return tcp_ao_hash_skb(AF_INET, ao_hash, key, sk, skb, 6421e03d32bSDmitry Safonov tkey, hash_offset, sne); 6431e03d32bSDmitry Safonov } 6441e03d32bSDmitry Safonov 6459427c6aaSDmitry Safonov int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key, 6469427c6aaSDmitry Safonov struct request_sock *req, const struct sk_buff *skb, 6479427c6aaSDmitry Safonov int hash_offset, u32 sne) 6489427c6aaSDmitry Safonov { 6499427c6aaSDmitry Safonov void *hash_buf = NULL; 6509427c6aaSDmitry Safonov int err; 6519427c6aaSDmitry Safonov 6529427c6aaSDmitry Safonov hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); 6539427c6aaSDmitry Safonov if (!hash_buf) 6549427c6aaSDmitry Safonov return -ENOMEM; 6559427c6aaSDmitry Safonov 6569427c6aaSDmitry Safonov err = tcp_v4_ao_calc_key_rsk(ao_key, hash_buf, req); 6579427c6aaSDmitry Safonov if (err) 6589427c6aaSDmitry Safonov goto out; 6599427c6aaSDmitry Safonov 6609427c6aaSDmitry Safonov err = tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb, 6619427c6aaSDmitry Safonov hash_buf, hash_offset, sne); 6629427c6aaSDmitry Safonov out: 6639427c6aaSDmitry Safonov kfree(hash_buf); 6649427c6aaSDmitry Safonov return err; 6659427c6aaSDmitry Safonov } 6669427c6aaSDmitry Safonov 66706b22ef2SDmitry Safonov struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk, 66806b22ef2SDmitry Safonov struct request_sock *req, 66906b22ef2SDmitry Safonov int sndid, int rcvid) 67006b22ef2SDmitry Safonov { 67106b22ef2SDmitry Safonov union tcp_ao_addr *addr = 67206b22ef2SDmitry Safonov (union tcp_ao_addr *)&inet_rsk(req)->ir_rmt_addr; 67306b22ef2SDmitry Safonov 67406b22ef2SDmitry Safonov return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid); 67506b22ef2SDmitry Safonov } 67606b22ef2SDmitry Safonov 6770aadc739SDmitry Safonov struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, 6780aadc739SDmitry Safonov int sndid, int rcvid) 6790aadc739SDmitry Safonov { 6800aadc739SDmitry Safonov union tcp_ao_addr *addr = (union tcp_ao_addr *)&addr_sk->sk_daddr; 6810aadc739SDmitry Safonov 6820aadc739SDmitry Safonov return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid); 6830aadc739SDmitry Safonov } 6840aadc739SDmitry Safonov 685ba7783adSDmitry Safonov int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, 68664382c71SDmitry Safonov const struct tcp_ao_hdr *aoh, int l3index, u32 seq, 687ba7783adSDmitry Safonov struct tcp_ao_key **key, char **traffic_key, 688ba7783adSDmitry Safonov bool *allocated_traffic_key, u8 *keyid, u32 *sne) 689ba7783adSDmitry Safonov { 69006b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 691ba7783adSDmitry Safonov struct tcp_ao_info *ao_info; 692ba7783adSDmitry Safonov 693ba7783adSDmitry Safonov *allocated_traffic_key = false; 694ba7783adSDmitry Safonov /* If there's no socket - than initial sisn/disn are unknown. 695ba7783adSDmitry Safonov * Drop the segment. RFC5925 (7.7) advises to require graceful 696ba7783adSDmitry Safonov * restart [RFC4724]. Alternatively, the RFC5925 advises to 697ba7783adSDmitry Safonov * save/restore traffic keys before/after reboot. 698ba7783adSDmitry Safonov * Linux TCP-AO support provides TCP_AO_ADD_KEY and TCP_AO_REPAIR 699ba7783adSDmitry Safonov * options to restore a socket post-reboot. 700ba7783adSDmitry Safonov */ 701ba7783adSDmitry Safonov if (!sk) 702ba7783adSDmitry Safonov return -ENOTCONN; 703ba7783adSDmitry Safonov 704decde258SDmitry Safonov if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { 70506b22ef2SDmitry Safonov unsigned int family = READ_ONCE(sk->sk_family); 70606b22ef2SDmitry Safonov union tcp_ao_addr *addr; 70706b22ef2SDmitry Safonov __be32 disn, sisn; 70806b22ef2SDmitry Safonov 70906b22ef2SDmitry Safonov if (sk->sk_state == TCP_NEW_SYN_RECV) { 71006b22ef2SDmitry Safonov struct request_sock *req = inet_reqsk(sk); 71106b22ef2SDmitry Safonov 71206b22ef2SDmitry Safonov sisn = htonl(tcp_rsk(req)->rcv_isn); 71306b22ef2SDmitry Safonov disn = htonl(tcp_rsk(req)->snt_isn); 71464382c71SDmitry Safonov *sne = tcp_ao_compute_sne(0, tcp_rsk(req)->snt_isn, seq); 71506b22ef2SDmitry Safonov } else { 71606b22ef2SDmitry Safonov sisn = th->seq; 71706b22ef2SDmitry Safonov disn = 0; 71806b22ef2SDmitry Safonov } 71906b22ef2SDmitry Safonov if (IS_ENABLED(CONFIG_IPV6) && family == AF_INET6) 72006b22ef2SDmitry Safonov addr = (union tcp_md5_addr *)&ipv6_hdr(skb)->saddr; 72106b22ef2SDmitry Safonov else 72206b22ef2SDmitry Safonov addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr; 72306b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 72406b22ef2SDmitry Safonov if (family == AF_INET6 && ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 72506b22ef2SDmitry Safonov family = AF_INET; 72606b22ef2SDmitry Safonov #endif 72706b22ef2SDmitry Safonov 72806b22ef2SDmitry Safonov sk = sk_const_to_full_sk(sk); 72906b22ef2SDmitry Safonov ao_info = rcu_dereference(tcp_sk(sk)->ao_info); 73006b22ef2SDmitry Safonov if (!ao_info) 73106b22ef2SDmitry Safonov return -ENOENT; 73206b22ef2SDmitry Safonov *key = tcp_ao_do_lookup(sk, addr, family, -1, aoh->rnext_keyid); 73306b22ef2SDmitry Safonov if (!*key) 73406b22ef2SDmitry Safonov return -ENOENT; 73506b22ef2SDmitry Safonov *traffic_key = kmalloc(tcp_ao_digest_size(*key), GFP_ATOMIC); 73606b22ef2SDmitry Safonov if (!*traffic_key) 73706b22ef2SDmitry Safonov return -ENOMEM; 73806b22ef2SDmitry Safonov *allocated_traffic_key = true; 73906b22ef2SDmitry Safonov if (tcp_ao_calc_key_skb(*key, *traffic_key, skb, 74006b22ef2SDmitry Safonov sisn, disn, family)) 741ba7783adSDmitry Safonov return -1; 74206b22ef2SDmitry Safonov *keyid = (*key)->rcvid; 74306b22ef2SDmitry Safonov } else { 74406b22ef2SDmitry Safonov struct tcp_ao_key *rnext_key; 74564382c71SDmitry Safonov u32 snd_basis; 746ba7783adSDmitry Safonov 74764382c71SDmitry Safonov if (sk->sk_state == TCP_TIME_WAIT) { 748decde258SDmitry Safonov ao_info = rcu_dereference(tcp_twsk(sk)->ao_info); 74964382c71SDmitry Safonov snd_basis = tcp_twsk(sk)->tw_snd_nxt; 75064382c71SDmitry Safonov } else { 751ba7783adSDmitry Safonov ao_info = rcu_dereference(tcp_sk(sk)->ao_info); 75264382c71SDmitry Safonov snd_basis = tcp_sk(sk)->snd_una; 75364382c71SDmitry Safonov } 754ba7783adSDmitry Safonov if (!ao_info) 755ba7783adSDmitry Safonov return -ENOENT; 756ba7783adSDmitry Safonov 757ba7783adSDmitry Safonov *key = tcp_ao_established_key(ao_info, aoh->rnext_keyid, -1); 758ba7783adSDmitry Safonov if (!*key) 759ba7783adSDmitry Safonov return -ENOENT; 760ba7783adSDmitry Safonov *traffic_key = snd_other_key(*key); 761ba7783adSDmitry Safonov rnext_key = READ_ONCE(ao_info->rnext_key); 762ba7783adSDmitry Safonov *keyid = rnext_key->rcvid; 76364382c71SDmitry Safonov *sne = tcp_ao_compute_sne(READ_ONCE(ao_info->snd_sne), 76464382c71SDmitry Safonov snd_basis, seq); 76506b22ef2SDmitry Safonov } 766ba7783adSDmitry Safonov return 0; 767ba7783adSDmitry Safonov } 768ba7783adSDmitry Safonov 7691e03d32bSDmitry Safonov int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, 7701e03d32bSDmitry Safonov struct tcp_ao_key *key, struct tcphdr *th, 7711e03d32bSDmitry Safonov __u8 *hash_location) 7721e03d32bSDmitry Safonov { 7731e03d32bSDmitry Safonov struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); 7741e03d32bSDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 7751e03d32bSDmitry Safonov struct tcp_ao_info *ao; 7761e03d32bSDmitry Safonov void *tkey_buf = NULL; 7771e03d32bSDmitry Safonov u8 *traffic_key; 77864382c71SDmitry Safonov u32 sne; 7791e03d32bSDmitry Safonov 7801e03d32bSDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 7811e03d32bSDmitry Safonov lockdep_sock_is_held(sk)); 7821e03d32bSDmitry Safonov traffic_key = snd_other_key(key); 7831e03d32bSDmitry Safonov if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) { 7841e03d32bSDmitry Safonov __be32 disn; 7851e03d32bSDmitry Safonov 7861e03d32bSDmitry Safonov if (!(tcb->tcp_flags & TCPHDR_ACK)) { 7871e03d32bSDmitry Safonov disn = 0; 7881e03d32bSDmitry Safonov tkey_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 7891e03d32bSDmitry Safonov if (!tkey_buf) 7901e03d32bSDmitry Safonov return -ENOMEM; 7911e03d32bSDmitry Safonov traffic_key = tkey_buf; 7921e03d32bSDmitry Safonov } else { 7931e03d32bSDmitry Safonov disn = ao->risn; 7941e03d32bSDmitry Safonov } 7951e03d32bSDmitry Safonov tp->af_specific->ao_calc_key_sk(key, traffic_key, 7961e03d32bSDmitry Safonov sk, ao->lisn, disn, true); 7971e03d32bSDmitry Safonov } 79864382c71SDmitry Safonov sne = tcp_ao_compute_sne(READ_ONCE(ao->snd_sne), READ_ONCE(tp->snd_una), 79964382c71SDmitry Safonov ntohl(th->seq)); 8001e03d32bSDmitry Safonov tp->af_specific->calc_ao_hash(hash_location, key, sk, skb, traffic_key, 80164382c71SDmitry Safonov hash_location - (u8 *)th, sne); 8021e03d32bSDmitry Safonov kfree(tkey_buf); 8031e03d32bSDmitry Safonov return 0; 8041e03d32bSDmitry Safonov } 8051e03d32bSDmitry Safonov 80606b22ef2SDmitry Safonov static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family, 80706b22ef2SDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 80806b22ef2SDmitry Safonov int sndid, int rcvid) 80906b22ef2SDmitry Safonov { 81006b22ef2SDmitry Safonov if (family == AF_INET) { 81106b22ef2SDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 81206b22ef2SDmitry Safonov 81306b22ef2SDmitry Safonov return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr, 81406b22ef2SDmitry Safonov AF_INET, sndid, rcvid); 81506b22ef2SDmitry Safonov } else { 81606b22ef2SDmitry Safonov const struct ipv6hdr *iph = ipv6_hdr(skb); 81706b22ef2SDmitry Safonov 81806b22ef2SDmitry Safonov return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr, 81906b22ef2SDmitry Safonov AF_INET6, sndid, rcvid); 82006b22ef2SDmitry Safonov } 82106b22ef2SDmitry Safonov } 82206b22ef2SDmitry Safonov 82306b22ef2SDmitry Safonov void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, 82406b22ef2SDmitry Safonov struct tcp_request_sock *treq, 82506b22ef2SDmitry Safonov unsigned short int family) 82606b22ef2SDmitry Safonov { 82706b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 82806b22ef2SDmitry Safonov const struct tcp_ao_hdr *aoh; 82906b22ef2SDmitry Safonov struct tcp_ao_key *key; 83006b22ef2SDmitry Safonov 83106b22ef2SDmitry Safonov treq->maclen = 0; 83206b22ef2SDmitry Safonov 83306b22ef2SDmitry Safonov if (tcp_parse_auth_options(th, NULL, &aoh) || !aoh) 83406b22ef2SDmitry Safonov return; 83506b22ef2SDmitry Safonov 83606b22ef2SDmitry Safonov key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid); 83706b22ef2SDmitry Safonov if (!key) 83806b22ef2SDmitry Safonov /* Key not found, continue without TCP-AO */ 83906b22ef2SDmitry Safonov return; 84006b22ef2SDmitry Safonov 84106b22ef2SDmitry Safonov treq->ao_rcv_next = aoh->keyid; 84206b22ef2SDmitry Safonov treq->ao_keyid = aoh->rnext_keyid; 84306b22ef2SDmitry Safonov treq->maclen = tcp_ao_maclen(key); 84406b22ef2SDmitry Safonov } 84506b22ef2SDmitry Safonov 8460a3a8090SDmitry Safonov static enum skb_drop_reason 8470a3a8090SDmitry Safonov tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, 8480a3a8090SDmitry Safonov unsigned short int family, struct tcp_ao_info *info, 8490a3a8090SDmitry Safonov const struct tcp_ao_hdr *aoh, struct tcp_ao_key *key, 8500a3a8090SDmitry Safonov u8 *traffic_key, u8 *phash, u32 sne) 8510a3a8090SDmitry Safonov { 8520a3a8090SDmitry Safonov u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr); 8530a3a8090SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 8540a3a8090SDmitry Safonov void *hash_buf = NULL; 8550a3a8090SDmitry Safonov 856af09a341SDmitry Safonov if (maclen != tcp_ao_maclen(key)) { 857af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); 858af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_bad); 859af09a341SDmitry Safonov atomic64_inc(&key->pkt_bad); 8602717b5adSDmitry Safonov tcp_hash_fail("AO hash wrong length", family, skb, 8612717b5adSDmitry Safonov "%u != %d", maclen, tcp_ao_maclen(key)); 8620a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 863af09a341SDmitry Safonov } 8640a3a8090SDmitry Safonov 8650a3a8090SDmitry Safonov hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 8660a3a8090SDmitry Safonov if (!hash_buf) 8670a3a8090SDmitry Safonov return SKB_DROP_REASON_NOT_SPECIFIED; 8680a3a8090SDmitry Safonov 8690a3a8090SDmitry Safonov /* XXX: make it per-AF callback? */ 8700a3a8090SDmitry Safonov tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key, 8710a3a8090SDmitry Safonov (phash - (u8 *)th), sne); 8720a3a8090SDmitry Safonov if (memcmp(phash, hash_buf, maclen)) { 873af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); 874af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_bad); 875af09a341SDmitry Safonov atomic64_inc(&key->pkt_bad); 8762717b5adSDmitry Safonov tcp_hash_fail("AO hash mismatch", family, skb, ""); 8770a3a8090SDmitry Safonov kfree(hash_buf); 8780a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 8790a3a8090SDmitry Safonov } 880af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD); 881af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_good); 882af09a341SDmitry Safonov atomic64_inc(&key->pkt_good); 8830a3a8090SDmitry Safonov kfree(hash_buf); 8840a3a8090SDmitry Safonov return SKB_NOT_DROPPED_YET; 8850a3a8090SDmitry Safonov } 8860a3a8090SDmitry Safonov 8870a3a8090SDmitry Safonov enum skb_drop_reason 8880a3a8090SDmitry Safonov tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, 8890a3a8090SDmitry Safonov unsigned short int family, const struct request_sock *req, 8900a3a8090SDmitry Safonov const struct tcp_ao_hdr *aoh) 8910a3a8090SDmitry Safonov { 8920a3a8090SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 8930a3a8090SDmitry Safonov u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */ 8940a3a8090SDmitry Safonov struct tcp_ao_info *info; 8950a3a8090SDmitry Safonov enum skb_drop_reason ret; 8960a3a8090SDmitry Safonov struct tcp_ao_key *key; 8970a3a8090SDmitry Safonov __be32 sisn, disn; 8980a3a8090SDmitry Safonov u8 *traffic_key; 8990a3a8090SDmitry Safonov u32 sne = 0; 9000a3a8090SDmitry Safonov 9010a3a8090SDmitry Safonov info = rcu_dereference(tcp_sk(sk)->ao_info); 902af09a341SDmitry Safonov if (!info) { 903af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); 9042717b5adSDmitry Safonov tcp_hash_fail("AO key not found", family, skb, 9052717b5adSDmitry Safonov "keyid: %u", aoh->keyid); 9060a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOUNEXPECTED; 907af09a341SDmitry Safonov } 9080a3a8090SDmitry Safonov 9090a3a8090SDmitry Safonov if (unlikely(th->syn)) { 9100a3a8090SDmitry Safonov sisn = th->seq; 9110a3a8090SDmitry Safonov disn = 0; 9120a3a8090SDmitry Safonov } 9130a3a8090SDmitry Safonov 9140a3a8090SDmitry Safonov /* Fast-path */ 9150a3a8090SDmitry Safonov if (likely((1 << sk->sk_state) & TCP_AO_ESTABLISHED)) { 9160a3a8090SDmitry Safonov enum skb_drop_reason err; 9170a3a8090SDmitry Safonov struct tcp_ao_key *current_key; 9180a3a8090SDmitry Safonov 9190a3a8090SDmitry Safonov /* Check if this socket's rnext_key matches the keyid in the 9200a3a8090SDmitry Safonov * packet. If not we lookup the key based on the keyid 9210a3a8090SDmitry Safonov * matching the rcvid in the mkt. 9220a3a8090SDmitry Safonov */ 9230a3a8090SDmitry Safonov key = READ_ONCE(info->rnext_key); 9240a3a8090SDmitry Safonov if (key->rcvid != aoh->keyid) { 9250a3a8090SDmitry Safonov key = tcp_ao_established_key(info, -1, aoh->keyid); 9260a3a8090SDmitry Safonov if (!key) 9270a3a8090SDmitry Safonov goto key_not_found; 9280a3a8090SDmitry Safonov } 9290a3a8090SDmitry Safonov 9300a3a8090SDmitry Safonov /* Delayed retransmitted SYN */ 9310a3a8090SDmitry Safonov if (unlikely(th->syn && !th->ack)) 9320a3a8090SDmitry Safonov goto verify_hash; 9330a3a8090SDmitry Safonov 93464382c71SDmitry Safonov sne = tcp_ao_compute_sne(info->rcv_sne, tcp_sk(sk)->rcv_nxt, 93564382c71SDmitry Safonov ntohl(th->seq)); 9360a3a8090SDmitry Safonov /* Established socket, traffic key are cached */ 9370a3a8090SDmitry Safonov traffic_key = rcv_other_key(key); 9380a3a8090SDmitry Safonov err = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, 9390a3a8090SDmitry Safonov traffic_key, phash, sne); 9400a3a8090SDmitry Safonov if (err) 9410a3a8090SDmitry Safonov return err; 9420a3a8090SDmitry Safonov current_key = READ_ONCE(info->current_key); 9430a3a8090SDmitry Safonov /* Key rotation: the peer asks us to use new key (RNext) */ 9440a3a8090SDmitry Safonov if (unlikely(aoh->rnext_keyid != current_key->sndid)) { 9450a3a8090SDmitry Safonov /* If the key is not found we do nothing. */ 9460a3a8090SDmitry Safonov key = tcp_ao_established_key(info, aoh->rnext_keyid, -1); 9470a3a8090SDmitry Safonov if (key) 9480a3a8090SDmitry Safonov /* pairs with tcp_ao_del_cmd */ 9490a3a8090SDmitry Safonov WRITE_ONCE(info->current_key, key); 9500a3a8090SDmitry Safonov } 9510a3a8090SDmitry Safonov return SKB_NOT_DROPPED_YET; 9520a3a8090SDmitry Safonov } 9530a3a8090SDmitry Safonov 9540a3a8090SDmitry Safonov /* Lookup key based on peer address and keyid. 9550a3a8090SDmitry Safonov * current_key and rnext_key must not be used on tcp listen 9560a3a8090SDmitry Safonov * sockets as otherwise: 9570a3a8090SDmitry Safonov * - request sockets would race on those key pointers 9580a3a8090SDmitry Safonov * - tcp_ao_del_cmd() allows async key removal 9590a3a8090SDmitry Safonov */ 9600a3a8090SDmitry Safonov key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid); 9610a3a8090SDmitry Safonov if (!key) 9620a3a8090SDmitry Safonov goto key_not_found; 9630a3a8090SDmitry Safonov 9640a3a8090SDmitry Safonov if (th->syn && !th->ack) 9650a3a8090SDmitry Safonov goto verify_hash; 9660a3a8090SDmitry Safonov 9670a3a8090SDmitry Safonov if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { 9680a3a8090SDmitry Safonov /* Make the initial syn the likely case here */ 9690a3a8090SDmitry Safonov if (unlikely(req)) { 97064382c71SDmitry Safonov sne = tcp_ao_compute_sne(0, tcp_rsk(req)->rcv_isn, 97164382c71SDmitry Safonov ntohl(th->seq)); 9720a3a8090SDmitry Safonov sisn = htonl(tcp_rsk(req)->rcv_isn); 9730a3a8090SDmitry Safonov disn = htonl(tcp_rsk(req)->snt_isn); 9740a3a8090SDmitry Safonov } else if (unlikely(th->ack && !th->syn)) { 9750a3a8090SDmitry Safonov /* Possible syncookie packet */ 9760a3a8090SDmitry Safonov sisn = htonl(ntohl(th->seq) - 1); 9770a3a8090SDmitry Safonov disn = htonl(ntohl(th->ack_seq) - 1); 97864382c71SDmitry Safonov sne = tcp_ao_compute_sne(0, ntohl(sisn), 97964382c71SDmitry Safonov ntohl(th->seq)); 9800a3a8090SDmitry Safonov } else if (unlikely(!th->syn)) { 9810a3a8090SDmitry Safonov /* no way to figure out initial sisn/disn - drop */ 9820a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_FLAGS; 9830a3a8090SDmitry Safonov } 9840a3a8090SDmitry Safonov } else if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { 9850a3a8090SDmitry Safonov disn = info->lisn; 9860a3a8090SDmitry Safonov if (th->syn || th->rst) 9870a3a8090SDmitry Safonov sisn = th->seq; 9880a3a8090SDmitry Safonov else 9890a3a8090SDmitry Safonov sisn = info->risn; 9900a3a8090SDmitry Safonov } else { 9910a3a8090SDmitry Safonov WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", sk->sk_state); 9920a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 9930a3a8090SDmitry Safonov } 9940a3a8090SDmitry Safonov verify_hash: 9950a3a8090SDmitry Safonov traffic_key = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 9960a3a8090SDmitry Safonov if (!traffic_key) 9970a3a8090SDmitry Safonov return SKB_DROP_REASON_NOT_SPECIFIED; 9980a3a8090SDmitry Safonov tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family); 9990a3a8090SDmitry Safonov ret = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, 10000a3a8090SDmitry Safonov traffic_key, phash, sne); 10010a3a8090SDmitry Safonov kfree(traffic_key); 10020a3a8090SDmitry Safonov return ret; 10030a3a8090SDmitry Safonov 10040a3a8090SDmitry Safonov key_not_found: 1005af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); 1006af09a341SDmitry Safonov atomic64_inc(&info->counters.key_not_found); 10072717b5adSDmitry Safonov tcp_hash_fail("Requested by the peer AO key id not found", 10082717b5adSDmitry Safonov family, skb, ""); 10090a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; 10100a3a8090SDmitry Safonov } 10110a3a8090SDmitry Safonov 10127c2ffaf2SDmitry Safonov static int tcp_ao_cache_traffic_keys(const struct sock *sk, 10137c2ffaf2SDmitry Safonov struct tcp_ao_info *ao, 10147c2ffaf2SDmitry Safonov struct tcp_ao_key *ao_key) 10157c2ffaf2SDmitry Safonov { 10167c2ffaf2SDmitry Safonov u8 *traffic_key = snd_other_key(ao_key); 10177c2ffaf2SDmitry Safonov int ret; 10187c2ffaf2SDmitry Safonov 10197c2ffaf2SDmitry Safonov ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk, 10207c2ffaf2SDmitry Safonov ao->lisn, ao->risn, true); 10217c2ffaf2SDmitry Safonov if (ret) 10227c2ffaf2SDmitry Safonov return ret; 10237c2ffaf2SDmitry Safonov 10247c2ffaf2SDmitry Safonov traffic_key = rcv_other_key(ao_key); 10257c2ffaf2SDmitry Safonov ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk, 10267c2ffaf2SDmitry Safonov ao->lisn, ao->risn, false); 10277c2ffaf2SDmitry Safonov return ret; 10287c2ffaf2SDmitry Safonov } 10297c2ffaf2SDmitry Safonov 10307c2ffaf2SDmitry Safonov void tcp_ao_connect_init(struct sock *sk) 10317c2ffaf2SDmitry Safonov { 10327c2ffaf2SDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 10337c2ffaf2SDmitry Safonov struct tcp_ao_info *ao_info; 10347c2ffaf2SDmitry Safonov union tcp_ao_addr *addr; 10357c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 10367c2ffaf2SDmitry Safonov int family; 10377c2ffaf2SDmitry Safonov 10387c2ffaf2SDmitry Safonov ao_info = rcu_dereference_protected(tp->ao_info, 10397c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 10407c2ffaf2SDmitry Safonov if (!ao_info) 10417c2ffaf2SDmitry Safonov return; 10427c2ffaf2SDmitry Safonov 10437c2ffaf2SDmitry Safonov /* Remove all keys that don't match the peer */ 10447c2ffaf2SDmitry Safonov family = sk->sk_family; 10457c2ffaf2SDmitry Safonov if (family == AF_INET) 10467c2ffaf2SDmitry Safonov addr = (union tcp_ao_addr *)&sk->sk_daddr; 10477c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 10487c2ffaf2SDmitry Safonov else if (family == AF_INET6) 10497c2ffaf2SDmitry Safonov addr = (union tcp_ao_addr *)&sk->sk_v6_daddr; 10507c2ffaf2SDmitry Safonov #endif 10517c2ffaf2SDmitry Safonov else 10527c2ffaf2SDmitry Safonov return; 10537c2ffaf2SDmitry Safonov 10547c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 10557c2ffaf2SDmitry Safonov if (!tcp_ao_key_cmp(key, addr, key->prefixlen, family, -1, -1)) 10567c2ffaf2SDmitry Safonov continue; 10577c2ffaf2SDmitry Safonov 10587c2ffaf2SDmitry Safonov if (key == ao_info->current_key) 10597c2ffaf2SDmitry Safonov ao_info->current_key = NULL; 10607c2ffaf2SDmitry Safonov if (key == ao_info->rnext_key) 10617c2ffaf2SDmitry Safonov ao_info->rnext_key = NULL; 10627c2ffaf2SDmitry Safonov hlist_del_rcu(&key->node); 10637c2ffaf2SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 10647c2ffaf2SDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 10657c2ffaf2SDmitry Safonov } 10667c2ffaf2SDmitry Safonov 10677c2ffaf2SDmitry Safonov key = tp->af_specific->ao_lookup(sk, sk, -1, -1); 10687c2ffaf2SDmitry Safonov if (key) { 10697c2ffaf2SDmitry Safonov /* if current_key or rnext_key were not provided, 10707c2ffaf2SDmitry Safonov * use the first key matching the peer 10717c2ffaf2SDmitry Safonov */ 10727c2ffaf2SDmitry Safonov if (!ao_info->current_key) 10737c2ffaf2SDmitry Safonov ao_info->current_key = key; 10747c2ffaf2SDmitry Safonov if (!ao_info->rnext_key) 10757c2ffaf2SDmitry Safonov ao_info->rnext_key = key; 10767c2ffaf2SDmitry Safonov tp->tcp_header_len += tcp_ao_len(key); 10777c2ffaf2SDmitry Safonov 10787c2ffaf2SDmitry Safonov ao_info->lisn = htonl(tp->write_seq); 107964382c71SDmitry Safonov ao_info->snd_sne = 0; 10807c2ffaf2SDmitry Safonov } else { 10817c2ffaf2SDmitry Safonov /* Can't happen: tcp_connect() verifies that there's 10827c2ffaf2SDmitry Safonov * at least one tcp-ao key that matches the remote peer. 10837c2ffaf2SDmitry Safonov */ 10847c2ffaf2SDmitry Safonov WARN_ON_ONCE(1); 10857c2ffaf2SDmitry Safonov rcu_assign_pointer(tp->ao_info, NULL); 10867c2ffaf2SDmitry Safonov kfree(ao_info); 10877c2ffaf2SDmitry Safonov } 10887c2ffaf2SDmitry Safonov } 10897c2ffaf2SDmitry Safonov 10907c2ffaf2SDmitry Safonov void tcp_ao_established(struct sock *sk) 10917c2ffaf2SDmitry Safonov { 10927c2ffaf2SDmitry Safonov struct tcp_ao_info *ao; 10937c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 10947c2ffaf2SDmitry Safonov 10957c2ffaf2SDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 10967c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 10977c2ffaf2SDmitry Safonov if (!ao) 10987c2ffaf2SDmitry Safonov return; 10997c2ffaf2SDmitry Safonov 11007c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 11017c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 11027c2ffaf2SDmitry Safonov } 11037c2ffaf2SDmitry Safonov 11047c2ffaf2SDmitry Safonov void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb) 11057c2ffaf2SDmitry Safonov { 11067c2ffaf2SDmitry Safonov struct tcp_ao_info *ao; 11077c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 11087c2ffaf2SDmitry Safonov 11097c2ffaf2SDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 11107c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 11117c2ffaf2SDmitry Safonov if (!ao) 11127c2ffaf2SDmitry Safonov return; 11137c2ffaf2SDmitry Safonov 11147c2ffaf2SDmitry Safonov WRITE_ONCE(ao->risn, tcp_hdr(skb)->seq); 111564382c71SDmitry Safonov ao->rcv_sne = 0; 11167c2ffaf2SDmitry Safonov 11177c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 11187c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 11197c2ffaf2SDmitry Safonov } 11207c2ffaf2SDmitry Safonov 112106b22ef2SDmitry Safonov int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, 112206b22ef2SDmitry Safonov struct request_sock *req, struct sk_buff *skb, 112306b22ef2SDmitry Safonov int family) 112406b22ef2SDmitry Safonov { 112506b22ef2SDmitry Safonov struct tcp_ao_key *key, *new_key, *first_key; 112606b22ef2SDmitry Safonov struct tcp_ao_info *new_ao, *ao; 112706b22ef2SDmitry Safonov struct hlist_node *key_head; 112806b22ef2SDmitry Safonov union tcp_ao_addr *addr; 112906b22ef2SDmitry Safonov bool match = false; 113006b22ef2SDmitry Safonov int ret = -ENOMEM; 113106b22ef2SDmitry Safonov 113206b22ef2SDmitry Safonov ao = rcu_dereference(tcp_sk(sk)->ao_info); 113306b22ef2SDmitry Safonov if (!ao) 113406b22ef2SDmitry Safonov return 0; 113506b22ef2SDmitry Safonov 113606b22ef2SDmitry Safonov /* New socket without TCP-AO on it */ 113706b22ef2SDmitry Safonov if (!tcp_rsk_used_ao(req)) 113806b22ef2SDmitry Safonov return 0; 113906b22ef2SDmitry Safonov 114006b22ef2SDmitry Safonov new_ao = tcp_ao_alloc_info(GFP_ATOMIC); 114106b22ef2SDmitry Safonov if (!new_ao) 114206b22ef2SDmitry Safonov return -ENOMEM; 114306b22ef2SDmitry Safonov new_ao->lisn = htonl(tcp_rsk(req)->snt_isn); 114406b22ef2SDmitry Safonov new_ao->risn = htonl(tcp_rsk(req)->rcv_isn); 114506b22ef2SDmitry Safonov new_ao->ao_required = ao->ao_required; 1146953af8e3SDmitry Safonov new_ao->accept_icmps = ao->accept_icmps; 114706b22ef2SDmitry Safonov 114806b22ef2SDmitry Safonov if (family == AF_INET) { 114906b22ef2SDmitry Safonov addr = (union tcp_ao_addr *)&newsk->sk_daddr; 115006b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 115106b22ef2SDmitry Safonov } else if (family == AF_INET6) { 115206b22ef2SDmitry Safonov addr = (union tcp_ao_addr *)&newsk->sk_v6_daddr; 115306b22ef2SDmitry Safonov #endif 115406b22ef2SDmitry Safonov } else { 115506b22ef2SDmitry Safonov ret = -EAFNOSUPPORT; 115606b22ef2SDmitry Safonov goto free_ao; 115706b22ef2SDmitry Safonov } 115806b22ef2SDmitry Safonov 115906b22ef2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 116006b22ef2SDmitry Safonov if (tcp_ao_key_cmp(key, addr, key->prefixlen, family, -1, -1)) 116106b22ef2SDmitry Safonov continue; 116206b22ef2SDmitry Safonov 116306b22ef2SDmitry Safonov new_key = tcp_ao_copy_key(newsk, key); 116406b22ef2SDmitry Safonov if (!new_key) 116506b22ef2SDmitry Safonov goto free_and_exit; 116606b22ef2SDmitry Safonov 116706b22ef2SDmitry Safonov tcp_ao_cache_traffic_keys(newsk, new_ao, new_key); 116806b22ef2SDmitry Safonov tcp_ao_link_mkt(new_ao, new_key); 116906b22ef2SDmitry Safonov match = true; 117006b22ef2SDmitry Safonov } 117106b22ef2SDmitry Safonov 117206b22ef2SDmitry Safonov if (!match) { 117306b22ef2SDmitry Safonov /* RFC5925 (7.4.1) specifies that the TCP-AO status 117406b22ef2SDmitry Safonov * of a connection is determined on the initial SYN. 117506b22ef2SDmitry Safonov * At this point the connection was TCP-AO enabled, so 117606b22ef2SDmitry Safonov * it can't switch to being unsigned if peer's key 117706b22ef2SDmitry Safonov * disappears on the listening socket. 117806b22ef2SDmitry Safonov */ 117906b22ef2SDmitry Safonov ret = -EKEYREJECTED; 118006b22ef2SDmitry Safonov goto free_and_exit; 118106b22ef2SDmitry Safonov } 118206b22ef2SDmitry Safonov 118306b22ef2SDmitry Safonov key_head = rcu_dereference(hlist_first_rcu(&new_ao->head)); 118406b22ef2SDmitry Safonov first_key = hlist_entry_safe(key_head, struct tcp_ao_key, node); 118506b22ef2SDmitry Safonov 118606b22ef2SDmitry Safonov key = tcp_ao_established_key(new_ao, tcp_rsk(req)->ao_keyid, -1); 118706b22ef2SDmitry Safonov if (key) 118806b22ef2SDmitry Safonov new_ao->current_key = key; 118906b22ef2SDmitry Safonov else 119006b22ef2SDmitry Safonov new_ao->current_key = first_key; 119106b22ef2SDmitry Safonov 119206b22ef2SDmitry Safonov /* set rnext_key */ 119306b22ef2SDmitry Safonov key = tcp_ao_established_key(new_ao, -1, tcp_rsk(req)->ao_rcv_next); 119406b22ef2SDmitry Safonov if (key) 119506b22ef2SDmitry Safonov new_ao->rnext_key = key; 119606b22ef2SDmitry Safonov else 119706b22ef2SDmitry Safonov new_ao->rnext_key = first_key; 119806b22ef2SDmitry Safonov 119906b22ef2SDmitry Safonov sk_gso_disable(newsk); 120006b22ef2SDmitry Safonov rcu_assign_pointer(tcp_sk(newsk)->ao_info, new_ao); 120106b22ef2SDmitry Safonov 120206b22ef2SDmitry Safonov return 0; 120306b22ef2SDmitry Safonov 120406b22ef2SDmitry Safonov free_and_exit: 120506b22ef2SDmitry Safonov hlist_for_each_entry_safe(key, key_head, &new_ao->head, node) { 120606b22ef2SDmitry Safonov hlist_del(&key->node); 120706b22ef2SDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 120806b22ef2SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc); 120906b22ef2SDmitry Safonov kfree_sensitive(key); 121006b22ef2SDmitry Safonov } 121106b22ef2SDmitry Safonov free_ao: 121206b22ef2SDmitry Safonov kfree(new_ao); 121306b22ef2SDmitry Safonov return ret; 121406b22ef2SDmitry Safonov } 121506b22ef2SDmitry Safonov 12164954f17dSDmitry Safonov static bool tcp_ao_can_set_current_rnext(struct sock *sk) 12174954f17dSDmitry Safonov { 12184954f17dSDmitry Safonov /* There aren't current/rnext keys on TCP_LISTEN sockets */ 12194954f17dSDmitry Safonov if (sk->sk_state == TCP_LISTEN) 12204954f17dSDmitry Safonov return false; 12214954f17dSDmitry Safonov return true; 12224954f17dSDmitry Safonov } 12234954f17dSDmitry Safonov 12244954f17dSDmitry Safonov static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao_add *cmd, 12254954f17dSDmitry Safonov union tcp_ao_addr **addr) 12264954f17dSDmitry Safonov { 12274954f17dSDmitry Safonov struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->addr; 12284954f17dSDmitry Safonov struct inet_sock *inet = inet_sk(sk); 12294954f17dSDmitry Safonov 12304954f17dSDmitry Safonov if (sin->sin_family != AF_INET) 12314954f17dSDmitry Safonov return -EINVAL; 12324954f17dSDmitry Safonov 12334954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 12344954f17dSDmitry Safonov if (sin->sin_port != 0) 12354954f17dSDmitry Safonov return -EINVAL; 12364954f17dSDmitry Safonov 12374954f17dSDmitry Safonov /* Check prefix and trailing 0's in addr */ 12384954f17dSDmitry Safonov if (cmd->prefix != 0) { 12394954f17dSDmitry Safonov __be32 mask; 12404954f17dSDmitry Safonov 12414954f17dSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY) 12424954f17dSDmitry Safonov return -EINVAL; 12434954f17dSDmitry Safonov if (cmd->prefix > 32) 12444954f17dSDmitry Safonov return -EINVAL; 12454954f17dSDmitry Safonov 12464954f17dSDmitry Safonov mask = inet_make_mask(cmd->prefix); 12474954f17dSDmitry Safonov if (sin->sin_addr.s_addr & ~mask) 12484954f17dSDmitry Safonov return -EINVAL; 12494954f17dSDmitry Safonov 12504954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 12514954f17dSDmitry Safonov if (ntohl(inet->inet_daddr) != INADDR_ANY && 12524954f17dSDmitry Safonov (inet->inet_daddr & mask) != sin->sin_addr.s_addr) 12534954f17dSDmitry Safonov return -EINVAL; 12544954f17dSDmitry Safonov } else { 12554954f17dSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) != INADDR_ANY) 12564954f17dSDmitry Safonov return -EINVAL; 12574954f17dSDmitry Safonov } 12584954f17dSDmitry Safonov 12594954f17dSDmitry Safonov *addr = (union tcp_ao_addr *)&sin->sin_addr; 12604954f17dSDmitry Safonov return 0; 12614954f17dSDmitry Safonov } 12624954f17dSDmitry Safonov 12634954f17dSDmitry Safonov static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key) 12644954f17dSDmitry Safonov { 12654954f17dSDmitry Safonov unsigned int syn_tcp_option_space; 12664954f17dSDmitry Safonov bool is_kdf_aes_128_cmac = false; 12674954f17dSDmitry Safonov struct crypto_ahash *tfm; 12684954f17dSDmitry Safonov struct tcp_sigpool hp; 12694954f17dSDmitry Safonov void *tmp_key = NULL; 12704954f17dSDmitry Safonov int err; 12714954f17dSDmitry Safonov 12724954f17dSDmitry Safonov /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 12734954f17dSDmitry Safonov if (!strcmp("cmac(aes128)", cmd->alg_name)) { 12744954f17dSDmitry Safonov strscpy(cmd->alg_name, "cmac(aes)", sizeof(cmd->alg_name)); 12754954f17dSDmitry Safonov is_kdf_aes_128_cmac = (cmd->keylen != 16); 12764954f17dSDmitry Safonov tmp_key = kmalloc(cmd->keylen, GFP_KERNEL); 12774954f17dSDmitry Safonov if (!tmp_key) 12784954f17dSDmitry Safonov return -ENOMEM; 12794954f17dSDmitry Safonov } 12804954f17dSDmitry Safonov 12814954f17dSDmitry Safonov key->maclen = cmd->maclen ?: 12; /* 12 is the default in RFC5925 */ 12824954f17dSDmitry Safonov 12834954f17dSDmitry Safonov /* Check: maclen + tcp-ao header <= (MAX_TCP_OPTION_SPACE - mss 12844954f17dSDmitry Safonov * - tstamp - wscale - sackperm), 12854954f17dSDmitry Safonov * see tcp_syn_options(), tcp_synack_options(), commit 33ad798c924b. 12864954f17dSDmitry Safonov * 12874954f17dSDmitry Safonov * In order to allow D-SACK with TCP-AO, the header size should be: 12884954f17dSDmitry Safonov * (MAX_TCP_OPTION_SPACE - TCPOLEN_TSTAMP_ALIGNED 12894954f17dSDmitry Safonov * - TCPOLEN_SACK_BASE_ALIGNED 12904954f17dSDmitry Safonov * - 2 * TCPOLEN_SACK_PERBLOCK) = 8 (maclen = 4), 12914954f17dSDmitry Safonov * see tcp_established_options(). 12924954f17dSDmitry Safonov * 12934954f17dSDmitry Safonov * RFC5925, 2.2: 12944954f17dSDmitry Safonov * Typical MACs are 96-128 bits (12-16 bytes), but any length 12954954f17dSDmitry Safonov * that fits in the header of the segment being authenticated 12964954f17dSDmitry Safonov * is allowed. 12974954f17dSDmitry Safonov * 12984954f17dSDmitry Safonov * RFC5925, 7.6: 12994954f17dSDmitry Safonov * TCP-AO continues to consume 16 bytes in non-SYN segments, 13004954f17dSDmitry Safonov * leaving a total of 24 bytes for other options, of which 13014954f17dSDmitry Safonov * the timestamp consumes 10. This leaves 14 bytes, of which 10 13024954f17dSDmitry Safonov * are used for a single SACK block. When two SACK blocks are used, 13034954f17dSDmitry Safonov * such as to handle D-SACK, a smaller TCP-AO MAC would be required 13044954f17dSDmitry Safonov * to make room for the additional SACK block (i.e., to leave 18 13054954f17dSDmitry Safonov * bytes for the D-SACK variant of the SACK option) [RFC2883]. 13064954f17dSDmitry Safonov * Note that D-SACK is not supportable in TCP MD5 in the presence 13074954f17dSDmitry Safonov * of timestamps, because TCP MD5’s MAC length is fixed and too 13084954f17dSDmitry Safonov * large to leave sufficient option space. 13094954f17dSDmitry Safonov */ 13104954f17dSDmitry Safonov syn_tcp_option_space = MAX_TCP_OPTION_SPACE; 13114954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED; 13124954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED; 13134954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_SACKPERM_ALIGNED; 13144954f17dSDmitry Safonov if (tcp_ao_len(key) > syn_tcp_option_space) { 13154954f17dSDmitry Safonov err = -EMSGSIZE; 13164954f17dSDmitry Safonov goto err_kfree; 13174954f17dSDmitry Safonov } 13184954f17dSDmitry Safonov 13194954f17dSDmitry Safonov key->keylen = cmd->keylen; 13204954f17dSDmitry Safonov memcpy(key->key, cmd->key, cmd->keylen); 13214954f17dSDmitry Safonov 13224954f17dSDmitry Safonov err = tcp_sigpool_start(key->tcp_sigpool_id, &hp); 13234954f17dSDmitry Safonov if (err) 13244954f17dSDmitry Safonov goto err_kfree; 13254954f17dSDmitry Safonov 13264954f17dSDmitry Safonov tfm = crypto_ahash_reqtfm(hp.req); 13274954f17dSDmitry Safonov if (is_kdf_aes_128_cmac) { 13284954f17dSDmitry Safonov void *scratch = hp.scratch; 13294954f17dSDmitry Safonov struct scatterlist sg; 13304954f17dSDmitry Safonov 13314954f17dSDmitry Safonov memcpy(tmp_key, cmd->key, cmd->keylen); 13324954f17dSDmitry Safonov sg_init_one(&sg, tmp_key, cmd->keylen); 13334954f17dSDmitry Safonov 13344954f17dSDmitry Safonov /* Using zero-key of 16 bytes as described in RFC5926 */ 13354954f17dSDmitry Safonov memset(scratch, 0, 16); 13364954f17dSDmitry Safonov err = crypto_ahash_setkey(tfm, scratch, 16); 13374954f17dSDmitry Safonov if (err) 13384954f17dSDmitry Safonov goto err_pool_end; 13394954f17dSDmitry Safonov 13404954f17dSDmitry Safonov err = crypto_ahash_init(hp.req); 13414954f17dSDmitry Safonov if (err) 13424954f17dSDmitry Safonov goto err_pool_end; 13434954f17dSDmitry Safonov 13444954f17dSDmitry Safonov ahash_request_set_crypt(hp.req, &sg, key->key, cmd->keylen); 13454954f17dSDmitry Safonov err = crypto_ahash_update(hp.req); 13464954f17dSDmitry Safonov if (err) 13474954f17dSDmitry Safonov goto err_pool_end; 13484954f17dSDmitry Safonov 13494954f17dSDmitry Safonov err |= crypto_ahash_final(hp.req); 13504954f17dSDmitry Safonov if (err) 13514954f17dSDmitry Safonov goto err_pool_end; 13524954f17dSDmitry Safonov key->keylen = 16; 13534954f17dSDmitry Safonov } 13544954f17dSDmitry Safonov 13554954f17dSDmitry Safonov err = crypto_ahash_setkey(tfm, key->key, key->keylen); 13564954f17dSDmitry Safonov if (err) 13574954f17dSDmitry Safonov goto err_pool_end; 13584954f17dSDmitry Safonov 13594954f17dSDmitry Safonov tcp_sigpool_end(&hp); 13604954f17dSDmitry Safonov kfree_sensitive(tmp_key); 13614954f17dSDmitry Safonov 13624954f17dSDmitry Safonov if (tcp_ao_maclen(key) > key->digest_size) 13634954f17dSDmitry Safonov return -EINVAL; 13644954f17dSDmitry Safonov 13654954f17dSDmitry Safonov return 0; 13664954f17dSDmitry Safonov 13674954f17dSDmitry Safonov err_pool_end: 13684954f17dSDmitry Safonov tcp_sigpool_end(&hp); 13694954f17dSDmitry Safonov err_kfree: 13704954f17dSDmitry Safonov kfree_sensitive(tmp_key); 13714954f17dSDmitry Safonov return err; 13724954f17dSDmitry Safonov } 13734954f17dSDmitry Safonov 13744954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 13754954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 13764954f17dSDmitry Safonov union tcp_ao_addr **paddr, 13774954f17dSDmitry Safonov unsigned short int *family) 13784954f17dSDmitry Safonov { 13794954f17dSDmitry Safonov struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->addr; 13804954f17dSDmitry Safonov struct in6_addr *addr = &sin6->sin6_addr; 13814954f17dSDmitry Safonov u8 prefix = cmd->prefix; 13824954f17dSDmitry Safonov 13834954f17dSDmitry Safonov if (sin6->sin6_family != AF_INET6) 13844954f17dSDmitry Safonov return -EINVAL; 13854954f17dSDmitry Safonov 13864954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 13874954f17dSDmitry Safonov if (sin6->sin6_port != 0) 13884954f17dSDmitry Safonov return -EINVAL; 13894954f17dSDmitry Safonov 13904954f17dSDmitry Safonov /* Check prefix and trailing 0's in addr */ 13914954f17dSDmitry Safonov if (cmd->prefix != 0 && ipv6_addr_v4mapped(addr)) { 13924954f17dSDmitry Safonov __be32 addr4 = addr->s6_addr32[3]; 13934954f17dSDmitry Safonov __be32 mask; 13944954f17dSDmitry Safonov 13954954f17dSDmitry Safonov if (prefix > 32 || ntohl(addr4) == INADDR_ANY) 13964954f17dSDmitry Safonov return -EINVAL; 13974954f17dSDmitry Safonov 13984954f17dSDmitry Safonov mask = inet_make_mask(prefix); 13994954f17dSDmitry Safonov if (addr4 & ~mask) 14004954f17dSDmitry Safonov return -EINVAL; 14014954f17dSDmitry Safonov 14024954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 14034954f17dSDmitry Safonov if (!ipv6_addr_any(&sk->sk_v6_daddr)) { 14044954f17dSDmitry Safonov __be32 daddr4 = sk->sk_v6_daddr.s6_addr32[3]; 14054954f17dSDmitry Safonov 14064954f17dSDmitry Safonov if (!ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 14074954f17dSDmitry Safonov return -EINVAL; 14084954f17dSDmitry Safonov if ((daddr4 & mask) != addr4) 14094954f17dSDmitry Safonov return -EINVAL; 14104954f17dSDmitry Safonov } 14114954f17dSDmitry Safonov 14124954f17dSDmitry Safonov *paddr = (union tcp_ao_addr *)&addr->s6_addr32[3]; 14134954f17dSDmitry Safonov *family = AF_INET; 14144954f17dSDmitry Safonov return 0; 14154954f17dSDmitry Safonov } else if (cmd->prefix != 0) { 14164954f17dSDmitry Safonov struct in6_addr pfx; 14174954f17dSDmitry Safonov 14184954f17dSDmitry Safonov if (ipv6_addr_any(addr) || prefix > 128) 14194954f17dSDmitry Safonov return -EINVAL; 14204954f17dSDmitry Safonov 14214954f17dSDmitry Safonov ipv6_addr_prefix(&pfx, addr, prefix); 14224954f17dSDmitry Safonov if (ipv6_addr_cmp(&pfx, addr)) 14234954f17dSDmitry Safonov return -EINVAL; 14244954f17dSDmitry Safonov 14254954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 14264954f17dSDmitry Safonov if (!ipv6_addr_any(&sk->sk_v6_daddr) && 14274954f17dSDmitry Safonov !ipv6_prefix_equal(&sk->sk_v6_daddr, addr, prefix)) 14284954f17dSDmitry Safonov 14294954f17dSDmitry Safonov return -EINVAL; 14304954f17dSDmitry Safonov } else { 14314954f17dSDmitry Safonov if (!ipv6_addr_any(addr)) 14324954f17dSDmitry Safonov return -EINVAL; 14334954f17dSDmitry Safonov } 14344954f17dSDmitry Safonov 14354954f17dSDmitry Safonov *paddr = (union tcp_ao_addr *)addr; 14364954f17dSDmitry Safonov return 0; 14374954f17dSDmitry Safonov } 14384954f17dSDmitry Safonov #else 14394954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 14404954f17dSDmitry Safonov union tcp_ao_addr **paddr, 14414954f17dSDmitry Safonov unsigned short int *family) 14424954f17dSDmitry Safonov { 14434954f17dSDmitry Safonov return -EOPNOTSUPP; 14444954f17dSDmitry Safonov } 14454954f17dSDmitry Safonov #endif 14464954f17dSDmitry Safonov 14474954f17dSDmitry Safonov static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) 14484954f17dSDmitry Safonov { 14494954f17dSDmitry Safonov if (sk_fullsock(sk)) { 14504954f17dSDmitry Safonov return rcu_dereference_protected(tcp_sk(sk)->ao_info, 14514954f17dSDmitry Safonov lockdep_sock_is_held(sk)); 1452decde258SDmitry Safonov } else if (sk->sk_state == TCP_TIME_WAIT) { 1453decde258SDmitry Safonov return rcu_dereference_protected(tcp_twsk(sk)->ao_info, 1454decde258SDmitry Safonov lockdep_sock_is_held(sk)); 14554954f17dSDmitry Safonov } 14564954f17dSDmitry Safonov return ERR_PTR(-ESOCKTNOSUPPORT); 14574954f17dSDmitry Safonov } 14584954f17dSDmitry Safonov 14597753c2f0SDmitry Safonov #define TCP_AO_KEYF_ALL (TCP_AO_KEYF_EXCLUDE_OPT) 14604954f17dSDmitry Safonov 14614954f17dSDmitry Safonov static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk, 14624954f17dSDmitry Safonov struct tcp_ao_add *cmd) 14634954f17dSDmitry Safonov { 14644954f17dSDmitry Safonov const char *algo = cmd->alg_name; 14654954f17dSDmitry Safonov unsigned int digest_size; 14664954f17dSDmitry Safonov struct crypto_ahash *tfm; 14674954f17dSDmitry Safonov struct tcp_ao_key *key; 14684954f17dSDmitry Safonov struct tcp_sigpool hp; 14694954f17dSDmitry Safonov int err, pool_id; 14704954f17dSDmitry Safonov size_t size; 14714954f17dSDmitry Safonov 14724954f17dSDmitry Safonov /* Force null-termination of alg_name */ 14734954f17dSDmitry Safonov cmd->alg_name[ARRAY_SIZE(cmd->alg_name) - 1] = '\0'; 14744954f17dSDmitry Safonov 14754954f17dSDmitry Safonov /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 14764954f17dSDmitry Safonov if (!strcmp("cmac(aes128)", algo)) 14774954f17dSDmitry Safonov algo = "cmac(aes)"; 14784954f17dSDmitry Safonov 14794954f17dSDmitry Safonov /* Full TCP header (th->doff << 2) should fit into scratch area, 14804954f17dSDmitry Safonov * see tcp_ao_hash_header(). 14814954f17dSDmitry Safonov */ 14824954f17dSDmitry Safonov pool_id = tcp_sigpool_alloc_ahash(algo, 60); 14834954f17dSDmitry Safonov if (pool_id < 0) 14844954f17dSDmitry Safonov return ERR_PTR(pool_id); 14854954f17dSDmitry Safonov 14864954f17dSDmitry Safonov err = tcp_sigpool_start(pool_id, &hp); 14874954f17dSDmitry Safonov if (err) 14884954f17dSDmitry Safonov goto err_free_pool; 14894954f17dSDmitry Safonov 14904954f17dSDmitry Safonov tfm = crypto_ahash_reqtfm(hp.req); 14914954f17dSDmitry Safonov if (crypto_ahash_alignmask(tfm) > TCP_AO_KEY_ALIGN) { 14924954f17dSDmitry Safonov err = -EOPNOTSUPP; 14934954f17dSDmitry Safonov goto err_pool_end; 14944954f17dSDmitry Safonov } 14954954f17dSDmitry Safonov digest_size = crypto_ahash_digestsize(tfm); 14964954f17dSDmitry Safonov tcp_sigpool_end(&hp); 14974954f17dSDmitry Safonov 14984954f17dSDmitry Safonov size = sizeof(struct tcp_ao_key) + (digest_size << 1); 14994954f17dSDmitry Safonov key = sock_kmalloc(sk, size, GFP_KERNEL); 15004954f17dSDmitry Safonov if (!key) { 15014954f17dSDmitry Safonov err = -ENOMEM; 15024954f17dSDmitry Safonov goto err_free_pool; 15034954f17dSDmitry Safonov } 15044954f17dSDmitry Safonov 15054954f17dSDmitry Safonov key->tcp_sigpool_id = pool_id; 15064954f17dSDmitry Safonov key->digest_size = digest_size; 15074954f17dSDmitry Safonov return key; 15084954f17dSDmitry Safonov 15094954f17dSDmitry Safonov err_pool_end: 15104954f17dSDmitry Safonov tcp_sigpool_end(&hp); 15114954f17dSDmitry Safonov err_free_pool: 15124954f17dSDmitry Safonov tcp_sigpool_release(pool_id); 15134954f17dSDmitry Safonov return ERR_PTR(err); 15144954f17dSDmitry Safonov } 15154954f17dSDmitry Safonov 15164954f17dSDmitry Safonov static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, 15174954f17dSDmitry Safonov sockptr_t optval, int optlen) 15184954f17dSDmitry Safonov { 15194954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 15204954f17dSDmitry Safonov union tcp_ao_addr *addr; 15214954f17dSDmitry Safonov struct tcp_ao_key *key; 15224954f17dSDmitry Safonov struct tcp_ao_add cmd; 15234954f17dSDmitry Safonov bool first = false; 15244954f17dSDmitry Safonov int ret; 15254954f17dSDmitry Safonov 15264954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 15274954f17dSDmitry Safonov return -EINVAL; 15284954f17dSDmitry Safonov 15294954f17dSDmitry Safonov ret = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 15304954f17dSDmitry Safonov if (ret) 15314954f17dSDmitry Safonov return ret; 15324954f17dSDmitry Safonov 15334954f17dSDmitry Safonov if (cmd.keylen > TCP_AO_MAXKEYLEN) 15344954f17dSDmitry Safonov return -EINVAL; 15354954f17dSDmitry Safonov 15364954f17dSDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 15374954f17dSDmitry Safonov return -EINVAL; 15384954f17dSDmitry Safonov 15394954f17dSDmitry Safonov if (family == AF_INET) 15404954f17dSDmitry Safonov ret = tcp_ao_verify_ipv4(sk, &cmd, &addr); 15414954f17dSDmitry Safonov else 15424954f17dSDmitry Safonov ret = tcp_ao_verify_ipv6(sk, &cmd, &addr, &family); 15434954f17dSDmitry Safonov if (ret) 15444954f17dSDmitry Safonov return ret; 15454954f17dSDmitry Safonov 15464954f17dSDmitry Safonov if (cmd.keyflags & ~TCP_AO_KEYF_ALL) 15474954f17dSDmitry Safonov return -EINVAL; 15484954f17dSDmitry Safonov 15494954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 15504954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 15514954f17dSDmitry Safonov return -EINVAL; 15524954f17dSDmitry Safonov } 15534954f17dSDmitry Safonov 15540aadc739SDmitry Safonov /* Don't allow keys for peers that have a matching TCP-MD5 key */ 15550aadc739SDmitry Safonov if (tcp_md5_do_lookup_any_l3index(sk, addr, family)) 15560aadc739SDmitry Safonov return -EKEYREJECTED; 15570aadc739SDmitry Safonov 15584954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 15594954f17dSDmitry Safonov if (IS_ERR(ao_info)) 15604954f17dSDmitry Safonov return PTR_ERR(ao_info); 15614954f17dSDmitry Safonov 15624954f17dSDmitry Safonov if (!ao_info) { 15634954f17dSDmitry Safonov ao_info = tcp_ao_alloc_info(GFP_KERNEL); 15644954f17dSDmitry Safonov if (!ao_info) 15654954f17dSDmitry Safonov return -ENOMEM; 15664954f17dSDmitry Safonov first = true; 15674954f17dSDmitry Safonov } else { 15684954f17dSDmitry Safonov /* Check that neither RecvID nor SendID match any 15694954f17dSDmitry Safonov * existing key for the peer, RFC5925 3.1: 15704954f17dSDmitry Safonov * > The IDs of MKTs MUST NOT overlap where their 15714954f17dSDmitry Safonov * > TCP connection identifiers overlap. 15724954f17dSDmitry Safonov */ 15734954f17dSDmitry Safonov if (__tcp_ao_do_lookup(sk, addr, family, 15744954f17dSDmitry Safonov cmd.prefix, -1, cmd.rcvid)) 15754954f17dSDmitry Safonov return -EEXIST; 15764954f17dSDmitry Safonov if (__tcp_ao_do_lookup(sk, addr, family, 15774954f17dSDmitry Safonov cmd.prefix, cmd.sndid, -1)) 15784954f17dSDmitry Safonov return -EEXIST; 15794954f17dSDmitry Safonov } 15804954f17dSDmitry Safonov 15814954f17dSDmitry Safonov key = tcp_ao_key_alloc(sk, &cmd); 15824954f17dSDmitry Safonov if (IS_ERR(key)) { 15834954f17dSDmitry Safonov ret = PTR_ERR(key); 15844954f17dSDmitry Safonov goto err_free_ao; 15854954f17dSDmitry Safonov } 15864954f17dSDmitry Safonov 15874954f17dSDmitry Safonov INIT_HLIST_NODE(&key->node); 15884954f17dSDmitry Safonov memcpy(&key->addr, addr, (family == AF_INET) ? sizeof(struct in_addr) : 15894954f17dSDmitry Safonov sizeof(struct in6_addr)); 15904954f17dSDmitry Safonov key->prefixlen = cmd.prefix; 15914954f17dSDmitry Safonov key->family = family; 15924954f17dSDmitry Safonov key->keyflags = cmd.keyflags; 15934954f17dSDmitry Safonov key->sndid = cmd.sndid; 15944954f17dSDmitry Safonov key->rcvid = cmd.rcvid; 1595af09a341SDmitry Safonov atomic64_set(&key->pkt_good, 0); 1596af09a341SDmitry Safonov atomic64_set(&key->pkt_bad, 0); 15974954f17dSDmitry Safonov 15984954f17dSDmitry Safonov ret = tcp_ao_parse_crypto(&cmd, key); 15994954f17dSDmitry Safonov if (ret < 0) 16004954f17dSDmitry Safonov goto err_free_sock; 16014954f17dSDmitry Safonov 16027c2ffaf2SDmitry Safonov /* Change this condition if we allow adding keys in states 16037c2ffaf2SDmitry Safonov * like close_wait, syn_sent or fin_wait... 16047c2ffaf2SDmitry Safonov */ 16057c2ffaf2SDmitry Safonov if (sk->sk_state == TCP_ESTABLISHED) 16067c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao_info, key); 16077c2ffaf2SDmitry Safonov 16084954f17dSDmitry Safonov tcp_ao_link_mkt(ao_info, key); 16094954f17dSDmitry Safonov if (first) { 16104954f17dSDmitry Safonov sk_gso_disable(sk); 16114954f17dSDmitry Safonov rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 16124954f17dSDmitry Safonov } 16134954f17dSDmitry Safonov 16144954f17dSDmitry Safonov if (cmd.set_current) 16154954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, key); 16164954f17dSDmitry Safonov if (cmd.set_rnext) 16174954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, key); 16184954f17dSDmitry Safonov return 0; 16194954f17dSDmitry Safonov 16204954f17dSDmitry Safonov err_free_sock: 16214954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 16224954f17dSDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 16234954f17dSDmitry Safonov kfree_sensitive(key); 16244954f17dSDmitry Safonov err_free_ao: 16254954f17dSDmitry Safonov if (first) 16264954f17dSDmitry Safonov kfree(ao_info); 16274954f17dSDmitry Safonov return ret; 16284954f17dSDmitry Safonov } 16294954f17dSDmitry Safonov 16304954f17dSDmitry Safonov static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, 16314954f17dSDmitry Safonov struct tcp_ao_key *key, 16324954f17dSDmitry Safonov struct tcp_ao_key *new_current, 16334954f17dSDmitry Safonov struct tcp_ao_key *new_rnext) 16344954f17dSDmitry Safonov { 16354954f17dSDmitry Safonov int err; 16364954f17dSDmitry Safonov 16374954f17dSDmitry Safonov hlist_del_rcu(&key->node); 16384954f17dSDmitry Safonov 16394954f17dSDmitry Safonov /* At this moment another CPU could have looked this key up 16404954f17dSDmitry Safonov * while it was unlinked from the list. Wait for RCU grace period, 16414954f17dSDmitry Safonov * after which the key is off-list and can't be looked up again; 16424954f17dSDmitry Safonov * the rx path [just before RCU came] might have used it and set it 16434954f17dSDmitry Safonov * as current_key (very unlikely). 16444954f17dSDmitry Safonov */ 16454954f17dSDmitry Safonov synchronize_rcu(); 16464954f17dSDmitry Safonov if (new_current) 16474954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, new_current); 16484954f17dSDmitry Safonov if (new_rnext) 16494954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, new_rnext); 16504954f17dSDmitry Safonov 16514954f17dSDmitry Safonov if (unlikely(READ_ONCE(ao_info->current_key) == key || 16524954f17dSDmitry Safonov READ_ONCE(ao_info->rnext_key) == key)) { 16534954f17dSDmitry Safonov err = -EBUSY; 16544954f17dSDmitry Safonov goto add_key; 16554954f17dSDmitry Safonov } 16564954f17dSDmitry Safonov 16574954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 16584954f17dSDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 16594954f17dSDmitry Safonov 16604954f17dSDmitry Safonov return 0; 16614954f17dSDmitry Safonov add_key: 16624954f17dSDmitry Safonov hlist_add_head_rcu(&key->node, &ao_info->head); 16634954f17dSDmitry Safonov return err; 16644954f17dSDmitry Safonov } 16654954f17dSDmitry Safonov 16664954f17dSDmitry Safonov static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, 16674954f17dSDmitry Safonov sockptr_t optval, int optlen) 16684954f17dSDmitry Safonov { 16694954f17dSDmitry Safonov struct tcp_ao_key *key, *new_current = NULL, *new_rnext = NULL; 16704954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 16714954f17dSDmitry Safonov union tcp_ao_addr *addr; 16724954f17dSDmitry Safonov struct tcp_ao_del cmd; 16734954f17dSDmitry Safonov int addr_len; 16744954f17dSDmitry Safonov __u8 prefix; 16754954f17dSDmitry Safonov u16 port; 16764954f17dSDmitry Safonov int err; 16774954f17dSDmitry Safonov 16784954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 16794954f17dSDmitry Safonov return -EINVAL; 16804954f17dSDmitry Safonov 16814954f17dSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 16824954f17dSDmitry Safonov if (err) 16834954f17dSDmitry Safonov return err; 16844954f17dSDmitry Safonov 16854954f17dSDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 16864954f17dSDmitry Safonov return -EINVAL; 16874954f17dSDmitry Safonov 16884954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 16894954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 16904954f17dSDmitry Safonov return -EINVAL; 16914954f17dSDmitry Safonov } 16924954f17dSDmitry Safonov 16934954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 16944954f17dSDmitry Safonov if (IS_ERR(ao_info)) 16954954f17dSDmitry Safonov return PTR_ERR(ao_info); 16964954f17dSDmitry Safonov if (!ao_info) 16974954f17dSDmitry Safonov return -ENOENT; 16984954f17dSDmitry Safonov 16994954f17dSDmitry Safonov /* For sockets in TCP_CLOSED it's possible set keys that aren't 17004954f17dSDmitry Safonov * matching the future peer (address/VRF/etc), 17014954f17dSDmitry Safonov * tcp_ao_connect_init() will choose a correct matching MKT 17024954f17dSDmitry Safonov * if there's any. 17034954f17dSDmitry Safonov */ 17044954f17dSDmitry Safonov if (cmd.set_current) { 17054954f17dSDmitry Safonov new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 17064954f17dSDmitry Safonov if (!new_current) 17074954f17dSDmitry Safonov return -ENOENT; 17084954f17dSDmitry Safonov } 17094954f17dSDmitry Safonov if (cmd.set_rnext) { 17104954f17dSDmitry Safonov new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 17114954f17dSDmitry Safonov if (!new_rnext) 17124954f17dSDmitry Safonov return -ENOENT; 17134954f17dSDmitry Safonov } 17144954f17dSDmitry Safonov 17154954f17dSDmitry Safonov if (family == AF_INET) { 17164954f17dSDmitry Safonov struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.addr; 17174954f17dSDmitry Safonov 17184954f17dSDmitry Safonov addr = (union tcp_ao_addr *)&sin->sin_addr; 17194954f17dSDmitry Safonov addr_len = sizeof(struct in_addr); 17204954f17dSDmitry Safonov port = ntohs(sin->sin_port); 17214954f17dSDmitry Safonov } else { 17224954f17dSDmitry Safonov struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.addr; 17234954f17dSDmitry Safonov struct in6_addr *addr6 = &sin6->sin6_addr; 17244954f17dSDmitry Safonov 17254954f17dSDmitry Safonov if (ipv6_addr_v4mapped(addr6)) { 17264954f17dSDmitry Safonov addr = (union tcp_ao_addr *)&addr6->s6_addr32[3]; 17274954f17dSDmitry Safonov addr_len = sizeof(struct in_addr); 17284954f17dSDmitry Safonov family = AF_INET; 17294954f17dSDmitry Safonov } else { 17304954f17dSDmitry Safonov addr = (union tcp_ao_addr *)addr6; 17314954f17dSDmitry Safonov addr_len = sizeof(struct in6_addr); 17324954f17dSDmitry Safonov } 17334954f17dSDmitry Safonov port = ntohs(sin6->sin6_port); 17344954f17dSDmitry Safonov } 17354954f17dSDmitry Safonov prefix = cmd.prefix; 17364954f17dSDmitry Safonov 17374954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 17384954f17dSDmitry Safonov if (port != 0) 17394954f17dSDmitry Safonov return -EINVAL; 17404954f17dSDmitry Safonov 17414954f17dSDmitry Safonov /* We could choose random present key here for current/rnext 17424954f17dSDmitry Safonov * but that's less predictable. Let's be strict and don't 17434954f17dSDmitry Safonov * allow removing a key that's in use. RFC5925 doesn't 17444954f17dSDmitry Safonov * specify how-to coordinate key removal, but says: 17454954f17dSDmitry Safonov * "It is presumed that an MKT affecting a particular 17464954f17dSDmitry Safonov * connection cannot be destroyed during an active connection" 17474954f17dSDmitry Safonov */ 17484954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 17494954f17dSDmitry Safonov if (cmd.sndid != key->sndid || 17504954f17dSDmitry Safonov cmd.rcvid != key->rcvid) 17514954f17dSDmitry Safonov continue; 17524954f17dSDmitry Safonov 17534954f17dSDmitry Safonov if (family != key->family || 17544954f17dSDmitry Safonov prefix != key->prefixlen || 17554954f17dSDmitry Safonov memcmp(addr, &key->addr, addr_len)) 17564954f17dSDmitry Safonov continue; 17574954f17dSDmitry Safonov 17584954f17dSDmitry Safonov if (key == new_current || key == new_rnext) 17594954f17dSDmitry Safonov continue; 17604954f17dSDmitry Safonov 17614954f17dSDmitry Safonov return tcp_ao_delete_key(sk, ao_info, key, 17624954f17dSDmitry Safonov new_current, new_rnext); 17634954f17dSDmitry Safonov } 17644954f17dSDmitry Safonov return -ENOENT; 17654954f17dSDmitry Safonov } 17664954f17dSDmitry Safonov 17670aadc739SDmitry Safonov /* cmd.ao_required makes a socket TCP-AO only. 17680aadc739SDmitry Safonov * Don't allow any md5 keys for any l3intf on the socket together with it. 17690aadc739SDmitry Safonov * Restricting it early in setsockopt() removes a check for 17700aadc739SDmitry Safonov * ao_info->ao_required on inbound tcp segment fast-path. 17710aadc739SDmitry Safonov */ 17720aadc739SDmitry Safonov static int tcp_ao_required_verify(struct sock *sk) 17730aadc739SDmitry Safonov { 17740aadc739SDmitry Safonov #ifdef CONFIG_TCP_MD5SIG 17750aadc739SDmitry Safonov const struct tcp_md5sig_info *md5sig; 17760aadc739SDmitry Safonov 17770aadc739SDmitry Safonov if (!static_branch_unlikely(&tcp_md5_needed.key)) 17780aadc739SDmitry Safonov return 0; 17790aadc739SDmitry Safonov 17800aadc739SDmitry Safonov md5sig = rcu_dereference_check(tcp_sk(sk)->md5sig_info, 17810aadc739SDmitry Safonov lockdep_sock_is_held(sk)); 17820aadc739SDmitry Safonov if (!md5sig) 17830aadc739SDmitry Safonov return 0; 17840aadc739SDmitry Safonov 17850aadc739SDmitry Safonov if (rcu_dereference_check(hlist_first_rcu(&md5sig->head), 17860aadc739SDmitry Safonov lockdep_sock_is_held(sk))) 17870aadc739SDmitry Safonov return 1; 17880aadc739SDmitry Safonov #endif 17890aadc739SDmitry Safonov return 0; 17900aadc739SDmitry Safonov } 17910aadc739SDmitry Safonov 17924954f17dSDmitry Safonov static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, 17934954f17dSDmitry Safonov sockptr_t optval, int optlen) 17944954f17dSDmitry Safonov { 17954954f17dSDmitry Safonov struct tcp_ao_key *new_current = NULL, *new_rnext = NULL; 17964954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 17974954f17dSDmitry Safonov struct tcp_ao_info_opt cmd; 17984954f17dSDmitry Safonov bool first = false; 17994954f17dSDmitry Safonov int err; 18004954f17dSDmitry Safonov 18014954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 18024954f17dSDmitry Safonov return -EINVAL; 18034954f17dSDmitry Safonov 18044954f17dSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 18054954f17dSDmitry Safonov if (err) 18064954f17dSDmitry Safonov return err; 18074954f17dSDmitry Safonov 18084954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 18094954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 18104954f17dSDmitry Safonov return -EINVAL; 18114954f17dSDmitry Safonov } 18124954f17dSDmitry Safonov 1813af09a341SDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 18144954f17dSDmitry Safonov return -EINVAL; 18154954f17dSDmitry Safonov 18164954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 18174954f17dSDmitry Safonov if (IS_ERR(ao_info)) 18184954f17dSDmitry Safonov return PTR_ERR(ao_info); 18194954f17dSDmitry Safonov if (!ao_info) { 18204954f17dSDmitry Safonov ao_info = tcp_ao_alloc_info(GFP_KERNEL); 18214954f17dSDmitry Safonov if (!ao_info) 18224954f17dSDmitry Safonov return -ENOMEM; 18234954f17dSDmitry Safonov first = true; 18244954f17dSDmitry Safonov } 18254954f17dSDmitry Safonov 18260aadc739SDmitry Safonov if (cmd.ao_required && tcp_ao_required_verify(sk)) 18270aadc739SDmitry Safonov return -EKEYREJECTED; 18280aadc739SDmitry Safonov 18294954f17dSDmitry Safonov /* For sockets in TCP_CLOSED it's possible set keys that aren't 18304954f17dSDmitry Safonov * matching the future peer (address/port/VRF/etc), 18314954f17dSDmitry Safonov * tcp_ao_connect_init() will choose a correct matching MKT 18324954f17dSDmitry Safonov * if there's any. 18334954f17dSDmitry Safonov */ 18344954f17dSDmitry Safonov if (cmd.set_current) { 18354954f17dSDmitry Safonov new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 18364954f17dSDmitry Safonov if (!new_current) { 18374954f17dSDmitry Safonov err = -ENOENT; 18384954f17dSDmitry Safonov goto out; 18394954f17dSDmitry Safonov } 18404954f17dSDmitry Safonov } 18414954f17dSDmitry Safonov if (cmd.set_rnext) { 18424954f17dSDmitry Safonov new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 18434954f17dSDmitry Safonov if (!new_rnext) { 18444954f17dSDmitry Safonov err = -ENOENT; 18454954f17dSDmitry Safonov goto out; 18464954f17dSDmitry Safonov } 18474954f17dSDmitry Safonov } 1848af09a341SDmitry Safonov if (cmd.set_counters) { 1849af09a341SDmitry Safonov atomic64_set(&ao_info->counters.pkt_good, cmd.pkt_good); 1850af09a341SDmitry Safonov atomic64_set(&ao_info->counters.pkt_bad, cmd.pkt_bad); 1851af09a341SDmitry Safonov atomic64_set(&ao_info->counters.key_not_found, cmd.pkt_key_not_found); 1852af09a341SDmitry Safonov atomic64_set(&ao_info->counters.ao_required, cmd.pkt_ao_required); 1853953af8e3SDmitry Safonov atomic64_set(&ao_info->counters.dropped_icmp, cmd.pkt_dropped_icmp); 1854af09a341SDmitry Safonov } 18554954f17dSDmitry Safonov 18564954f17dSDmitry Safonov ao_info->ao_required = cmd.ao_required; 1857953af8e3SDmitry Safonov ao_info->accept_icmps = cmd.accept_icmps; 18584954f17dSDmitry Safonov if (new_current) 18594954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, new_current); 18604954f17dSDmitry Safonov if (new_rnext) 18614954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, new_rnext); 18624954f17dSDmitry Safonov if (first) { 18634954f17dSDmitry Safonov sk_gso_disable(sk); 18644954f17dSDmitry Safonov rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 18654954f17dSDmitry Safonov } 18664954f17dSDmitry Safonov return 0; 18674954f17dSDmitry Safonov out: 18684954f17dSDmitry Safonov if (first) 18694954f17dSDmitry Safonov kfree(ao_info); 18704954f17dSDmitry Safonov return err; 18714954f17dSDmitry Safonov } 18724954f17dSDmitry Safonov 18734954f17dSDmitry Safonov int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, 18744954f17dSDmitry Safonov sockptr_t optval, int optlen) 18754954f17dSDmitry Safonov { 18764954f17dSDmitry Safonov if (WARN_ON_ONCE(family != AF_INET && family != AF_INET6)) 18774954f17dSDmitry Safonov return -EAFNOSUPPORT; 18784954f17dSDmitry Safonov 18794954f17dSDmitry Safonov switch (cmd) { 18804954f17dSDmitry Safonov case TCP_AO_ADD_KEY: 18814954f17dSDmitry Safonov return tcp_ao_add_cmd(sk, family, optval, optlen); 18824954f17dSDmitry Safonov case TCP_AO_DEL_KEY: 18834954f17dSDmitry Safonov return tcp_ao_del_cmd(sk, family, optval, optlen); 18844954f17dSDmitry Safonov case TCP_AO_INFO: 18854954f17dSDmitry Safonov return tcp_ao_info_cmd(sk, family, optval, optlen); 18864954f17dSDmitry Safonov default: 18874954f17dSDmitry Safonov WARN_ON_ONCE(1); 18884954f17dSDmitry Safonov return -EINVAL; 18894954f17dSDmitry Safonov } 18904954f17dSDmitry Safonov } 18914954f17dSDmitry Safonov 18924954f17dSDmitry Safonov int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) 18934954f17dSDmitry Safonov { 18944954f17dSDmitry Safonov return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen); 18954954f17dSDmitry Safonov } 18964954f17dSDmitry Safonov 1897*ef84703aSDmitry Safonov /* tcp_ao_copy_mkts_to_user(ao_info, optval, optlen) 1898*ef84703aSDmitry Safonov * 1899*ef84703aSDmitry Safonov * @ao_info: struct tcp_ao_info on the socket that 1900*ef84703aSDmitry Safonov * socket getsockopt(TCP_AO_GET_KEYS) is executed on 1901*ef84703aSDmitry Safonov * @optval: pointer to array of tcp_ao_getsockopt structures in user space. 1902*ef84703aSDmitry Safonov * Must be != NULL. 1903*ef84703aSDmitry Safonov * @optlen: pointer to size of tcp_ao_getsockopt structure. 1904*ef84703aSDmitry Safonov * Must be != NULL. 1905*ef84703aSDmitry Safonov * 1906*ef84703aSDmitry Safonov * Return value: 0 on success, a negative error number otherwise. 1907*ef84703aSDmitry Safonov * 1908*ef84703aSDmitry Safonov * optval points to an array of tcp_ao_getsockopt structures in user space. 1909*ef84703aSDmitry Safonov * optval[0] is used as both input and output to getsockopt. It determines 1910*ef84703aSDmitry Safonov * which keys are returned by the kernel. 1911*ef84703aSDmitry Safonov * optval[0].nkeys is the size of the array in user space. On return it contains 1912*ef84703aSDmitry Safonov * the number of keys matching the search criteria. 1913*ef84703aSDmitry Safonov * If tcp_ao_getsockopt::get_all is set, then all keys in the socket are 1914*ef84703aSDmitry Safonov * returned, otherwise only keys matching <addr, prefix, sndid, rcvid> 1915*ef84703aSDmitry Safonov * in optval[0] are returned. 1916*ef84703aSDmitry Safonov * optlen is also used as both input and output. The user provides the size 1917*ef84703aSDmitry Safonov * of struct tcp_ao_getsockopt in user space, and the kernel returns the size 1918*ef84703aSDmitry Safonov * of the structure in kernel space. 1919*ef84703aSDmitry Safonov * The size of struct tcp_ao_getsockopt may differ between user and kernel. 1920*ef84703aSDmitry Safonov * There are three cases to consider: 1921*ef84703aSDmitry Safonov * * If usize == ksize, then keys are copied verbatim. 1922*ef84703aSDmitry Safonov * * If usize < ksize, then the userspace has passed an old struct to a 1923*ef84703aSDmitry Safonov * newer kernel. The rest of the trailing bytes in optval[0] 1924*ef84703aSDmitry Safonov * (ksize - usize) are interpreted as 0 by the kernel. 1925*ef84703aSDmitry Safonov * * If usize > ksize, then the userspace has passed a new struct to an 1926*ef84703aSDmitry Safonov * older kernel. The trailing bytes unknown to the kernel (usize - ksize) 1927*ef84703aSDmitry Safonov * are checked to ensure they are zeroed, otherwise -E2BIG is returned. 1928*ef84703aSDmitry Safonov * On return the kernel fills in min(usize, ksize) in each entry of the array. 1929*ef84703aSDmitry Safonov * The layout of the fields in the user and kernel structures is expected to 1930*ef84703aSDmitry Safonov * be the same (including in the 32bit vs 64bit case). 1931*ef84703aSDmitry Safonov */ 1932*ef84703aSDmitry Safonov static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, 1933*ef84703aSDmitry Safonov sockptr_t optval, sockptr_t optlen) 1934*ef84703aSDmitry Safonov { 1935*ef84703aSDmitry Safonov struct tcp_ao_getsockopt opt_in, opt_out; 1936*ef84703aSDmitry Safonov struct tcp_ao_key *key, *current_key; 1937*ef84703aSDmitry Safonov bool do_address_matching = true; 1938*ef84703aSDmitry Safonov union tcp_ao_addr *addr = NULL; 1939*ef84703aSDmitry Safonov unsigned int max_keys; /* maximum number of keys to copy to user */ 1940*ef84703aSDmitry Safonov size_t out_offset = 0; 1941*ef84703aSDmitry Safonov size_t bytes_to_write; /* number of bytes to write to user level */ 1942*ef84703aSDmitry Safonov int err, user_len; 1943*ef84703aSDmitry Safonov u32 matched_keys; /* keys from ao_info matched so far */ 1944*ef84703aSDmitry Safonov int optlen_out; 1945*ef84703aSDmitry Safonov __be16 port = 0; 1946*ef84703aSDmitry Safonov 1947*ef84703aSDmitry Safonov if (copy_from_sockptr(&user_len, optlen, sizeof(int))) 1948*ef84703aSDmitry Safonov return -EFAULT; 1949*ef84703aSDmitry Safonov 1950*ef84703aSDmitry Safonov if (user_len <= 0) 1951*ef84703aSDmitry Safonov return -EINVAL; 1952*ef84703aSDmitry Safonov 1953*ef84703aSDmitry Safonov memset(&opt_in, 0, sizeof(struct tcp_ao_getsockopt)); 1954*ef84703aSDmitry Safonov err = copy_struct_from_sockptr(&opt_in, sizeof(opt_in), 1955*ef84703aSDmitry Safonov optval, user_len); 1956*ef84703aSDmitry Safonov if (err < 0) 1957*ef84703aSDmitry Safonov return err; 1958*ef84703aSDmitry Safonov 1959*ef84703aSDmitry Safonov if (opt_in.pkt_good || opt_in.pkt_bad) 1960*ef84703aSDmitry Safonov return -EINVAL; 1961*ef84703aSDmitry Safonov 1962*ef84703aSDmitry Safonov if (opt_in.reserved != 0) 1963*ef84703aSDmitry Safonov return -EINVAL; 1964*ef84703aSDmitry Safonov 1965*ef84703aSDmitry Safonov max_keys = opt_in.nkeys; 1966*ef84703aSDmitry Safonov 1967*ef84703aSDmitry Safonov if (opt_in.get_all || opt_in.is_current || opt_in.is_rnext) { 1968*ef84703aSDmitry Safonov if (opt_in.get_all && (opt_in.is_current || opt_in.is_rnext)) 1969*ef84703aSDmitry Safonov return -EINVAL; 1970*ef84703aSDmitry Safonov do_address_matching = false; 1971*ef84703aSDmitry Safonov } 1972*ef84703aSDmitry Safonov 1973*ef84703aSDmitry Safonov switch (opt_in.addr.ss_family) { 1974*ef84703aSDmitry Safonov case AF_INET: { 1975*ef84703aSDmitry Safonov struct sockaddr_in *sin; 1976*ef84703aSDmitry Safonov __be32 mask; 1977*ef84703aSDmitry Safonov 1978*ef84703aSDmitry Safonov sin = (struct sockaddr_in *)&opt_in.addr; 1979*ef84703aSDmitry Safonov port = sin->sin_port; 1980*ef84703aSDmitry Safonov addr = (union tcp_ao_addr *)&sin->sin_addr; 1981*ef84703aSDmitry Safonov 1982*ef84703aSDmitry Safonov if (opt_in.prefix > 32) 1983*ef84703aSDmitry Safonov return -EINVAL; 1984*ef84703aSDmitry Safonov 1985*ef84703aSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY && 1986*ef84703aSDmitry Safonov opt_in.prefix != 0) 1987*ef84703aSDmitry Safonov return -EINVAL; 1988*ef84703aSDmitry Safonov 1989*ef84703aSDmitry Safonov mask = inet_make_mask(opt_in.prefix); 1990*ef84703aSDmitry Safonov if (sin->sin_addr.s_addr & ~mask) 1991*ef84703aSDmitry Safonov return -EINVAL; 1992*ef84703aSDmitry Safonov 1993*ef84703aSDmitry Safonov break; 1994*ef84703aSDmitry Safonov } 1995*ef84703aSDmitry Safonov case AF_INET6: { 1996*ef84703aSDmitry Safonov struct sockaddr_in6 *sin6; 1997*ef84703aSDmitry Safonov struct in6_addr *addr6; 1998*ef84703aSDmitry Safonov 1999*ef84703aSDmitry Safonov sin6 = (struct sockaddr_in6 *)&opt_in.addr; 2000*ef84703aSDmitry Safonov addr = (union tcp_ao_addr *)&sin6->sin6_addr; 2001*ef84703aSDmitry Safonov addr6 = &sin6->sin6_addr; 2002*ef84703aSDmitry Safonov port = sin6->sin6_port; 2003*ef84703aSDmitry Safonov 2004*ef84703aSDmitry Safonov /* We don't have to change family and @addr here if 2005*ef84703aSDmitry Safonov * ipv6_addr_v4mapped() like in key adding: 2006*ef84703aSDmitry Safonov * tcp_ao_key_cmp() does it. Do the sanity checks though. 2007*ef84703aSDmitry Safonov */ 2008*ef84703aSDmitry Safonov if (opt_in.prefix != 0) { 2009*ef84703aSDmitry Safonov if (ipv6_addr_v4mapped(addr6)) { 2010*ef84703aSDmitry Safonov __be32 mask, addr4 = addr6->s6_addr32[3]; 2011*ef84703aSDmitry Safonov 2012*ef84703aSDmitry Safonov if (opt_in.prefix > 32 || 2013*ef84703aSDmitry Safonov ntohl(addr4) == INADDR_ANY) 2014*ef84703aSDmitry Safonov return -EINVAL; 2015*ef84703aSDmitry Safonov mask = inet_make_mask(opt_in.prefix); 2016*ef84703aSDmitry Safonov if (addr4 & ~mask) 2017*ef84703aSDmitry Safonov return -EINVAL; 2018*ef84703aSDmitry Safonov } else { 2019*ef84703aSDmitry Safonov struct in6_addr pfx; 2020*ef84703aSDmitry Safonov 2021*ef84703aSDmitry Safonov if (ipv6_addr_any(addr6) || 2022*ef84703aSDmitry Safonov opt_in.prefix > 128) 2023*ef84703aSDmitry Safonov return -EINVAL; 2024*ef84703aSDmitry Safonov 2025*ef84703aSDmitry Safonov ipv6_addr_prefix(&pfx, addr6, opt_in.prefix); 2026*ef84703aSDmitry Safonov if (ipv6_addr_cmp(&pfx, addr6)) 2027*ef84703aSDmitry Safonov return -EINVAL; 2028*ef84703aSDmitry Safonov } 2029*ef84703aSDmitry Safonov } else if (!ipv6_addr_any(addr6)) { 2030*ef84703aSDmitry Safonov return -EINVAL; 2031*ef84703aSDmitry Safonov } 2032*ef84703aSDmitry Safonov break; 2033*ef84703aSDmitry Safonov } 2034*ef84703aSDmitry Safonov case 0: 2035*ef84703aSDmitry Safonov if (!do_address_matching) 2036*ef84703aSDmitry Safonov break; 2037*ef84703aSDmitry Safonov fallthrough; 2038*ef84703aSDmitry Safonov default: 2039*ef84703aSDmitry Safonov return -EAFNOSUPPORT; 2040*ef84703aSDmitry Safonov } 2041*ef84703aSDmitry Safonov 2042*ef84703aSDmitry Safonov if (!do_address_matching) { 2043*ef84703aSDmitry Safonov /* We could just ignore those, but let's do stricter checks */ 2044*ef84703aSDmitry Safonov if (addr || port) 2045*ef84703aSDmitry Safonov return -EINVAL; 2046*ef84703aSDmitry Safonov if (opt_in.prefix || opt_in.sndid || opt_in.rcvid) 2047*ef84703aSDmitry Safonov return -EINVAL; 2048*ef84703aSDmitry Safonov } 2049*ef84703aSDmitry Safonov 2050*ef84703aSDmitry Safonov bytes_to_write = min_t(int, user_len, sizeof(struct tcp_ao_getsockopt)); 2051*ef84703aSDmitry Safonov matched_keys = 0; 2052*ef84703aSDmitry Safonov /* May change in RX, while we're dumping, pre-fetch it */ 2053*ef84703aSDmitry Safonov current_key = READ_ONCE(ao_info->current_key); 2054*ef84703aSDmitry Safonov 2055*ef84703aSDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 2056*ef84703aSDmitry Safonov if (opt_in.get_all) 2057*ef84703aSDmitry Safonov goto match; 2058*ef84703aSDmitry Safonov 2059*ef84703aSDmitry Safonov if (opt_in.is_current || opt_in.is_rnext) { 2060*ef84703aSDmitry Safonov if (opt_in.is_current && key == current_key) 2061*ef84703aSDmitry Safonov goto match; 2062*ef84703aSDmitry Safonov if (opt_in.is_rnext && key == ao_info->rnext_key) 2063*ef84703aSDmitry Safonov goto match; 2064*ef84703aSDmitry Safonov continue; 2065*ef84703aSDmitry Safonov } 2066*ef84703aSDmitry Safonov 2067*ef84703aSDmitry Safonov if (tcp_ao_key_cmp(key, addr, opt_in.prefix, 2068*ef84703aSDmitry Safonov opt_in.addr.ss_family, 2069*ef84703aSDmitry Safonov opt_in.sndid, opt_in.rcvid) != 0) 2070*ef84703aSDmitry Safonov continue; 2071*ef84703aSDmitry Safonov match: 2072*ef84703aSDmitry Safonov matched_keys++; 2073*ef84703aSDmitry Safonov if (matched_keys > max_keys) 2074*ef84703aSDmitry Safonov continue; 2075*ef84703aSDmitry Safonov 2076*ef84703aSDmitry Safonov memset(&opt_out, 0, sizeof(struct tcp_ao_getsockopt)); 2077*ef84703aSDmitry Safonov 2078*ef84703aSDmitry Safonov if (key->family == AF_INET) { 2079*ef84703aSDmitry Safonov struct sockaddr_in *sin_out = (struct sockaddr_in *)&opt_out.addr; 2080*ef84703aSDmitry Safonov 2081*ef84703aSDmitry Safonov sin_out->sin_family = key->family; 2082*ef84703aSDmitry Safonov sin_out->sin_port = 0; 2083*ef84703aSDmitry Safonov memcpy(&sin_out->sin_addr, &key->addr, sizeof(struct in_addr)); 2084*ef84703aSDmitry Safonov } else { 2085*ef84703aSDmitry Safonov struct sockaddr_in6 *sin6_out = (struct sockaddr_in6 *)&opt_out.addr; 2086*ef84703aSDmitry Safonov 2087*ef84703aSDmitry Safonov sin6_out->sin6_family = key->family; 2088*ef84703aSDmitry Safonov sin6_out->sin6_port = 0; 2089*ef84703aSDmitry Safonov memcpy(&sin6_out->sin6_addr, &key->addr, sizeof(struct in6_addr)); 2090*ef84703aSDmitry Safonov } 2091*ef84703aSDmitry Safonov opt_out.sndid = key->sndid; 2092*ef84703aSDmitry Safonov opt_out.rcvid = key->rcvid; 2093*ef84703aSDmitry Safonov opt_out.prefix = key->prefixlen; 2094*ef84703aSDmitry Safonov opt_out.keyflags = key->keyflags; 2095*ef84703aSDmitry Safonov opt_out.is_current = (key == current_key); 2096*ef84703aSDmitry Safonov opt_out.is_rnext = (key == ao_info->rnext_key); 2097*ef84703aSDmitry Safonov opt_out.nkeys = 0; 2098*ef84703aSDmitry Safonov opt_out.maclen = key->maclen; 2099*ef84703aSDmitry Safonov opt_out.keylen = key->keylen; 2100*ef84703aSDmitry Safonov opt_out.pkt_good = atomic64_read(&key->pkt_good); 2101*ef84703aSDmitry Safonov opt_out.pkt_bad = atomic64_read(&key->pkt_bad); 2102*ef84703aSDmitry Safonov memcpy(&opt_out.key, key->key, key->keylen); 2103*ef84703aSDmitry Safonov tcp_sigpool_algo(key->tcp_sigpool_id, opt_out.alg_name, 64); 2104*ef84703aSDmitry Safonov 2105*ef84703aSDmitry Safonov /* Copy key to user */ 2106*ef84703aSDmitry Safonov if (copy_to_sockptr_offset(optval, out_offset, 2107*ef84703aSDmitry Safonov &opt_out, bytes_to_write)) 2108*ef84703aSDmitry Safonov return -EFAULT; 2109*ef84703aSDmitry Safonov out_offset += user_len; 2110*ef84703aSDmitry Safonov } 2111*ef84703aSDmitry Safonov 2112*ef84703aSDmitry Safonov optlen_out = (int)sizeof(struct tcp_ao_getsockopt); 2113*ef84703aSDmitry Safonov if (copy_to_sockptr(optlen, &optlen_out, sizeof(int))) 2114*ef84703aSDmitry Safonov return -EFAULT; 2115*ef84703aSDmitry Safonov 2116*ef84703aSDmitry Safonov out_offset = offsetof(struct tcp_ao_getsockopt, nkeys); 2117*ef84703aSDmitry Safonov if (copy_to_sockptr_offset(optval, out_offset, 2118*ef84703aSDmitry Safonov &matched_keys, sizeof(u32))) 2119*ef84703aSDmitry Safonov return -EFAULT; 2120*ef84703aSDmitry Safonov 2121*ef84703aSDmitry Safonov return 0; 2122*ef84703aSDmitry Safonov } 2123*ef84703aSDmitry Safonov 2124*ef84703aSDmitry Safonov int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2125*ef84703aSDmitry Safonov { 2126*ef84703aSDmitry Safonov struct tcp_ao_info *ao_info; 2127*ef84703aSDmitry Safonov 2128*ef84703aSDmitry Safonov ao_info = setsockopt_ao_info(sk); 2129*ef84703aSDmitry Safonov if (IS_ERR(ao_info)) 2130*ef84703aSDmitry Safonov return PTR_ERR(ao_info); 2131*ef84703aSDmitry Safonov if (!ao_info) 2132*ef84703aSDmitry Safonov return -ENOENT; 2133*ef84703aSDmitry Safonov 2134*ef84703aSDmitry Safonov return tcp_ao_copy_mkts_to_user(ao_info, optval, optlen); 2135*ef84703aSDmitry Safonov } 2136*ef84703aSDmitry Safonov 2137*ef84703aSDmitry Safonov int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2138*ef84703aSDmitry Safonov { 2139*ef84703aSDmitry Safonov struct tcp_ao_info_opt out, in = {}; 2140*ef84703aSDmitry Safonov struct tcp_ao_key *current_key; 2141*ef84703aSDmitry Safonov struct tcp_ao_info *ao; 2142*ef84703aSDmitry Safonov int err, len; 2143*ef84703aSDmitry Safonov 2144*ef84703aSDmitry Safonov if (copy_from_sockptr(&len, optlen, sizeof(int))) 2145*ef84703aSDmitry Safonov return -EFAULT; 2146*ef84703aSDmitry Safonov 2147*ef84703aSDmitry Safonov if (len <= 0) 2148*ef84703aSDmitry Safonov return -EINVAL; 2149*ef84703aSDmitry Safonov 2150*ef84703aSDmitry Safonov /* Copying this "in" only to check ::reserved, ::reserved2, 2151*ef84703aSDmitry Safonov * that may be needed to extend (struct tcp_ao_info_opt) and 2152*ef84703aSDmitry Safonov * what getsockopt() provides in future. 2153*ef84703aSDmitry Safonov */ 2154*ef84703aSDmitry Safonov err = copy_struct_from_sockptr(&in, sizeof(in), optval, len); 2155*ef84703aSDmitry Safonov if (err) 2156*ef84703aSDmitry Safonov return err; 2157*ef84703aSDmitry Safonov 2158*ef84703aSDmitry Safonov if (in.reserved != 0 || in.reserved2 != 0) 2159*ef84703aSDmitry Safonov return -EINVAL; 2160*ef84703aSDmitry Safonov 2161*ef84703aSDmitry Safonov ao = setsockopt_ao_info(sk); 2162*ef84703aSDmitry Safonov if (IS_ERR(ao)) 2163*ef84703aSDmitry Safonov return PTR_ERR(ao); 2164*ef84703aSDmitry Safonov if (!ao) 2165*ef84703aSDmitry Safonov return -ENOENT; 2166*ef84703aSDmitry Safonov 2167*ef84703aSDmitry Safonov memset(&out, 0, sizeof(out)); 2168*ef84703aSDmitry Safonov out.ao_required = ao->ao_required; 2169*ef84703aSDmitry Safonov out.accept_icmps = ao->accept_icmps; 2170*ef84703aSDmitry Safonov out.pkt_good = atomic64_read(&ao->counters.pkt_good); 2171*ef84703aSDmitry Safonov out.pkt_bad = atomic64_read(&ao->counters.pkt_bad); 2172*ef84703aSDmitry Safonov out.pkt_key_not_found = atomic64_read(&ao->counters.key_not_found); 2173*ef84703aSDmitry Safonov out.pkt_ao_required = atomic64_read(&ao->counters.ao_required); 2174*ef84703aSDmitry Safonov out.pkt_dropped_icmp = atomic64_read(&ao->counters.dropped_icmp); 2175*ef84703aSDmitry Safonov 2176*ef84703aSDmitry Safonov current_key = READ_ONCE(ao->current_key); 2177*ef84703aSDmitry Safonov if (current_key) { 2178*ef84703aSDmitry Safonov out.set_current = 1; 2179*ef84703aSDmitry Safonov out.current_key = current_key->sndid; 2180*ef84703aSDmitry Safonov } 2181*ef84703aSDmitry Safonov if (ao->rnext_key) { 2182*ef84703aSDmitry Safonov out.set_rnext = 1; 2183*ef84703aSDmitry Safonov out.rnext = ao->rnext_key->rcvid; 2184*ef84703aSDmitry Safonov } 2185*ef84703aSDmitry Safonov 2186*ef84703aSDmitry Safonov if (copy_to_sockptr(optval, &out, min_t(int, len, sizeof(out)))) 2187*ef84703aSDmitry Safonov return -EFAULT; 2188*ef84703aSDmitry Safonov 2189*ef84703aSDmitry Safonov return 0; 2190*ef84703aSDmitry Safonov } 2191*ef84703aSDmitry Safonov 2192