Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net/tcp: Introduce TCP_AO setsockopt()s

Add 3 setsockopt()s:
1. TCP_AO_ADD_KEY to add a new Master Key Tuple (MKT) on a socket
2. TCP_AO_DEL_KEY to delete present MKT from a socket
3. TCP_AO_INFO to change flags, Current_key/RNext_key on a TCP-AO sk

Userspace has to introduce keys on every socket it wants to use TCP-AO
option on, similarly to TCP_MD5SIG/TCP_MD5SIG_EXT.
RFC5925 prohibits definition of MKTs that would match the same peer,
so do sanity checks on the data provided by userspace. Be as
conservative as possible, including refusal of defining MKT on
an established connection with no AO, removing the key in-use and etc.

(1) and (2) are to be used by userspace key manager to add/remove keys.
(3) main purpose is to set RNext_key, which (as prescribed by RFC5925)
is the KeyID that will be requested in TCP-AO header from the peer to
sign their segments with.

At this moment the life of ao_info ends in tcp_v4_destroy_sock().

Co-developed-by: Francesco Ruggeri <fruggeri@arista.com>
Signed-off-by: Francesco Ruggeri <fruggeri@arista.com>
Co-developed-by: Salam Noureddine <noureddine@arista.com>
Signed-off-by: Salam Noureddine <noureddine@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
Acked-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Dmitry Safonov and committed by
David S. Miller
4954f17d c845f5f3

+952 -18
+23
include/linux/sockptr.h
··· 55 55 return copy_from_sockptr_offset(dst, src, 0, size); 56 56 } 57 57 58 + static inline int copy_struct_from_sockptr(void *dst, size_t ksize, 59 + sockptr_t src, size_t usize) 60 + { 61 + size_t size = min(ksize, usize); 62 + size_t rest = max(ksize, usize) - size; 63 + 64 + if (!sockptr_is_kernel(src)) 65 + return copy_struct_from_user(dst, ksize, src.user, size); 66 + 67 + if (usize < ksize) { 68 + memset(dst + size, 0, rest); 69 + } else if (usize > ksize) { 70 + char *p = src.kernel; 71 + 72 + while (rest--) { 73 + if (*p++) 74 + return -E2BIG; 75 + } 76 + } 77 + memcpy(dst, src.kernel, size); 78 + return 0; 79 + } 80 + 58 81 static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset, 59 82 const void *src, size_t size) 60 83 {
+3
include/net/tcp.h
··· 2175 2175 sockptr_t optval, 2176 2176 int optlen); 2177 2177 #endif 2178 + #ifdef CONFIG_TCP_AO 2179 + int (*ao_parse)(struct sock *sk, int optname, sockptr_t optval, int optlen); 2180 + #endif 2178 2181 }; 2179 2182 2180 2183 struct tcp_request_sock_ops {
+16 -1
include/net/tcp_ao.h
··· 81 81 */ 82 82 struct tcp_ao_key *current_key; 83 83 struct tcp_ao_key *rnext_key; 84 - u32 flags; 84 + u32 ao_required :1, 85 + __unused :31; 85 86 __be32 lisn; 86 87 __be32 risn; 87 88 struct rcu_head rcu; 88 89 }; 90 + 91 + #ifdef CONFIG_TCP_AO 92 + int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, 93 + sockptr_t optval, int optlen); 94 + void tcp_ao_destroy_sock(struct sock *sk); 95 + /* ipv4 specific functions */ 96 + int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen); 97 + /* ipv6 specific functions */ 98 + int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen); 99 + #else 100 + static inline void tcp_ao_destroy_sock(struct sock *sk) 101 + { 102 + } 103 + #endif 89 104 90 105 #endif /* _TCP_AO_H */
+46
include/uapi/linux/tcp.h
··· 129 129 130 130 #define TCP_TX_DELAY 37 /* delay outgoing packets by XX usec */ 131 131 132 + #define TCP_AO_ADD_KEY 38 /* Add/Set MKT */ 133 + #define TCP_AO_DEL_KEY 39 /* Delete MKT */ 134 + #define TCP_AO_INFO 40 /* Modify TCP-AO per-socket options */ 132 135 133 136 #define TCP_REPAIR_ON 1 134 137 #define TCP_REPAIR_OFF 0 ··· 365 362 }; 366 363 367 364 #define TCP_AO_MAXKEYLEN 80 365 + 366 + #define TCP_AO_KEYF_IFINDEX (1 << 0) /* L3 ifindex for VRF */ 367 + 368 + struct tcp_ao_add { /* setsockopt(TCP_AO_ADD_KEY) */ 369 + struct __kernel_sockaddr_storage addr; /* peer's address for the key */ 370 + char alg_name[64]; /* crypto hash algorithm to use */ 371 + __s32 ifindex; /* L3 dev index for VRF */ 372 + __u32 set_current :1, /* set key as Current_key at once */ 373 + set_rnext :1, /* request it from peer with RNext_key */ 374 + reserved :30; /* must be 0 */ 375 + __u16 reserved2; /* padding, must be 0 */ 376 + __u8 prefix; /* peer's address prefix */ 377 + __u8 sndid; /* SendID for outgoing segments */ 378 + __u8 rcvid; /* RecvID to match for incoming seg */ 379 + __u8 maclen; /* length of authentication code (hash) */ 380 + __u8 keyflags; /* see TCP_AO_KEYF_ */ 381 + __u8 keylen; /* length of ::key */ 382 + __u8 key[TCP_AO_MAXKEYLEN]; 383 + } __attribute__((aligned(8))); 384 + 385 + struct tcp_ao_del { /* setsockopt(TCP_AO_DEL_KEY) */ 386 + struct __kernel_sockaddr_storage addr; /* peer's address for the key */ 387 + __s32 ifindex; /* L3 dev index for VRF */ 388 + __u32 set_current :1, /* corresponding ::current_key */ 389 + set_rnext :1, /* corresponding ::rnext */ 390 + reserved :30; /* must be 0 */ 391 + __u16 reserved2; /* padding, must be 0 */ 392 + __u8 prefix; /* peer's address prefix */ 393 + __u8 sndid; /* SendID for outgoing segments */ 394 + __u8 rcvid; /* RecvID to match for incoming seg */ 395 + __u8 current_key; /* KeyID to set as Current_key */ 396 + __u8 rnext; /* KeyID to set as Rnext_key */ 397 + __u8 keyflags; /* see TCP_AO_KEYF_ */ 398 + } __attribute__((aligned(8))); 399 + 400 + struct tcp_ao_info_opt { /* setsockopt(TCP_AO_INFO) */ 401 + __u32 set_current :1, /* corresponding ::current_key */ 402 + set_rnext :1, /* corresponding ::rnext */ 403 + ao_required :1, /* don't accept non-AO connects */ 404 + reserved :29; /* must be 0 */ 405 + __u8 current_key; /* KeyID to set as Current_key */ 406 + __u8 rnext; /* KeyID to set as Rnext_key */ 407 + } __attribute__((aligned(8))); 368 408 369 409 /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ 370 410
+1
net/ipv4/Makefile
··· 69 69 70 70 obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ 71 71 xfrm4_output.o xfrm4_protocol.o 72 + obj-$(CONFIG_TCP_AO) += tcp_ao.o 72 73 73 74 ifeq ($(CONFIG_BPF_JIT),y) 74 75 obj-$(CONFIG_BPF_SYSCALL) += bpf_tcp_ca.o
+17
net/ipv4/tcp.c
··· 3593 3593 __tcp_sock_set_quickack(sk, val); 3594 3594 break; 3595 3595 3596 + #ifdef CONFIG_TCP_AO 3597 + case TCP_AO_ADD_KEY: 3598 + case TCP_AO_DEL_KEY: 3599 + case TCP_AO_INFO: { 3600 + /* If this is the first TCP-AO setsockopt() on the socket, 3601 + * sk_state has to be LISTEN or CLOSE 3602 + */ 3603 + if (((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) || 3604 + rcu_dereference_protected(tcp_sk(sk)->ao_info, 3605 + lockdep_sock_is_held(sk))) 3606 + err = tp->af_specific->ao_parse(sk, optname, optval, 3607 + optlen); 3608 + else 3609 + err = -EISCONN; 3610 + break; 3611 + } 3612 + #endif 3596 3613 #ifdef CONFIG_TCP_MD5SIG 3597 3614 case TCP_MD5SIG: 3598 3615 case TCP_MD5SIG_EXT:
+794
net/ipv4/tcp_ao.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * INET An implementation of the TCP Authentication Option (TCP-AO). 4 + * See RFC5925. 5 + * 6 + * Authors: Dmitry Safonov <dima@arista.com> 7 + * Francesco Ruggeri <fruggeri@arista.com> 8 + * Salam Noureddine <noureddine@arista.com> 9 + */ 10 + #define pr_fmt(fmt) "TCP: " fmt 11 + 12 + #include <crypto/hash.h> 13 + #include <linux/inetdevice.h> 14 + #include <linux/tcp.h> 15 + 16 + #include <net/tcp.h> 17 + #include <net/ipv6.h> 18 + 19 + /* Optimized version of tcp_ao_do_lookup(): only for sockets for which 20 + * it's known that the keys in ao_info are matching peer's 21 + * family/address/VRF/etc. 22 + */ 23 + static struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao, 24 + int sndid, int rcvid) 25 + { 26 + struct tcp_ao_key *key; 27 + 28 + hlist_for_each_entry_rcu(key, &ao->head, node) { 29 + if ((sndid >= 0 && key->sndid != sndid) || 30 + (rcvid >= 0 && key->rcvid != rcvid)) 31 + continue; 32 + return key; 33 + } 34 + 35 + return NULL; 36 + } 37 + 38 + static int ipv4_prefix_cmp(const struct in_addr *addr1, 39 + const struct in_addr *addr2, 40 + unsigned int prefixlen) 41 + { 42 + __be32 mask = inet_make_mask(prefixlen); 43 + __be32 a1 = addr1->s_addr & mask; 44 + __be32 a2 = addr2->s_addr & mask; 45 + 46 + if (a1 == a2) 47 + return 0; 48 + return memcmp(&a1, &a2, sizeof(a1)); 49 + } 50 + 51 + static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, 52 + const union tcp_ao_addr *addr, u8 prefixlen, 53 + int family, int sndid, int rcvid) 54 + { 55 + if (sndid >= 0 && key->sndid != sndid) 56 + return (key->sndid > sndid) ? 1 : -1; 57 + if (rcvid >= 0 && key->rcvid != rcvid) 58 + return (key->rcvid > rcvid) ? 1 : -1; 59 + 60 + if (family == AF_UNSPEC) 61 + return 0; 62 + if (key->family != family) 63 + return (key->family > family) ? 1 : -1; 64 + 65 + if (family == AF_INET) { 66 + if (ntohl(key->addr.a4.s_addr) == INADDR_ANY) 67 + return 0; 68 + if (ntohl(addr->a4.s_addr) == INADDR_ANY) 69 + return 0; 70 + return ipv4_prefix_cmp(&key->addr.a4, &addr->a4, prefixlen); 71 + #if IS_ENABLED(CONFIG_IPV6) 72 + } else { 73 + if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6)) 74 + return 0; 75 + if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen)) 76 + return 0; 77 + return memcmp(&key->addr.a6, &addr->a6, sizeof(addr->a6)); 78 + #endif 79 + } 80 + return -1; 81 + } 82 + 83 + static int tcp_ao_key_cmp(const struct tcp_ao_key *key, 84 + const union tcp_ao_addr *addr, u8 prefixlen, 85 + int family, int sndid, int rcvid) 86 + { 87 + #if IS_ENABLED(CONFIG_IPV6) 88 + if (family == AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) { 89 + __be32 addr4 = addr->a6.s6_addr32[3]; 90 + 91 + return __tcp_ao_key_cmp(key, (union tcp_ao_addr *)&addr4, 92 + prefixlen, AF_INET, sndid, rcvid); 93 + } 94 + #endif 95 + return __tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid); 96 + } 97 + 98 + static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk, 99 + const union tcp_ao_addr *addr, int family, u8 prefix, 100 + int sndid, int rcvid) 101 + { 102 + struct tcp_ao_key *key; 103 + struct tcp_ao_info *ao; 104 + 105 + ao = rcu_dereference_check(tcp_sk(sk)->ao_info, 106 + lockdep_sock_is_held(sk)); 107 + if (!ao) 108 + return NULL; 109 + 110 + hlist_for_each_entry_rcu(key, &ao->head, node) { 111 + u8 prefixlen = min(prefix, key->prefixlen); 112 + 113 + if (!tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid)) 114 + return key; 115 + } 116 + return NULL; 117 + } 118 + 119 + static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags) 120 + { 121 + struct tcp_ao_info *ao; 122 + 123 + ao = kzalloc(sizeof(*ao), flags); 124 + if (!ao) 125 + return NULL; 126 + INIT_HLIST_HEAD(&ao->head); 127 + 128 + return ao; 129 + } 130 + 131 + static void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt) 132 + { 133 + hlist_add_head_rcu(&mkt->node, &ao->head); 134 + } 135 + 136 + static void tcp_ao_key_free_rcu(struct rcu_head *head) 137 + { 138 + struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu); 139 + 140 + tcp_sigpool_release(key->tcp_sigpool_id); 141 + kfree_sensitive(key); 142 + } 143 + 144 + void tcp_ao_destroy_sock(struct sock *sk) 145 + { 146 + struct tcp_ao_info *ao; 147 + struct tcp_ao_key *key; 148 + struct hlist_node *n; 149 + 150 + ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1); 151 + tcp_sk(sk)->ao_info = NULL; 152 + 153 + if (!ao) 154 + return; 155 + 156 + hlist_for_each_entry_safe(key, n, &ao->head, node) { 157 + hlist_del_rcu(&key->node); 158 + atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 159 + call_rcu(&key->rcu, tcp_ao_key_free_rcu); 160 + } 161 + 162 + kfree_rcu(ao, rcu); 163 + } 164 + 165 + static bool tcp_ao_can_set_current_rnext(struct sock *sk) 166 + { 167 + /* There aren't current/rnext keys on TCP_LISTEN sockets */ 168 + if (sk->sk_state == TCP_LISTEN) 169 + return false; 170 + return true; 171 + } 172 + 173 + static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao_add *cmd, 174 + union tcp_ao_addr **addr) 175 + { 176 + struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->addr; 177 + struct inet_sock *inet = inet_sk(sk); 178 + 179 + if (sin->sin_family != AF_INET) 180 + return -EINVAL; 181 + 182 + /* Currently matching is not performed on port (or port ranges) */ 183 + if (sin->sin_port != 0) 184 + return -EINVAL; 185 + 186 + /* Check prefix and trailing 0's in addr */ 187 + if (cmd->prefix != 0) { 188 + __be32 mask; 189 + 190 + if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY) 191 + return -EINVAL; 192 + if (cmd->prefix > 32) 193 + return -EINVAL; 194 + 195 + mask = inet_make_mask(cmd->prefix); 196 + if (sin->sin_addr.s_addr & ~mask) 197 + return -EINVAL; 198 + 199 + /* Check that MKT address is consistent with socket */ 200 + if (ntohl(inet->inet_daddr) != INADDR_ANY && 201 + (inet->inet_daddr & mask) != sin->sin_addr.s_addr) 202 + return -EINVAL; 203 + } else { 204 + if (ntohl(sin->sin_addr.s_addr) != INADDR_ANY) 205 + return -EINVAL; 206 + } 207 + 208 + *addr = (union tcp_ao_addr *)&sin->sin_addr; 209 + return 0; 210 + } 211 + 212 + static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key) 213 + { 214 + unsigned int syn_tcp_option_space; 215 + bool is_kdf_aes_128_cmac = false; 216 + struct crypto_ahash *tfm; 217 + struct tcp_sigpool hp; 218 + void *tmp_key = NULL; 219 + int err; 220 + 221 + /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 222 + if (!strcmp("cmac(aes128)", cmd->alg_name)) { 223 + strscpy(cmd->alg_name, "cmac(aes)", sizeof(cmd->alg_name)); 224 + is_kdf_aes_128_cmac = (cmd->keylen != 16); 225 + tmp_key = kmalloc(cmd->keylen, GFP_KERNEL); 226 + if (!tmp_key) 227 + return -ENOMEM; 228 + } 229 + 230 + key->maclen = cmd->maclen ?: 12; /* 12 is the default in RFC5925 */ 231 + 232 + /* Check: maclen + tcp-ao header <= (MAX_TCP_OPTION_SPACE - mss 233 + * - tstamp - wscale - sackperm), 234 + * see tcp_syn_options(), tcp_synack_options(), commit 33ad798c924b. 235 + * 236 + * In order to allow D-SACK with TCP-AO, the header size should be: 237 + * (MAX_TCP_OPTION_SPACE - TCPOLEN_TSTAMP_ALIGNED 238 + * - TCPOLEN_SACK_BASE_ALIGNED 239 + * - 2 * TCPOLEN_SACK_PERBLOCK) = 8 (maclen = 4), 240 + * see tcp_established_options(). 241 + * 242 + * RFC5925, 2.2: 243 + * Typical MACs are 96-128 bits (12-16 bytes), but any length 244 + * that fits in the header of the segment being authenticated 245 + * is allowed. 246 + * 247 + * RFC5925, 7.6: 248 + * TCP-AO continues to consume 16 bytes in non-SYN segments, 249 + * leaving a total of 24 bytes for other options, of which 250 + * the timestamp consumes 10. This leaves 14 bytes, of which 10 251 + * are used for a single SACK block. When two SACK blocks are used, 252 + * such as to handle D-SACK, a smaller TCP-AO MAC would be required 253 + * to make room for the additional SACK block (i.e., to leave 18 254 + * bytes for the D-SACK variant of the SACK option) [RFC2883]. 255 + * Note that D-SACK is not supportable in TCP MD5 in the presence 256 + * of timestamps, because TCP MD5’s MAC length is fixed and too 257 + * large to leave sufficient option space. 258 + */ 259 + syn_tcp_option_space = MAX_TCP_OPTION_SPACE; 260 + syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED; 261 + syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED; 262 + syn_tcp_option_space -= TCPOLEN_SACKPERM_ALIGNED; 263 + if (tcp_ao_len(key) > syn_tcp_option_space) { 264 + err = -EMSGSIZE; 265 + goto err_kfree; 266 + } 267 + 268 + key->keylen = cmd->keylen; 269 + memcpy(key->key, cmd->key, cmd->keylen); 270 + 271 + err = tcp_sigpool_start(key->tcp_sigpool_id, &hp); 272 + if (err) 273 + goto err_kfree; 274 + 275 + tfm = crypto_ahash_reqtfm(hp.req); 276 + if (is_kdf_aes_128_cmac) { 277 + void *scratch = hp.scratch; 278 + struct scatterlist sg; 279 + 280 + memcpy(tmp_key, cmd->key, cmd->keylen); 281 + sg_init_one(&sg, tmp_key, cmd->keylen); 282 + 283 + /* Using zero-key of 16 bytes as described in RFC5926 */ 284 + memset(scratch, 0, 16); 285 + err = crypto_ahash_setkey(tfm, scratch, 16); 286 + if (err) 287 + goto err_pool_end; 288 + 289 + err = crypto_ahash_init(hp.req); 290 + if (err) 291 + goto err_pool_end; 292 + 293 + ahash_request_set_crypt(hp.req, &sg, key->key, cmd->keylen); 294 + err = crypto_ahash_update(hp.req); 295 + if (err) 296 + goto err_pool_end; 297 + 298 + err |= crypto_ahash_final(hp.req); 299 + if (err) 300 + goto err_pool_end; 301 + key->keylen = 16; 302 + } 303 + 304 + err = crypto_ahash_setkey(tfm, key->key, key->keylen); 305 + if (err) 306 + goto err_pool_end; 307 + 308 + tcp_sigpool_end(&hp); 309 + kfree_sensitive(tmp_key); 310 + 311 + if (tcp_ao_maclen(key) > key->digest_size) 312 + return -EINVAL; 313 + 314 + return 0; 315 + 316 + err_pool_end: 317 + tcp_sigpool_end(&hp); 318 + err_kfree: 319 + kfree_sensitive(tmp_key); 320 + return err; 321 + } 322 + 323 + #if IS_ENABLED(CONFIG_IPV6) 324 + static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 325 + union tcp_ao_addr **paddr, 326 + unsigned short int *family) 327 + { 328 + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->addr; 329 + struct in6_addr *addr = &sin6->sin6_addr; 330 + u8 prefix = cmd->prefix; 331 + 332 + if (sin6->sin6_family != AF_INET6) 333 + return -EINVAL; 334 + 335 + /* Currently matching is not performed on port (or port ranges) */ 336 + if (sin6->sin6_port != 0) 337 + return -EINVAL; 338 + 339 + /* Check prefix and trailing 0's in addr */ 340 + if (cmd->prefix != 0 && ipv6_addr_v4mapped(addr)) { 341 + __be32 addr4 = addr->s6_addr32[3]; 342 + __be32 mask; 343 + 344 + if (prefix > 32 || ntohl(addr4) == INADDR_ANY) 345 + return -EINVAL; 346 + 347 + mask = inet_make_mask(prefix); 348 + if (addr4 & ~mask) 349 + return -EINVAL; 350 + 351 + /* Check that MKT address is consistent with socket */ 352 + if (!ipv6_addr_any(&sk->sk_v6_daddr)) { 353 + __be32 daddr4 = sk->sk_v6_daddr.s6_addr32[3]; 354 + 355 + if (!ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 356 + return -EINVAL; 357 + if ((daddr4 & mask) != addr4) 358 + return -EINVAL; 359 + } 360 + 361 + *paddr = (union tcp_ao_addr *)&addr->s6_addr32[3]; 362 + *family = AF_INET; 363 + return 0; 364 + } else if (cmd->prefix != 0) { 365 + struct in6_addr pfx; 366 + 367 + if (ipv6_addr_any(addr) || prefix > 128) 368 + return -EINVAL; 369 + 370 + ipv6_addr_prefix(&pfx, addr, prefix); 371 + if (ipv6_addr_cmp(&pfx, addr)) 372 + return -EINVAL; 373 + 374 + /* Check that MKT address is consistent with socket */ 375 + if (!ipv6_addr_any(&sk->sk_v6_daddr) && 376 + !ipv6_prefix_equal(&sk->sk_v6_daddr, addr, prefix)) 377 + 378 + return -EINVAL; 379 + } else { 380 + if (!ipv6_addr_any(addr)) 381 + return -EINVAL; 382 + } 383 + 384 + *paddr = (union tcp_ao_addr *)addr; 385 + return 0; 386 + } 387 + #else 388 + static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 389 + union tcp_ao_addr **paddr, 390 + unsigned short int *family) 391 + { 392 + return -EOPNOTSUPP; 393 + } 394 + #endif 395 + 396 + static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) 397 + { 398 + if (sk_fullsock(sk)) { 399 + return rcu_dereference_protected(tcp_sk(sk)->ao_info, 400 + lockdep_sock_is_held(sk)); 401 + } 402 + return ERR_PTR(-ESOCKTNOSUPPORT); 403 + } 404 + 405 + #define TCP_AO_KEYF_ALL (0) 406 + 407 + static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk, 408 + struct tcp_ao_add *cmd) 409 + { 410 + const char *algo = cmd->alg_name; 411 + unsigned int digest_size; 412 + struct crypto_ahash *tfm; 413 + struct tcp_ao_key *key; 414 + struct tcp_sigpool hp; 415 + int err, pool_id; 416 + size_t size; 417 + 418 + /* Force null-termination of alg_name */ 419 + cmd->alg_name[ARRAY_SIZE(cmd->alg_name) - 1] = '\0'; 420 + 421 + /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 422 + if (!strcmp("cmac(aes128)", algo)) 423 + algo = "cmac(aes)"; 424 + 425 + /* Full TCP header (th->doff << 2) should fit into scratch area, 426 + * see tcp_ao_hash_header(). 427 + */ 428 + pool_id = tcp_sigpool_alloc_ahash(algo, 60); 429 + if (pool_id < 0) 430 + return ERR_PTR(pool_id); 431 + 432 + err = tcp_sigpool_start(pool_id, &hp); 433 + if (err) 434 + goto err_free_pool; 435 + 436 + tfm = crypto_ahash_reqtfm(hp.req); 437 + if (crypto_ahash_alignmask(tfm) > TCP_AO_KEY_ALIGN) { 438 + err = -EOPNOTSUPP; 439 + goto err_pool_end; 440 + } 441 + digest_size = crypto_ahash_digestsize(tfm); 442 + tcp_sigpool_end(&hp); 443 + 444 + size = sizeof(struct tcp_ao_key) + (digest_size << 1); 445 + key = sock_kmalloc(sk, size, GFP_KERNEL); 446 + if (!key) { 447 + err = -ENOMEM; 448 + goto err_free_pool; 449 + } 450 + 451 + key->tcp_sigpool_id = pool_id; 452 + key->digest_size = digest_size; 453 + return key; 454 + 455 + err_pool_end: 456 + tcp_sigpool_end(&hp); 457 + err_free_pool: 458 + tcp_sigpool_release(pool_id); 459 + return ERR_PTR(err); 460 + } 461 + 462 + static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, 463 + sockptr_t optval, int optlen) 464 + { 465 + struct tcp_ao_info *ao_info; 466 + union tcp_ao_addr *addr; 467 + struct tcp_ao_key *key; 468 + struct tcp_ao_add cmd; 469 + bool first = false; 470 + int ret; 471 + 472 + if (optlen < sizeof(cmd)) 473 + return -EINVAL; 474 + 475 + ret = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 476 + if (ret) 477 + return ret; 478 + 479 + if (cmd.keylen > TCP_AO_MAXKEYLEN) 480 + return -EINVAL; 481 + 482 + if (cmd.reserved != 0 || cmd.reserved2 != 0) 483 + return -EINVAL; 484 + 485 + if (family == AF_INET) 486 + ret = tcp_ao_verify_ipv4(sk, &cmd, &addr); 487 + else 488 + ret = tcp_ao_verify_ipv6(sk, &cmd, &addr, &family); 489 + if (ret) 490 + return ret; 491 + 492 + if (cmd.keyflags & ~TCP_AO_KEYF_ALL) 493 + return -EINVAL; 494 + 495 + if (cmd.set_current || cmd.set_rnext) { 496 + if (!tcp_ao_can_set_current_rnext(sk)) 497 + return -EINVAL; 498 + } 499 + 500 + ao_info = setsockopt_ao_info(sk); 501 + if (IS_ERR(ao_info)) 502 + return PTR_ERR(ao_info); 503 + 504 + if (!ao_info) { 505 + ao_info = tcp_ao_alloc_info(GFP_KERNEL); 506 + if (!ao_info) 507 + return -ENOMEM; 508 + first = true; 509 + } else { 510 + /* Check that neither RecvID nor SendID match any 511 + * existing key for the peer, RFC5925 3.1: 512 + * > The IDs of MKTs MUST NOT overlap where their 513 + * > TCP connection identifiers overlap. 514 + */ 515 + if (__tcp_ao_do_lookup(sk, addr, family, 516 + cmd.prefix, -1, cmd.rcvid)) 517 + return -EEXIST; 518 + if (__tcp_ao_do_lookup(sk, addr, family, 519 + cmd.prefix, cmd.sndid, -1)) 520 + return -EEXIST; 521 + } 522 + 523 + key = tcp_ao_key_alloc(sk, &cmd); 524 + if (IS_ERR(key)) { 525 + ret = PTR_ERR(key); 526 + goto err_free_ao; 527 + } 528 + 529 + INIT_HLIST_NODE(&key->node); 530 + memcpy(&key->addr, addr, (family == AF_INET) ? sizeof(struct in_addr) : 531 + sizeof(struct in6_addr)); 532 + key->prefixlen = cmd.prefix; 533 + key->family = family; 534 + key->keyflags = cmd.keyflags; 535 + key->sndid = cmd.sndid; 536 + key->rcvid = cmd.rcvid; 537 + 538 + ret = tcp_ao_parse_crypto(&cmd, key); 539 + if (ret < 0) 540 + goto err_free_sock; 541 + 542 + tcp_ao_link_mkt(ao_info, key); 543 + if (first) { 544 + sk_gso_disable(sk); 545 + rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 546 + } 547 + 548 + if (cmd.set_current) 549 + WRITE_ONCE(ao_info->current_key, key); 550 + if (cmd.set_rnext) 551 + WRITE_ONCE(ao_info->rnext_key, key); 552 + return 0; 553 + 554 + err_free_sock: 555 + atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 556 + tcp_sigpool_release(key->tcp_sigpool_id); 557 + kfree_sensitive(key); 558 + err_free_ao: 559 + if (first) 560 + kfree(ao_info); 561 + return ret; 562 + } 563 + 564 + static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, 565 + struct tcp_ao_key *key, 566 + struct tcp_ao_key *new_current, 567 + struct tcp_ao_key *new_rnext) 568 + { 569 + int err; 570 + 571 + hlist_del_rcu(&key->node); 572 + 573 + /* At this moment another CPU could have looked this key up 574 + * while it was unlinked from the list. Wait for RCU grace period, 575 + * after which the key is off-list and can't be looked up again; 576 + * the rx path [just before RCU came] might have used it and set it 577 + * as current_key (very unlikely). 578 + */ 579 + synchronize_rcu(); 580 + if (new_current) 581 + WRITE_ONCE(ao_info->current_key, new_current); 582 + if (new_rnext) 583 + WRITE_ONCE(ao_info->rnext_key, new_rnext); 584 + 585 + if (unlikely(READ_ONCE(ao_info->current_key) == key || 586 + READ_ONCE(ao_info->rnext_key) == key)) { 587 + err = -EBUSY; 588 + goto add_key; 589 + } 590 + 591 + atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 592 + call_rcu(&key->rcu, tcp_ao_key_free_rcu); 593 + 594 + return 0; 595 + add_key: 596 + hlist_add_head_rcu(&key->node, &ao_info->head); 597 + return err; 598 + } 599 + 600 + static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, 601 + sockptr_t optval, int optlen) 602 + { 603 + struct tcp_ao_key *key, *new_current = NULL, *new_rnext = NULL; 604 + struct tcp_ao_info *ao_info; 605 + union tcp_ao_addr *addr; 606 + struct tcp_ao_del cmd; 607 + int addr_len; 608 + __u8 prefix; 609 + u16 port; 610 + int err; 611 + 612 + if (optlen < sizeof(cmd)) 613 + return -EINVAL; 614 + 615 + err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 616 + if (err) 617 + return err; 618 + 619 + if (cmd.reserved != 0 || cmd.reserved2 != 0) 620 + return -EINVAL; 621 + 622 + if (cmd.set_current || cmd.set_rnext) { 623 + if (!tcp_ao_can_set_current_rnext(sk)) 624 + return -EINVAL; 625 + } 626 + 627 + ao_info = setsockopt_ao_info(sk); 628 + if (IS_ERR(ao_info)) 629 + return PTR_ERR(ao_info); 630 + if (!ao_info) 631 + return -ENOENT; 632 + 633 + /* For sockets in TCP_CLOSED it's possible set keys that aren't 634 + * matching the future peer (address/VRF/etc), 635 + * tcp_ao_connect_init() will choose a correct matching MKT 636 + * if there's any. 637 + */ 638 + if (cmd.set_current) { 639 + new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 640 + if (!new_current) 641 + return -ENOENT; 642 + } 643 + if (cmd.set_rnext) { 644 + new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 645 + if (!new_rnext) 646 + return -ENOENT; 647 + } 648 + 649 + if (family == AF_INET) { 650 + struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.addr; 651 + 652 + addr = (union tcp_ao_addr *)&sin->sin_addr; 653 + addr_len = sizeof(struct in_addr); 654 + port = ntohs(sin->sin_port); 655 + } else { 656 + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.addr; 657 + struct in6_addr *addr6 = &sin6->sin6_addr; 658 + 659 + if (ipv6_addr_v4mapped(addr6)) { 660 + addr = (union tcp_ao_addr *)&addr6->s6_addr32[3]; 661 + addr_len = sizeof(struct in_addr); 662 + family = AF_INET; 663 + } else { 664 + addr = (union tcp_ao_addr *)addr6; 665 + addr_len = sizeof(struct in6_addr); 666 + } 667 + port = ntohs(sin6->sin6_port); 668 + } 669 + prefix = cmd.prefix; 670 + 671 + /* Currently matching is not performed on port (or port ranges) */ 672 + if (port != 0) 673 + return -EINVAL; 674 + 675 + /* We could choose random present key here for current/rnext 676 + * but that's less predictable. Let's be strict and don't 677 + * allow removing a key that's in use. RFC5925 doesn't 678 + * specify how-to coordinate key removal, but says: 679 + * "It is presumed that an MKT affecting a particular 680 + * connection cannot be destroyed during an active connection" 681 + */ 682 + hlist_for_each_entry_rcu(key, &ao_info->head, node) { 683 + if (cmd.sndid != key->sndid || 684 + cmd.rcvid != key->rcvid) 685 + continue; 686 + 687 + if (family != key->family || 688 + prefix != key->prefixlen || 689 + memcmp(addr, &key->addr, addr_len)) 690 + continue; 691 + 692 + if (key == new_current || key == new_rnext) 693 + continue; 694 + 695 + return tcp_ao_delete_key(sk, ao_info, key, 696 + new_current, new_rnext); 697 + } 698 + return -ENOENT; 699 + } 700 + 701 + static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, 702 + sockptr_t optval, int optlen) 703 + { 704 + struct tcp_ao_key *new_current = NULL, *new_rnext = NULL; 705 + struct tcp_ao_info *ao_info; 706 + struct tcp_ao_info_opt cmd; 707 + bool first = false; 708 + int err; 709 + 710 + if (optlen < sizeof(cmd)) 711 + return -EINVAL; 712 + 713 + err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 714 + if (err) 715 + return err; 716 + 717 + if (cmd.set_current || cmd.set_rnext) { 718 + if (!tcp_ao_can_set_current_rnext(sk)) 719 + return -EINVAL; 720 + } 721 + 722 + if (cmd.reserved != 0) 723 + return -EINVAL; 724 + 725 + ao_info = setsockopt_ao_info(sk); 726 + if (IS_ERR(ao_info)) 727 + return PTR_ERR(ao_info); 728 + if (!ao_info) { 729 + ao_info = tcp_ao_alloc_info(GFP_KERNEL); 730 + if (!ao_info) 731 + return -ENOMEM; 732 + first = true; 733 + } 734 + 735 + /* For sockets in TCP_CLOSED it's possible set keys that aren't 736 + * matching the future peer (address/port/VRF/etc), 737 + * tcp_ao_connect_init() will choose a correct matching MKT 738 + * if there's any. 739 + */ 740 + if (cmd.set_current) { 741 + new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 742 + if (!new_current) { 743 + err = -ENOENT; 744 + goto out; 745 + } 746 + } 747 + if (cmd.set_rnext) { 748 + new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 749 + if (!new_rnext) { 750 + err = -ENOENT; 751 + goto out; 752 + } 753 + } 754 + 755 + ao_info->ao_required = cmd.ao_required; 756 + if (new_current) 757 + WRITE_ONCE(ao_info->current_key, new_current); 758 + if (new_rnext) 759 + WRITE_ONCE(ao_info->rnext_key, new_rnext); 760 + if (first) { 761 + sk_gso_disable(sk); 762 + rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 763 + } 764 + return 0; 765 + out: 766 + if (first) 767 + kfree(ao_info); 768 + return err; 769 + } 770 + 771 + int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, 772 + sockptr_t optval, int optlen) 773 + { 774 + if (WARN_ON_ONCE(family != AF_INET && family != AF_INET6)) 775 + return -EAFNOSUPPORT; 776 + 777 + switch (cmd) { 778 + case TCP_AO_ADD_KEY: 779 + return tcp_ao_add_cmd(sk, family, optval, optlen); 780 + case TCP_AO_DEL_KEY: 781 + return tcp_ao_del_cmd(sk, family, optval, optlen); 782 + case TCP_AO_INFO: 783 + return tcp_ao_info_cmd(sk, family, optval, optlen); 784 + default: 785 + WARN_ON_ONCE(1); 786 + return -EINVAL; 787 + } 788 + } 789 + 790 + int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) 791 + { 792 + return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen); 793 + } 794 +
+8 -2
net/ipv4/tcp_ipv4.c
··· 2271 2271 }; 2272 2272 EXPORT_SYMBOL(ipv4_specific); 2273 2273 2274 - #ifdef CONFIG_TCP_MD5SIG 2274 + #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) 2275 2275 static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = { 2276 + #ifdef CONFIG_TCP_MD5SIG 2276 2277 .md5_lookup = tcp_v4_md5_lookup, 2277 2278 .calc_md5_hash = tcp_v4_md5_hash_skb, 2278 2279 .md5_parse = tcp_v4_parse_md5_keys, 2280 + #endif 2281 + #ifdef CONFIG_TCP_AO 2282 + .ao_parse = tcp_v4_parse_ao, 2283 + #endif 2279 2284 }; 2280 2285 #endif 2281 2286 ··· 2295 2290 2296 2291 icsk->icsk_af_ops = &ipv4_specific; 2297 2292 2298 - #ifdef CONFIG_TCP_MD5SIG 2293 + #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) 2299 2294 tcp_sk(sk)->af_specific = &tcp_sock_ipv4_specific; 2300 2295 #endif 2301 2296 ··· 2346 2341 rcu_assign_pointer(tp->md5sig_info, NULL); 2347 2342 } 2348 2343 #endif 2344 + tcp_ao_destroy_sock(sk); 2349 2345 2350 2346 /* Clean up a referenced TCP bind bucket. */ 2351 2347 if (inet_csk(sk)->icsk_bind_hash)
+1
net/ipv6/Makefile
··· 52 52 ifneq ($(CONFIG_IPV6),) 53 53 obj-$(CONFIG_NET_UDP_TUNNEL) += ip6_udp_tunnel.o 54 54 obj-y += mcast_snoop.o 55 + obj-$(CONFIG_TCP_AO) += tcp_ao.o 55 56 endif
+19
net/ipv6/tcp_ao.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * INET An implementation of the TCP Authentication Option (TCP-AO). 4 + * See RFC5925. 5 + * 6 + * Authors: Dmitry Safonov <dima@arista.com> 7 + * Francesco Ruggeri <fruggeri@arista.com> 8 + * Salam Noureddine <noureddine@arista.com> 9 + */ 10 + #include <linux/tcp.h> 11 + 12 + #include <net/tcp.h> 13 + #include <net/ipv6.h> 14 + 15 + int tcp_v6_parse_ao(struct sock *sk, int cmd, 16 + sockptr_t optval, int optlen) 17 + { 18 + return tcp_parse_ao(sk, cmd, AF_INET6, optval, optlen); 19 + }
+24 -15
net/ipv6/tcp_ipv6.c
··· 76 76 77 77 static const struct inet_connection_sock_af_ops ipv6_mapped; 78 78 const struct inet_connection_sock_af_ops ipv6_specific; 79 - #ifdef CONFIG_TCP_MD5SIG 79 + #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) 80 80 static const struct tcp_sock_af_ops tcp_sock_ipv6_specific; 81 81 static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific; 82 - #else 83 - static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(const struct sock *sk, 84 - const struct in6_addr *addr, 85 - int l3index) 86 - { 87 - return NULL; 88 - } 89 82 #endif 90 83 91 84 /* Helper returning the inet6 address from a given tcp socket. ··· 232 239 if (sk_is_mptcp(sk)) 233 240 mptcpv6_handle_mapped(sk, true); 234 241 sk->sk_backlog_rcv = tcp_v4_do_rcv; 235 - #ifdef CONFIG_TCP_MD5SIG 242 + #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) 236 243 tp->af_specific = &tcp_sock_ipv6_mapped_specific; 237 244 #endif 238 245 ··· 245 252 if (sk_is_mptcp(sk)) 246 253 mptcpv6_handle_mapped(sk, false); 247 254 sk->sk_backlog_rcv = tcp_v6_do_rcv; 248 - #ifdef CONFIG_TCP_MD5SIG 255 + #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) 249 256 tp->af_specific = &tcp_sock_ipv6_specific; 250 257 #endif 251 258 goto failure; ··· 762 769 memset(md5_hash, 0, 16); 763 770 return 1; 764 771 } 765 - 772 + #else /* CONFIG_TCP_MD5SIG */ 773 + static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(const struct sock *sk, 774 + const struct in6_addr *addr, 775 + int l3index) 776 + { 777 + return NULL; 778 + } 766 779 #endif 767 780 768 781 static void tcp_v6_init_req(struct request_sock *req, ··· 1227 1228 if (sk_is_mptcp(newsk)) 1228 1229 mptcpv6_handle_mapped(newsk, true); 1229 1230 newsk->sk_backlog_rcv = tcp_v4_do_rcv; 1230 - #ifdef CONFIG_TCP_MD5SIG 1231 + #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) 1231 1232 newtp->af_specific = &tcp_sock_ipv6_mapped_specific; 1232 1233 #endif 1233 1234 ··· 1895 1896 .mtu_reduced = tcp_v6_mtu_reduced, 1896 1897 }; 1897 1898 1898 - #ifdef CONFIG_TCP_MD5SIG 1899 + #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) 1899 1900 static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = { 1901 + #ifdef CONFIG_TCP_MD5SIG 1900 1902 .md5_lookup = tcp_v6_md5_lookup, 1901 1903 .calc_md5_hash = tcp_v6_md5_hash_skb, 1902 1904 .md5_parse = tcp_v6_parse_md5_keys, 1905 + #endif 1906 + #ifdef CONFIG_TCP_AO 1907 + .ao_parse = tcp_v6_parse_ao, 1908 + #endif 1903 1909 }; 1904 1910 #endif 1905 1911 ··· 1926 1922 .mtu_reduced = tcp_v4_mtu_reduced, 1927 1923 }; 1928 1924 1929 - #ifdef CONFIG_TCP_MD5SIG 1925 + #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) 1930 1926 static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = { 1927 + #ifdef CONFIG_TCP_MD5SIG 1931 1928 .md5_lookup = tcp_v4_md5_lookup, 1932 1929 .calc_md5_hash = tcp_v4_md5_hash_skb, 1933 1930 .md5_parse = tcp_v6_parse_md5_keys, 1931 + #endif 1932 + #ifdef CONFIG_TCP_AO 1933 + .ao_parse = tcp_v6_parse_ao, 1934 + #endif 1934 1935 }; 1935 1936 #endif 1936 1937 ··· 1950 1941 1951 1942 icsk->icsk_af_ops = &ipv6_specific; 1952 1943 1953 - #ifdef CONFIG_TCP_MD5SIG 1944 + #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) 1954 1945 tcp_sk(sk)->af_specific = &tcp_sock_ipv6_specific; 1955 1946 #endif 1956 1947