From patchwork Thu Dec 23 15:39:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698413 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B38B9C433FE for ; Thu, 23 Dec 2021 15:40:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349018AbhLWPkn (ORCPT ); Thu, 23 Dec 2021 10:40:43 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33782 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232417AbhLWPkk (ORCPT ); Thu, 23 Dec 2021 10:40:40 -0500 Received: from mail-ed1-x52b.google.com (mail-ed1-x52b.google.com [IPv6:2a00:1450:4864:20::52b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 08FE7C061401; Thu, 23 Dec 2021 07:40:40 -0800 (PST) Received: by mail-ed1-x52b.google.com with SMTP id j21so22917645edt.9; Thu, 23 Dec 2021 07:40:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=A94jbFEgit2Xs8Q5F0ETPifT1brSUIg4/eJno4VI3a4=; b=fGI/5ltiNm5ooXknDZKJ72gZU9pLK6L4drqcbaB6Nmip6VZdDwBsEKsHY0hzrIhrNZ 3uybp98AGsZA1EziWX8FNkpP4GtJjQimbjr82uD/+5fbLL0mTcAg5eFbIWx3JNRQ8U/5 sTCEZa8oM/7sdx5ipkD3ZeE50wr4pJWtCLHEpA/5Rab6FPu9vs4VKiLYxd+ehzYFZP+N YXvS+LAFReetjjAa46AnBZNYc7iYcK6caqIDTRX2J5HapCpZ3xmm+fqkDfbvqzhoua/z xwmPF9hObQckNK/SNMuDHdIRkfr7XEaQgMrQqJp4F9m/9OryiAHXPfqbdxsZxr4sJUIh Q1RA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=A94jbFEgit2Xs8Q5F0ETPifT1brSUIg4/eJno4VI3a4=; b=TD7QgA9lc0A6TjLNveLAvK/FBtPcz1iUsQWrT/FzoJwM0csWmSLTZa36PFNBUSQM5f BjkRt6Zbe5uZb7sLbND+EJGRQ7iaT9lik2D6t2Ig7RQNUPGavqsxQI4xD8CLCOF7mrxv JiPO7UpZElqSFZaiv+MI2Fr7kIFewcU00Hh3dUKa4gfV51fLL1pjwcLeVVGSeDZxaL7e ONzLxW6nHST05xh++NiG806nkp0MyqvonAEMS/vGPiHXgghqOVkYK6BvOexu0n7nr+v0 Mh7s7Hh6qP2Pd75Y8OE6Sk1wwuB4kU7YXtjFYGE+Spx3tHN0S1+gWC4JMtMYCTfYTqf6 UOFg== X-Gm-Message-State: AOAM531DS9/mAr5yLg9yapDkoqWZnITdc2uWgXKHS/bjWwXwg/3TyM+T 9YToWLoQZRwn8lzQdN95Q6AuQKooQA/aAg== X-Google-Smtp-Source: ABdhPJwgxqb0g+T5NgXAjil4Vt1XYuVvTItK8v7spZTIIJwBqcSaV1C0d9Urh8+LjbBRoAvi3nxsEg== X-Received: by 2002:a17:907:a42a:: with SMTP id sg42mr2312911ejc.405.1640274038390; Thu, 23 Dec 2021 07:40:38 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:37 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 01/19] tcp: authopt: Initial support and key management Date: Thu, 23 Dec 2021 17:39:56 +0200 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org This commit adds support to add and remove keys but does not use them further. Similar to tcp md5 a single pointer to a struct tcp_authopt_info* struct is added to struct tcp_sock, this avoids increasing memory usage. The data structures related to tcp_authopt are initialized on setsockopt and only freed on socket close. Signed-off-by: Leonard Crestez --- include/linux/tcp.h | 9 + include/net/net_namespace.h | 4 + include/net/netns/tcp_authopt.h | 12 ++ include/net/tcp.h | 1 + include/net/tcp_authopt.h | 80 ++++++++ include/uapi/linux/tcp.h | 81 +++++++++ net/ipv4/Kconfig | 14 ++ net/ipv4/Makefile | 1 + net/ipv4/tcp.c | 30 +++ net/ipv4/tcp_authopt.c | 311 ++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 2 + 11 files changed, 545 insertions(+) create mode 100644 include/net/netns/tcp_authopt.h create mode 100644 include/net/tcp_authopt.h create mode 100644 net/ipv4/tcp_authopt.c diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 78b91bb92f0d..497604176119 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -140,10 +140,12 @@ struct tcp_request_sock { static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) { return (struct tcp_request_sock *)req; } +struct tcp_authopt_info; + struct tcp_sock { /* inet_connection_sock has to be the first member of tcp_sock */ struct inet_connection_sock inet_conn; u16 tcp_header_len; /* Bytes of tcp header to send */ u16 gso_segs; /* Max number of segs per GSO packet */ @@ -403,10 +405,14 @@ struct tcp_sock { /* TCP MD5 Signature Option information */ struct tcp_md5sig_info __rcu *md5sig_info; #endif +#ifdef CONFIG_TCP_AUTHOPT + struct tcp_authopt_info __rcu *authopt_info; +#endif + /* TCP fastopen related information */ struct tcp_fastopen_request *fastopen_req; /* fastopen_rsk points to request_sock that resulted in this big * socket. Used to retransmit SYNACKs etc. */ @@ -453,10 +459,13 @@ struct tcp_timewait_sock { int tw_ts_recent_stamp; u32 tw_tx_delay; #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *tw_md5_key; #endif +#ifdef CONFIG_TCP_AUTHOPT + struct tcp_authopt_info *tw_authopt_info; +#endif }; static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk) { return (struct tcp_timewait_sock *)sk; diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 5b61c462e534..5b3178b7fbda 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -32,10 +32,11 @@ #include #include #include #include #include +#include #include #include #include #include #include @@ -176,10 +177,13 @@ struct net { #endif struct sock *diag_nlsk; #if IS_ENABLED(CONFIG_SMC) struct netns_smc smc; #endif +#if IS_ENABLED(CONFIG_TCP_AUTHOPT) + struct netns_tcp_authopt tcp_authopt; +#endif } __randomize_layout; #include /* Init's network namespace */ diff --git a/include/net/netns/tcp_authopt.h b/include/net/netns/tcp_authopt.h new file mode 100644 index 000000000000..03b7f4e58448 --- /dev/null +++ b/include/net/netns/tcp_authopt.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __NETNS_TCP_AUTHOPT_H__ +#define __NETNS_TCP_AUTHOPT_H__ + +#include + +struct netns_tcp_authopt { + struct hlist_head head; + struct mutex mutex; +}; + +#endif /* __NETNS_TCP_AUTHOPT_H__ */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 44e442bf23f9..6cc2eeb45deb 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -184,10 +184,11 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOPT_WINDOW 3 /* Window scaling */ #define TCPOPT_SACK_PERM 4 /* SACK Permitted */ #define TCPOPT_SACK 5 /* SACK Block */ #define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ +#define TCPOPT_AUTHOPT 29 /* Auth Option (RFC5925) */ #define TCPOPT_MPTCP 30 /* Multipath TCP (RFC6824) */ #define TCPOPT_FASTOPEN 34 /* Fast open (RFC7413) */ #define TCPOPT_EXP 254 /* Experimental */ /* Magic number to be after the option value for sharing TCP * experimental options. See draft-ietf-tcpm-experimental-options-00.txt diff --git a/include/net/tcp_authopt.h b/include/net/tcp_authopt.h new file mode 100644 index 000000000000..0d9cab459d10 --- /dev/null +++ b/include/net/tcp_authopt.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _LINUX_TCP_AUTHOPT_H +#define _LINUX_TCP_AUTHOPT_H + +#include +#include +#include + +/** + * struct tcp_authopt_key_info - Representation of a Master Key Tuple as per RFC5925 + * + * Key structure lifetime is protected by RCU so send/recv code needs to hold a + * single rcu_read_lock until they're done with the key. + * + * Global keys can be cached in sockets, this requires increasing kref. + */ +struct tcp_authopt_key_info { + /** @node: node in &netns_tcp_authopt.head list */ + struct hlist_node node; + /** @rcu: for kfree_rcu */ + struct rcu_head rcu; + /** @ref: for kref_put */ + struct kref ref; + /** @flags: Combination of &enum tcp_authopt_key_flag */ + u32 flags; + /** @send_id: Same as &tcp_authopt_key.send_id */ + u8 send_id; + /** @recv_id: Same as &tcp_authopt_key.recv_id */ + u8 recv_id; + /** @alg_id: Same as &tcp_authopt_key.alg */ + u8 alg_id; + /** @keylen: Same as &tcp_authopt_key.keylen */ + u8 keylen; + /** @key: Same as &tcp_authopt_key.key */ + u8 key[TCP_AUTHOPT_MAXKEYLEN]; + /** @addr: Same as &tcp_authopt_key.addr */ + struct sockaddr_storage addr; +}; + +/** + * struct tcp_authopt_info - Per-socket information regarding tcp_authopt + * + * This is lazy-initialized in order to avoid increasing memory usage for + * regular TCP sockets. Once created it is only destroyed on socket close. + */ +struct tcp_authopt_info { + /** @rcu: for kfree_rcu */ + struct rcu_head rcu; + /** @flags: Combination of &enum tcp_authopt_key_flag */ + u32 flags; + /** @src_isn: Local Initial Sequence Number */ + u32 src_isn; + /** @dst_isn: Remote Initial Sequence Number */ + u32 dst_isn; +}; + +#ifdef CONFIG_TCP_AUTHOPT +void tcp_authopt_clear(struct sock *sk); +int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen); +int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *key); +int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen); +#else +static inline int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen) +{ + return -ENOPROTOOPT; +} +static inline int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *key) +{ + return -ENOPROTOOPT; +} +static inline void tcp_authopt_clear(struct sock *sk) +{ +} +static inline int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) +{ + return -ENOPROTOOPT; +} +#endif + +#endif /* _LINUX_TCP_AUTHOPT_H */ diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 8fc09e8638b3..76d7be6b27f4 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -126,10 +126,12 @@ enum { #define TCP_INQ 36 /* Notify bytes available to read as a cmsg on read */ #define TCP_CM_INQ TCP_INQ #define TCP_TX_DELAY 37 /* delay outgoing packets by XX usec */ +#define TCP_AUTHOPT 38 /* TCP Authentication Option (RFC5925) */ +#define TCP_AUTHOPT_KEY 39 /* TCP Authentication Option Key (RFC5925) */ #define TCP_REPAIR_ON 1 #define TCP_REPAIR_OFF 0 #define TCP_REPAIR_OFF_NO_WP -1 /* Turn off without window probes */ @@ -340,10 +342,89 @@ struct tcp_diag_md5sig { __u16 tcpm_keylen; __be32 tcpm_addr[4]; __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; }; +/** + * enum tcp_authopt_flag - flags for `tcp_authopt.flags` + */ +enum tcp_authopt_flag { + /** + * @TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED: + * Configure behavior of segments with TCP-AO coming from hosts for which no + * key is configured. The default recommended by RFC is to silently accept + * such connections. + */ + TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED = (1 << 2), +}; + +/** + * struct tcp_authopt - Per-socket options related to TCP Authentication Option + */ +struct tcp_authopt { + /** @flags: Combination of &enum tcp_authopt_flag */ + __u32 flags; +}; + +/** + * enum tcp_authopt_key_flag - flags for `tcp_authopt.flags` + * + * @TCP_AUTHOPT_KEY_DEL: Delete the key and ignore non-id fields + * @TCP_AUTHOPT_KEY_EXCLUDE_OPTS: Exclude TCP options from signature + * @TCP_AUTHOPT_KEY_ADDR_BIND: Key only valid for `tcp_authopt.addr` + */ +enum tcp_authopt_key_flag { + TCP_AUTHOPT_KEY_DEL = (1 << 0), + TCP_AUTHOPT_KEY_EXCLUDE_OPTS = (1 << 1), + TCP_AUTHOPT_KEY_ADDR_BIND = (1 << 2), +}; + +/** + * enum tcp_authopt_alg - Algorithms for TCP Authentication Option + */ +enum tcp_authopt_alg { + /** @TCP_AUTHOPT_ALG_HMAC_SHA_1_96: HMAC-SHA-1-96 as described in RFC5926 */ + TCP_AUTHOPT_ALG_HMAC_SHA_1_96 = 1, + /** @TCP_AUTHOPT_ALG_AES_128_CMAC_96: AES-128-CMAC-96 as described in RFC5926 */ + TCP_AUTHOPT_ALG_AES_128_CMAC_96 = 2, +}; + +/* for TCP_AUTHOPT_KEY socket option */ +#define TCP_AUTHOPT_MAXKEYLEN 80 + +/** + * struct tcp_authopt_key - TCP Authentication KEY + * + * Key are identified by the combination of: + * - send_id + * - recv_id + * - addr (iff TCP_AUTHOPT_KEY_ADDR_BIND) + * + * RFC5925 requires that key ids must not overlap for the same TCP connection. + * This is not enforced by linux. + */ +struct tcp_authopt_key { + /** @flags: Combination of &enum tcp_authopt_key_flag */ + __u32 flags; + /** @send_id: keyid value for send */ + __u8 send_id; + /** @recv_id: keyid value for receive */ + __u8 recv_id; + /** @alg: One of &enum tcp_authopt_alg */ + __u8 alg; + /** @keylen: Length of the key buffer */ + __u8 keylen; + /** @key: Secret key */ + __u8 key[TCP_AUTHOPT_MAXKEYLEN]; + /** + * @addr: Key is only valid for this address + * + * Ignored unless TCP_AUTHOPT_KEY_ADDR_BIND flag is set + */ + struct __kernel_sockaddr_storage addr; +}; + /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 struct tcp_zerocopy_receive { __u64 address; /* in: address of mapping */ diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 87983e70f03f..6459f4ea6f1d 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -740,5 +740,19 @@ config TCP_MD5SIG RFC2385 specifies a method of giving MD5 protection to TCP sessions. Its main (only?) use is to protect BGP sessions between core routers on the Internet. If unsure, say N. + +config TCP_AUTHOPT + bool "TCP: Authentication Option support (RFC5925)" + select CRYPTO + select CRYPTO_SHA1 + select CRYPTO_HMAC + select CRYPTO_AES + select CRYPTO_CMAC + help + RFC5925 specifies a new method of giving protection to TCP sessions. + Its intended use is to protect BGP sessions between core routers + on the Internet. It obsoletes TCP MD5 (RFC2385) but is incompatible. + + If unsure, say N. diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index bbdd9c44f14e..d336f32ce177 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -59,10 +59,11 @@ obj-$(CONFIG_TCP_CONG_NV) += tcp_nv.o obj-$(CONFIG_TCP_CONG_VENO) += tcp_veno.o obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o +obj-$(CONFIG_TCP_AUTHOPT) += tcp_authopt.o obj-$(CONFIG_NET_SOCK_MSG) += tcp_bpf.o obj-$(CONFIG_BPF_SYSCALL) += udp_bpf.o obj-$(CONFIG_NETLABEL) += cipso_ipv4.o obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 20054618c87e..022811dd705d 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -270,10 +270,11 @@ #include #include #include #include +#include #include #include #include #include @@ -3602,10 +3603,16 @@ static int do_tcp_setsockopt(struct sock *sk, int level, int optname, case TCP_MD5SIG: case TCP_MD5SIG_EXT: err = tp->af_specific->md5_parse(sk, optname, optval, optlen); break; #endif + case TCP_AUTHOPT: + err = tcp_set_authopt(sk, optval, optlen); + break; + case TCP_AUTHOPT_KEY: + err = tcp_set_authopt_key(sk, optval, optlen); + break; case TCP_USER_TIMEOUT: /* Cap the max time in ms TCP will retry or probe the window * before giving up and aborting (ETIMEDOUT) a connection. */ if (val < 0) @@ -4251,10 +4258,33 @@ static int do_tcp_getsockopt(struct sock *sk, int level, if (!err && copy_to_user(optval, &zc, len)) err = -EFAULT; return err; } #endif +#ifdef CONFIG_TCP_AUTHOPT + case TCP_AUTHOPT: { + struct tcp_authopt info; + int err; + + if (get_user(len, optlen)) + return -EFAULT; + + lock_sock(sk); + err = tcp_get_authopt_val(sk, &info); + release_sock(sk); + + if (err) + return err; + len = min_t(unsigned int, len, sizeof(info)); + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &info, len)) + return -EFAULT; + return 0; + } +#endif + default: return -ENOPROTOOPT; } if (put_user(len, optlen)) diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c new file mode 100644 index 000000000000..17392c42e99f --- /dev/null +++ b/net/ipv4/tcp_authopt.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +static inline struct netns_tcp_authopt *sock_net_tcp_authopt(const struct sock *sk) +{ + return &sock_net(sk)->tcp_authopt; +} + +static void tcp_authopt_key_release_kref(struct kref *ref) +{ + struct tcp_authopt_key_info *key = container_of(ref, struct tcp_authopt_key_info, ref); + + kfree_rcu(key, rcu); +} + +static void tcp_authopt_key_put(struct tcp_authopt_key_info *key) +{ + if (key) + kref_put(&key->ref, tcp_authopt_key_release_kref); +} + +static void tcp_authopt_key_del(struct netns_tcp_authopt *net, + struct tcp_authopt_key_info *key) +{ + lockdep_assert_held(&net->mutex); + hlist_del_rcu(&key->node); + key->flags |= TCP_AUTHOPT_KEY_DEL; + kref_put(&key->ref, tcp_authopt_key_release_kref); +} + +/* Free info and keys. + * Don't touch tp->authopt_info, it might not even be assigned yes. + */ +void tcp_authopt_free(struct sock *sk, struct tcp_authopt_info *info) +{ + kfree_rcu(info, rcu); +} + +/* Free everything and clear tcp_sock.authopt_info to NULL */ +void tcp_authopt_clear(struct sock *sk) +{ + struct tcp_authopt_info *info; + + info = rcu_dereference_protected(tcp_sk(sk)->authopt_info, lockdep_sock_is_held(sk)); + if (info) { + tcp_authopt_free(sk, info); + tcp_sk(sk)->authopt_info = NULL; + } +} + +/* checks that ipv4 or ipv6 addr matches. */ +static bool ipvx_addr_match(struct sockaddr_storage *a1, + struct sockaddr_storage *a2) +{ + if (a1->ss_family != a2->ss_family) + return false; + if (a1->ss_family == AF_INET && + (((struct sockaddr_in *)a1)->sin_addr.s_addr != + ((struct sockaddr_in *)a2)->sin_addr.s_addr)) + return false; + if (a1->ss_family == AF_INET6 && + !ipv6_addr_equal(&((struct sockaddr_in6 *)a1)->sin6_addr, + &((struct sockaddr_in6 *)a2)->sin6_addr)) + return false; + return true; +} + +static bool tcp_authopt_key_match_exact(struct tcp_authopt_key_info *info, + struct tcp_authopt_key *key) +{ + if (info->send_id != key->send_id) + return false; + if (info->recv_id != key->recv_id) + return false; + if ((info->flags & TCP_AUTHOPT_KEY_ADDR_BIND) != (key->flags & TCP_AUTHOPT_KEY_ADDR_BIND)) + return false; + if (info->flags & TCP_AUTHOPT_KEY_ADDR_BIND) + if (!ipvx_addr_match(&info->addr, &key->addr)) + return false; + + return true; +} + +static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct sock *sk, + struct netns_tcp_authopt *net, + struct tcp_authopt_key *ukey) +{ + struct tcp_authopt_key_info *key_info; + + hlist_for_each_entry_rcu(key_info, &net->head, node, lockdep_is_held(&net->mutex)) + if (tcp_authopt_key_match_exact(key_info, ukey)) + return key_info; + + return NULL; +} + +static struct tcp_authopt_info *__tcp_authopt_info_get_or_create(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_authopt_info *info; + + info = rcu_dereference_check(tp->authopt_info, lockdep_sock_is_held(sk)); + if (info) + return info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + sk_gso_disable(sk); + rcu_assign_pointer(tp->authopt_info, info); + + return info; +} + +#define TCP_AUTHOPT_KNOWN_FLAGS ( \ + TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED) + +/* Like copy_from_sockopt except tolerate different optlen for compatibility reasons + * + * If the src is shorter then it's from an old userspace and the rest of dst is + * filled with zeros. + * + * If the dst is shorter then src is from a newer userspace and we only accept + * if the rest of the option is all zeros. + * + * This allows sockopts to grow as long as for new fields zeros has no effect. + */ +static int _copy_from_sockptr_tolerant(u8 *dst, + unsigned int dstlen, + sockptr_t src, + unsigned int srclen) +{ + int err; + + /* If userspace optlen is too short fill the rest with zeros */ + if (srclen > dstlen) { + if (sockptr_is_kernel(src)) + return -EINVAL; + err = check_zeroed_user(src.user + dstlen, srclen - dstlen); + if (err < 0) + return err; + if (err == 0) + return -EINVAL; + } + err = copy_from_sockptr(dst, src, min(srclen, dstlen)); + if (err) + return err; + if (srclen < dstlen) + memset(dst + srclen, 0, dstlen - srclen); + + return err; +} + +int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen) +{ + struct tcp_authopt opt; + struct tcp_authopt_info *info; + int err; + + sock_owned_by_me(sk); + + err = _copy_from_sockptr_tolerant((u8 *)&opt, sizeof(opt), optval, optlen); + if (err) + return err; + + if (opt.flags & ~TCP_AUTHOPT_KNOWN_FLAGS) + return -EINVAL; + + info = __tcp_authopt_info_get_or_create(sk); + if (IS_ERR(info)) + return PTR_ERR(info); + + info->flags = opt.flags & TCP_AUTHOPT_KNOWN_FLAGS; + + return 0; +} + +int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *opt) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_authopt_info *info; + + sock_owned_by_me(sk); + + memset(opt, 0, sizeof(*opt)); + info = rcu_dereference_check(tp->authopt_info, lockdep_sock_is_held(sk)); + if (!info) + return -ENOENT; + + opt->flags = info->flags & TCP_AUTHOPT_KNOWN_FLAGS; + + return 0; +} + +#define TCP_AUTHOPT_KEY_KNOWN_FLAGS ( \ + TCP_AUTHOPT_KEY_DEL | \ + TCP_AUTHOPT_KEY_EXCLUDE_OPTS | \ + TCP_AUTHOPT_KEY_ADDR_BIND) + +int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) +{ + struct tcp_authopt_key opt; + struct tcp_authopt_info *info; + struct tcp_authopt_key_info *key_info, *old_key_info; + struct netns_tcp_authopt *net = sock_net_tcp_authopt(sk); + int err; + + sock_owned_by_me(sk); + if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) + return -EPERM; + + err = _copy_from_sockptr_tolerant((u8 *)&opt, sizeof(opt), optval, optlen); + if (err) + return err; + + if (opt.flags & ~TCP_AUTHOPT_KEY_KNOWN_FLAGS) + return -EINVAL; + + if (opt.keylen > TCP_AUTHOPT_MAXKEYLEN) + return -EINVAL; + + /* Delete is a special case: */ + if (opt.flags & TCP_AUTHOPT_KEY_DEL) { + mutex_lock(&net->mutex); + key_info = tcp_authopt_key_lookup_exact(sk, net, &opt); + if (key_info) { + tcp_authopt_key_del(net, key_info); + err = 0; + } else { + err = -ENOENT; + } + mutex_unlock(&net->mutex); + return err; + } + + /* check key family */ + if (opt.flags & TCP_AUTHOPT_KEY_ADDR_BIND) { + if (sk->sk_family != opt.addr.ss_family) + return -EINVAL; + } + + /* Initialize tcp_authopt_info if not already set */ + info = __tcp_authopt_info_get_or_create(sk); + if (IS_ERR(info)) + return PTR_ERR(info); + + key_info = sock_kmalloc(sk, sizeof(*key_info), GFP_KERNEL | __GFP_ZERO); + if (!key_info) + return -ENOMEM; + mutex_lock(&net->mutex); + kref_init(&key_info->ref); + /* If an old key exists with exact ID then remove and replace. + * RCU-protected readers might observe both and pick any. + */ + old_key_info = tcp_authopt_key_lookup_exact(sk, net, &opt); + if (old_key_info) + tcp_authopt_key_del(net, old_key_info); + key_info->flags = opt.flags & TCP_AUTHOPT_KEY_KNOWN_FLAGS; + key_info->send_id = opt.send_id; + key_info->recv_id = opt.recv_id; + key_info->alg_id = opt.alg; + key_info->keylen = opt.keylen; + memcpy(key_info->key, opt.key, opt.keylen); + memcpy(&key_info->addr, &opt.addr, sizeof(key_info->addr)); + hlist_add_head_rcu(&key_info->node, &net->head); + mutex_unlock(&net->mutex); + + return 0; +} + +static int tcp_authopt_init_net(struct net *full_net) +{ + struct netns_tcp_authopt *net = &full_net->tcp_authopt; + + mutex_init(&net->mutex); + INIT_HLIST_HEAD(&net->head); + + return 0; +} + +static void tcp_authopt_exit_net(struct net *full_net) +{ + struct netns_tcp_authopt *net = &full_net->tcp_authopt; + struct tcp_authopt_key_info *key; + struct hlist_node *n; + + mutex_lock(&net->mutex); + + hlist_for_each_entry_safe(key, n, &net->head, node) { + hlist_del_rcu(&key->node); + tcp_authopt_key_put(key); + } + + mutex_unlock(&net->mutex); +} + +static struct pernet_operations net_ops = { + .init = tcp_authopt_init_net, + .exit = tcp_authopt_exit_net, +}; + +static int __init tcp_authopt_init(void) +{ + return register_pernet_subsys(&net_ops); +} +late_initcall(tcp_authopt_init); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 3dd19a2bf06c..0aa5122b29e0 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -60,10 +60,11 @@ #include #include #include #include +#include #include #include #include #include #include @@ -2276,10 +2277,11 @@ void tcp_v4_destroy_sock(struct sock *sk) tcp_clear_md5_list(sk); kfree_rcu(rcu_dereference_protected(tp->md5sig_info, 1), rcu); tp->md5sig_info = NULL; } #endif + tcp_authopt_clear(sk); /* Clean up a referenced TCP bind bucket. */ if (inet_csk(sk)->icsk_bind_hash) inet_put_port(sk); From patchwork Thu Dec 23 15:39:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698414 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2B83CC433F5 for ; Thu, 23 Dec 2021 15:40:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349034AbhLWPkp (ORCPT ); Thu, 23 Dec 2021 10:40:45 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33794 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349013AbhLWPkm (ORCPT ); Thu, 23 Dec 2021 10:40:42 -0500 Received: from mail-ed1-x52f.google.com (mail-ed1-x52f.google.com [IPv6:2a00:1450:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E79ABC061757; Thu, 23 Dec 2021 07:40:41 -0800 (PST) Received: by mail-ed1-x52f.google.com with SMTP id o6so22972532edc.4; Thu, 23 Dec 2021 07:40:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=dkmS0Cn+xGZLK+jdSmt5kOascj8B8GCiGD14/T3W7CI=; b=T9lR1BqjJlLABep1NTKOI6UKfJb9U+22sWOMnYNzIdJM5fRvvnC1GNrrb0pw0RzFgz yjgz+o4y8jEl2p6hgEJgarlhrVB9wJT1XZq32PdiRuBRXaF1MoJDI8s33ddtUb7FqzUG 7vlJsXevJ3whHk+UWV1sf5s9NJPqh54AmDdK7uqL3uRrcIwpgGS3cTCE36fg58yyP0pV VK0aSNsqeWctt15JrI7UTTv0PtvlGZDP2dgokjq3mDP2N6iDzjFfyvalomZV6bzTfCUA 2juXiqh5U+h4eBOXP7XS3vvOMCQJ0E9xBf8+GIZXg0wPyMKLjRucKnPdyW/zJNlDT1Cs +0hQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=dkmS0Cn+xGZLK+jdSmt5kOascj8B8GCiGD14/T3W7CI=; b=elZP4w2Paa/inXVG2JiN5QhEG3FfBgmlXsUpdphQm31XJjQ2/6PMTbkECVBAd7SKB6 9LosS2CSFbne87hW20D7Ibwh5AFF3P9UaHC7nhw7lSDwBTesz6T+Om38AuyCSeevAxIS eliZTwCwg8Oo9D4dg4d9mmmi7Idbza1ToPkNaeZ9kMi5vb8Dkp5Zp/lApHSGoNYdZIcL P7ogUEYvyK6PenbBqEc/21xZ5FQo8LN4QyVdWpvJ/76BiXtNre7W7QWsfAuR4XQy5z0d m1VXf2VCvKQS7Quehwvo7BAOX2IgmJ3yklQKdgegh3vgqQ9TLZCD6A8UZTYSFaJJbRhe n+tg== X-Gm-Message-State: AOAM530qs7fkbNXoobQHc2aFkJRcIAy8tMYuyPHm5sA03sYxK1T7W3J6 B7noSXFPKcL27RlEn3yOh5Y= X-Google-Smtp-Source: ABdhPJx+PaQa0e19Kafpqo5KgHmsytvqhKFXKZIMk4610aJntPYi1bjuNgzeKI1ey547amYqbqOo9Q== X-Received: by 2002:a17:906:5208:: with SMTP id g8mr2312261ejm.634.1640274040511; Thu, 23 Dec 2021 07:40:40 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:39 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 02/19] docs: Add user documentation for tcp_authopt Date: Thu, 23 Dec 2021 17:39:57 +0200 Message-Id: <68b9a766f7ea045f0ea56f27d7523abb41daab37.1640273966.git.cdleonard@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org The .rst documentation contains a brief description of the user interface and includes kernel-doc generated from uapi header. Signed-off-by: Leonard Crestez --- Documentation/networking/index.rst | 1 + Documentation/networking/tcp_authopt.rst | 51 ++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 Documentation/networking/tcp_authopt.rst diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst index 58bc8cd367c6..f5c324a060d8 100644 --- a/Documentation/networking/index.rst +++ b/Documentation/networking/index.rst @@ -100,10 +100,11 @@ Contents: strparser switchdev sysfs-tagging tc-actions-env-rules tcp-thin + tcp_authopt team timestamping tipc tproxy tuntap diff --git a/Documentation/networking/tcp_authopt.rst b/Documentation/networking/tcp_authopt.rst new file mode 100644 index 000000000000..72adb7a891ce --- /dev/null +++ b/Documentation/networking/tcp_authopt.rst @@ -0,0 +1,51 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================= +TCP Authentication Option +========================= + +The TCP Authentication option specified by RFC5925 replaces the TCP MD5 +Signature option. It similar in goals but not compatible in either wire formats +or ABI. + +Interface +========= + +Individual keys can be added to or removed through an TCP socket by using +TCP_AUTHOPT_KEY setsockopt and a struct tcp_authopt_key. There is no +support for reading back keys and updates always replace the old key. These +structures represent "Master Key Tuples (MKTs)" as described by the RFC. + +Per-socket options can set or read using the TCP_AUTHOPT sockopt and a struct +tcp_authopt. This is optional: doing setsockopt TCP_AUTHOPT_KEY is sufficient to +enable the feature. + +Configuration associated with TCP Authentication is global for each network +namespace, this means that all sockets for which TCP_AUTHOPT is enabled will +be affected by the same set of keys. + +Manipulating keys requires ``CAP_NET_ADMIN``. + +Key binding +----------- + +Keys can be bound to remote addresses in a way that is somewhat similar to +``TCP_MD5SIG``. By default a key matches all connections but matching criteria can +be specified as fields inside struct tcp_authopt_key together with matching +flags in tcp_authopt_key.flags. The sort of these "matching criteria" can +expand over time by increasing the size of `struct tcp_authopt_key` and adding +new flags. + + * Address binding is optional, by default keys match all addresses + * Local address is ignored, matching is done by remote address + * Ports are ignored + +RFC5925 requires that key ids do not overlap when tcp identifiers (addr/port) +overlap. This is not enforced by linux, configuring ambiguous keys will result +in packet drops and lost connections. + +ABI Reference +============= + +.. kernel-doc:: include/uapi/linux/tcp.h + :identifiers: tcp_authopt tcp_authopt_flag tcp_authopt_key tcp_authopt_key_flag tcp_authopt_alg From patchwork Thu Dec 23 15:39:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698415 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 65FF4C433F5 for ; Thu, 23 Dec 2021 15:40:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232417AbhLWPk5 (ORCPT ); Thu, 23 Dec 2021 10:40:57 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33808 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349022AbhLWPko (ORCPT ); Thu, 23 Dec 2021 10:40:44 -0500 Received: from mail-ed1-x52d.google.com (mail-ed1-x52d.google.com [IPv6:2a00:1450:4864:20::52d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AAC4FC061756; Thu, 23 Dec 2021 07:40:43 -0800 (PST) Received: by mail-ed1-x52d.google.com with SMTP id j21so22918646edt.9; Thu, 23 Dec 2021 07:40:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=0XF4BCcCfYprTbmAK9I4jxJwe4NvjnyiY/HJigZj5DE=; b=fuPjVdHJjDD8gfhEGJoCVPhGJmsmGWklbAt5SrzDIsN2mRLsxI15eJH5QDOFfR5Dks ZwYWCKRG6dUGFVUVLaUlUemQySZtR4aKgOjmcuS4nWZbN2LUP3X1pw0n4dFZi++Cn05Z RDL5ldd9hV3SVZ4+lff50dowJ97lzKsY+rDd6o3jhVHGkbplv0LOQ540LmNSpJ841W5z p0SVHGhbhthgsO/mskdYLaJ+BsKc37l3Ze0ayMgk9gNXBWElVBd+ZrsjSDsP8qPQgSNd EVK8FKCWNwny5RdPsb9pUl0Y//n0Zu07oudvjVYNfP6NHuNfOQU3aZzrb/LqBTNqUfqM 3YEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=0XF4BCcCfYprTbmAK9I4jxJwe4NvjnyiY/HJigZj5DE=; b=FBJJFHbrYkqAJGtX4/EtVX7nMrHF/FLuDaCkVPVRcYbaxxbsxXxR+kxe7aGGn6Ud+P DAgBsTbnUogeuG0y81OFywgq4rkZtWoebfFPz55h2XvZZPcUzz4e4Frg1KTa9LNLztnx WXX3gYv9fc9JJU/7nkz6/nKaEQ+nw6Wlttun7SN8L5QRfXNU3rgqwfLk7Sg2IM3io7Vx Np55WyEAq85llMeF++caWaRo+lTNHUNEcuXwuo0NKSTqnyCpYp6eWzv3/2uAPg+z767C qJvjOnPcHOML8mWq2ZOrKRrUTKpYFnMhRQMGg22JGTqyIQgWpy/MXETLsaZ3M1a2TaNZ hkXg== X-Gm-Message-State: AOAM531dKrP2iM/OLydrpvjKW6zvp91yV2puioyN/C6oKA83haGjWPrl G+izAf4iqi/UHuPFnV11bfI= X-Google-Smtp-Source: ABdhPJxNOk2AlCxI/HfohKxFgaIY4DVgDibl4FNXpxSJtlFJSpuIyFeWc6zs3yO8WfzYYZ5YcWnIPA== X-Received: by 2002:a17:906:8607:: with SMTP id o7mr2487098ejx.334.1640274042184; Thu, 23 Dec 2021 07:40:42 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:41 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 03/19] tcp: authopt: Add crypto initialization Date: Thu, 23 Dec 2021 17:39:58 +0200 Message-Id: <99d7d0c53277c758be73c15d58849eb6358e79d0.1640273966.git.cdleonard@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org The crypto_shash API is used in order to compute packet signatures. The API comes with several unfortunate limitations: 1) Allocating a crypto_shash can sleep and must be done in user context. 2) Packet signatures must be computed in softirq context 3) Packet signatures use dynamic "traffic keys" which require exclusive access to crypto_shash for crypto_setkey. The solution is to allocate one crypto_shash for each possible cpu for each algorithm at setsockopt time. The per-cpu tfm is then borrowed from softirq context, signatures are computed and the tfm is returned. The pool for each algorithm is allocated on first use. Signed-off-by: Leonard Crestez --- include/net/tcp_authopt.h | 16 ++++ net/ipv4/tcp_authopt.c | 192 +++++++++++++++++++++++++++++++++++++- 2 files changed, 207 insertions(+), 1 deletion(-) diff --git a/include/net/tcp_authopt.h b/include/net/tcp_authopt.h index 0d9cab459d10..bbd0c0977954 100644 --- a/include/net/tcp_authopt.h +++ b/include/net/tcp_authopt.h @@ -4,10 +4,24 @@ #include #include #include +/* According to RFC5925 the length of the authentication option varies based on + * the signature algorithm. Linux only implements the algorithms defined in + * RFC5926 which have a constant length of 16. + * + * This is used in stack allocation of tcp option buffers for output. It is + * shorter than the length of the MD5 option. + * + * Input packets can have authentication options of different lengths but they + * will always be flagged as invalid (since no such algorithms are supported). + */ +#define TCPOLEN_AUTHOPT_OUTPUT 16 + +struct tcp_authopt_alg_imp; + /** * struct tcp_authopt_key_info - Representation of a Master Key Tuple as per RFC5925 * * Key structure lifetime is protected by RCU so send/recv code needs to hold a * single rcu_read_lock until they're done with the key. @@ -33,10 +47,12 @@ struct tcp_authopt_key_info { u8 keylen; /** @key: Same as &tcp_authopt_key.key */ u8 key[TCP_AUTHOPT_MAXKEYLEN]; /** @addr: Same as &tcp_authopt_key.addr */ struct sockaddr_storage addr; + /** @alg: Algorithm implementation matching alg_id */ + struct tcp_authopt_alg_imp *alg; }; /** * struct tcp_authopt_info - Per-socket information regarding tcp_authopt * diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index 17392c42e99f..8d04ececf09f 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -2,10 +2,189 @@ #include #include #include #include +#include + +/* All current algorithms have a mac length of 12 but crypto API digestsize can be larger */ +#define TCP_AUTHOPT_MAXMACBUF 20 +#define TCP_AUTHOPT_MAX_TRAFFIC_KEY_LEN 20 +#define TCP_AUTHOPT_MACLEN 12 + +struct tcp_authopt_alg_pool { + struct crypto_ahash *tfm; + struct ahash_request *req; +}; + +/* Constant data with per-algorithm information from RFC5926 + * The "KDF" and "MAC" happen to be the same for both algorithms. + */ +struct tcp_authopt_alg_imp { + /* Name of algorithm in crypto-api */ + const char *alg_name; + /* One of the TCP_AUTHOPT_ALG_* constants from uapi */ + u8 alg_id; + /* Length of traffic key */ + u8 traffic_key_len; + + /* shared crypto_ahash */ + struct mutex init_mutex; + bool init_done; + struct tcp_authopt_alg_pool __percpu *pool; +}; + +static struct tcp_authopt_alg_imp tcp_authopt_alg_list[] = { + { + .alg_id = TCP_AUTHOPT_ALG_HMAC_SHA_1_96, + .alg_name = "hmac(sha1)", + .traffic_key_len = 20, + .init_mutex = __MUTEX_INITIALIZER(tcp_authopt_alg_list[0].init_mutex), + }, + { + .alg_id = TCP_AUTHOPT_ALG_AES_128_CMAC_96, + .alg_name = "cmac(aes)", + .traffic_key_len = 16, + .init_mutex = __MUTEX_INITIALIZER(tcp_authopt_alg_list[1].init_mutex), + }, +}; + +/* get a pointer to the tcp_authopt_alg instance or NULL if id invalid */ +static inline struct tcp_authopt_alg_imp *tcp_authopt_alg_get(int alg_num) +{ + if (alg_num <= 0 || alg_num > 2) + return NULL; + return &tcp_authopt_alg_list[alg_num - 1]; +} + +static int tcp_authopt_alg_pool_init(struct tcp_authopt_alg_imp *alg, + struct tcp_authopt_alg_pool *pool) +{ + pool->tfm = crypto_alloc_ahash(alg->alg_name, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(pool->tfm)) + return PTR_ERR(pool->tfm); + + pool->req = ahash_request_alloc(pool->tfm, GFP_ATOMIC); + if (IS_ERR(pool->req)) + return PTR_ERR(pool->req); + ahash_request_set_callback(pool->req, 0, NULL, NULL); + + return 0; +} + +static void tcp_authopt_alg_pool_free(struct tcp_authopt_alg_pool *pool) +{ + ahash_request_free(pool->req); + pool->req = NULL; + crypto_free_ahash(pool->tfm); + pool->tfm = NULL; +} + +static void __tcp_authopt_alg_free(struct tcp_authopt_alg_imp *alg) +{ + int cpu; + struct tcp_authopt_alg_pool *pool; + + if (!alg->pool) + return; + for_each_possible_cpu(cpu) { + pool = per_cpu_ptr(alg->pool, cpu); + tcp_authopt_alg_pool_free(pool); + } + free_percpu(alg->pool); + alg->pool = NULL; +} + +static int __tcp_authopt_alg_init(struct tcp_authopt_alg_imp *alg) +{ + struct tcp_authopt_alg_pool *pool; + int cpu; + int err; + + BUILD_BUG_ON(TCP_AUTHOPT_MAXMACBUF < TCPOLEN_AUTHOPT_OUTPUT); + if (WARN_ON_ONCE(alg->traffic_key_len > TCP_AUTHOPT_MAX_TRAFFIC_KEY_LEN)) + return -ENOBUFS; + + alg->pool = alloc_percpu(struct tcp_authopt_alg_pool); + if (!alg->pool) + return -ENOMEM; + for_each_possible_cpu(cpu) { + pool = per_cpu_ptr(alg->pool, cpu); + err = tcp_authopt_alg_pool_init(alg, pool); + if (err) + goto out_err; + + pool = per_cpu_ptr(alg->pool, cpu); + /* sanity checks: */ + if (WARN_ON_ONCE(crypto_ahash_digestsize(pool->tfm) != alg->traffic_key_len)) { + err = -EINVAL; + goto out_err; + } + if (WARN_ON_ONCE(crypto_ahash_digestsize(pool->tfm) > TCP_AUTHOPT_MAXMACBUF)) { + err = -EINVAL; + goto out_err; + } + } + return 0; + +out_err: + __tcp_authopt_alg_free(alg); + return err; +} + +static int tcp_authopt_alg_require(struct tcp_authopt_alg_imp *alg) +{ + int err = 0; + + mutex_lock(&alg->init_mutex); + if (alg->init_done) + goto out; + err = __tcp_authopt_alg_init(alg); + if (err) + goto out; + pr_info("initialized tcp-ao algorithm %s", alg->alg_name); + alg->init_done = true; + +out: + mutex_unlock(&alg->init_mutex); + return err; +} + +static struct tcp_authopt_alg_pool *tcp_authopt_alg_get_pool(struct tcp_authopt_alg_imp *alg) +{ + local_bh_disable(); + return this_cpu_ptr(alg->pool); +} + +static void tcp_authopt_alg_put_pool(struct tcp_authopt_alg_imp *alg, + struct tcp_authopt_alg_pool *pool) +{ + WARN_ON(pool != this_cpu_ptr(alg->pool)); + local_bh_enable(); +} + +static struct tcp_authopt_alg_pool *tcp_authopt_get_kdf_pool(struct tcp_authopt_key_info *key) +{ + return tcp_authopt_alg_get_pool(key->alg); +} + +static void tcp_authopt_put_kdf_pool(struct tcp_authopt_key_info *key, + struct tcp_authopt_alg_pool *pool) +{ + return tcp_authopt_alg_put_pool(key->alg, pool); +} + +static struct tcp_authopt_alg_pool *tcp_authopt_get_mac_pool(struct tcp_authopt_key_info *key) +{ + return tcp_authopt_alg_get_pool(key->alg); +} + +static void tcp_authopt_put_mac_pool(struct tcp_authopt_key_info *key, + struct tcp_authopt_alg_pool *pool) +{ + return tcp_authopt_alg_put_pool(key->alg, pool); +} static inline struct netns_tcp_authopt *sock_net_tcp_authopt(const struct sock *sk) { return &sock_net(sk)->tcp_authopt; } @@ -49,11 +228,10 @@ void tcp_authopt_clear(struct sock *sk) if (info) { tcp_authopt_free(sk, info); tcp_sk(sk)->authopt_info = NULL; } } - /* checks that ipv4 or ipv6 addr matches. */ static bool ipvx_addr_match(struct sockaddr_storage *a1, struct sockaddr_storage *a2) { if (a1->ss_family != a2->ss_family) @@ -206,10 +384,11 @@ int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) { struct tcp_authopt_key opt; struct tcp_authopt_info *info; struct tcp_authopt_key_info *key_info, *old_key_info; struct netns_tcp_authopt *net = sock_net_tcp_authopt(sk); + struct tcp_authopt_alg_imp *alg; int err; sock_owned_by_me(sk); if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; @@ -247,10 +426,20 @@ int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) /* Initialize tcp_authopt_info if not already set */ info = __tcp_authopt_info_get_or_create(sk); if (IS_ERR(info)) return PTR_ERR(info); + /* check the algorithm */ + alg = tcp_authopt_alg_get(opt.alg); + if (!alg) + return -EINVAL; + if (WARN_ON_ONCE(alg->alg_id != opt.alg)) + return -EINVAL; + err = tcp_authopt_alg_require(alg); + if (err) + return err; + key_info = sock_kmalloc(sk, sizeof(*key_info), GFP_KERNEL | __GFP_ZERO); if (!key_info) return -ENOMEM; mutex_lock(&net->mutex); kref_init(&key_info->ref); @@ -262,10 +451,11 @@ int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) tcp_authopt_key_del(net, old_key_info); key_info->flags = opt.flags & TCP_AUTHOPT_KEY_KNOWN_FLAGS; key_info->send_id = opt.send_id; key_info->recv_id = opt.recv_id; key_info->alg_id = opt.alg; + key_info->alg = alg; key_info->keylen = opt.keylen; memcpy(key_info->key, opt.key, opt.keylen); memcpy(&key_info->addr, &opt.addr, sizeof(key_info->addr)); hlist_add_head_rcu(&key_info->node, &net->head); mutex_unlock(&net->mutex); From patchwork Thu Dec 23 15:39:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698416 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 11E50C433FE for ; Thu, 23 Dec 2021 15:40:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349091AbhLWPk6 (ORCPT ); Thu, 23 Dec 2021 10:40:58 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33818 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349036AbhLWPkq (ORCPT ); Thu, 23 Dec 2021 10:40:46 -0500 Received: from mail-ed1-x52a.google.com (mail-ed1-x52a.google.com [IPv6:2a00:1450:4864:20::52a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CB47CC061757; Thu, 23 Dec 2021 07:40:45 -0800 (PST) Received: by mail-ed1-x52a.google.com with SMTP id b13so22889502edd.8; Thu, 23 Dec 2021 07:40:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Zwg/3jXbHrDqLr7GxjKESPVOjZiPbN+/TXSEuiw/m4o=; b=OFeSHr8AnD7N7PpPg/gICmxN4HGNzZzmdm4MbcZd+x6sBOd80fc2e7t+VHT1a4IwOc pIemen5CdWuUwZmRI/4JQG7zriVnHmLQT491xhDZkau1698At5uWLlR2ZNat6/228BaU Ek6+aIlLHCoy3219OCvrPn10Y4+jxdTM7ymhMnJoqjF/OPNSyqBD24vMJCw9EgA9MDd5 p/02LitdqCIZsVZzBZxenKrHmHSTDados7TFtvseT0LydVKtZvIY0gPczyCz4BvNimq7 dA5cA2IgIu+6YnMR40eGV7fBIvJp6QurqbO142iwUjOFcucEuVEhGtLu8zK0jYbQ6vep I5qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Zwg/3jXbHrDqLr7GxjKESPVOjZiPbN+/TXSEuiw/m4o=; b=x3/9h6GFKlheMQaX6gg8IQH1Qn3DzRqrPOhMPIyvdhZuqNU3/8oHImvQPlUIt8WKk1 UuhhCXY5jmHdkhkEtzAjST97Jc8XEZ7WpG9+jQ9HdtAVvPEy8cTpYslRYQEpb8R2qsPN a4Ga734ghdvBFafMfkArdhGXb3VBIPUtRtY01vAow/4j3pT4eXEVpo9X+uop+9UDk6mC w8H1yUbN7Iu0HQi6bVxSoCbRVB7paLxj1IYDNZQh4H7hYKyhUvXBXkUnQ/qXs4MtGthm dexD3f5/4ueFidOEe36Py95TIHxx3rgOghX2HDjm1+VD0VLZ86DRQFeOaOxMwrA2ptr+ ML5Q== X-Gm-Message-State: AOAM533VF1T/aGHFkABv7wR1n38eupf8f6O7Jx4h6t0PKyV9GD/T+afo D5n6JfqL8rZKP6nIitLKQ5M= X-Google-Smtp-Source: ABdhPJwNri+jUZzFpH/ekr+dHNBINae4hbDJIN6g6FF0ArkXPaCCcihKmjy/KEjTStqdrPL+dXJUEA== X-Received: by 2002:aa7:dc05:: with SMTP id b5mr2443480edu.46.1640274043875; Thu, 23 Dec 2021 07:40:43 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:43 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 04/19] tcp: md5: Refactor tcp_sig_hash_skb_data for AO Date: Thu, 23 Dec 2021 17:39:59 +0200 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org This chunk of code is identical between the implementation of TCP-MD5 and TCP-AO so rename and refactor Signed-off-by: Leonard Crestez --- include/net/tcp.h | 2 +- net/ipv4/tcp.c | 38 ++++++++++++++++++++------------------ net/ipv4/tcp_ipv4.c | 2 +- net/ipv6/tcp_ipv6.c | 2 +- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 6cc2eeb45deb..1a0513b0ead0 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1688,11 +1688,11 @@ struct tcp_md5sig_pool *tcp_get_md5sig_pool(void); static inline void tcp_put_md5sig_pool(void) { local_bh_enable(); } -int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *, +int tcp_sig_hash_skb_data(struct ahash_request *, const struct sk_buff *, unsigned int header_len); int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *key); /* From tcp_fastopen.c */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 022811dd705d..8e6cbb5a1da7 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4400,16 +4400,31 @@ struct tcp_md5sig_pool *tcp_get_md5sig_pool(void) local_bh_enable(); return NULL; } EXPORT_SYMBOL(tcp_get_md5sig_pool); -int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp, +int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *key) +{ + u8 keylen = READ_ONCE(key->keylen); /* paired with WRITE_ONCE() in tcp_md5_do_add */ + struct scatterlist sg; + + sg_init_one(&sg, key->key, keylen); + ahash_request_set_crypt(hp->md5_req, &sg, NULL, keylen); + + /* We use data_race() because tcp_md5_do_add() might change key->key under us */ + return data_race(crypto_ahash_update(hp->md5_req)); +} +EXPORT_SYMBOL(tcp_md5_hash_key); +#endif /* CONFIG_TCP_MD5SIG */ + +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AUTHOPT) + +int tcp_sig_hash_skb_data(struct ahash_request *req, const struct sk_buff *skb, unsigned int header_len) { struct scatterlist sg; const struct tcphdr *tp = tcp_hdr(skb); - struct ahash_request *req = hp->md5_req; unsigned int i; const unsigned int head_data_len = skb_headlen(skb) > header_len ? skb_headlen(skb) - header_len : 0; const struct skb_shared_info *shi = skb_shinfo(skb); struct sk_buff *frag_iter; @@ -4432,31 +4447,18 @@ int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp, if (crypto_ahash_update(req)) return 1; } skb_walk_frags(skb, frag_iter) - if (tcp_md5_hash_skb_data(hp, frag_iter, 0)) + if (tcp_sig_hash_skb_data(req, frag_iter, 0)) return 1; return 0; } -EXPORT_SYMBOL(tcp_md5_hash_skb_data); +EXPORT_SYMBOL(tcp_sig_hash_skb_data); -int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *key) -{ - u8 keylen = READ_ONCE(key->keylen); /* paired with WRITE_ONCE() in tcp_md5_do_add */ - struct scatterlist sg; - - sg_init_one(&sg, key->key, keylen); - ahash_request_set_crypt(hp->md5_req, &sg, NULL, keylen); - - /* We use data_race() because tcp_md5_do_add() might change key->key under us */ - return data_race(crypto_ahash_update(hp->md5_req)); -} -EXPORT_SYMBOL(tcp_md5_hash_key); - -#endif +#endif /* defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AUTHOPT) */ void tcp_done(struct sock *sk) { struct request_sock *req; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 0aa5122b29e0..91cad11db32e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1381,11 +1381,11 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key, if (crypto_ahash_init(req)) goto clear_hash; if (tcp_v4_md5_hash_headers(hp, daddr, saddr, th, skb->len)) goto clear_hash; - if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2)) + if (tcp_sig_hash_skb_data(hp->md5_req, skb, th->doff << 2)) goto clear_hash; if (tcp_md5_hash_key(hp, key)) goto clear_hash; ahash_request_set_crypt(req, NULL, md5_hash, 0); if (crypto_ahash_final(req)) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3b7d6ede1364..e98fc6f12c61 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -750,11 +750,11 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, if (crypto_ahash_init(req)) goto clear_hash; if (tcp_v6_md5_hash_headers(hp, daddr, saddr, th, skb->len)) goto clear_hash; - if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2)) + if (tcp_sig_hash_skb_data(hp->md5_req, skb, th->doff << 2)) goto clear_hash; if (tcp_md5_hash_key(hp, key)) goto clear_hash; ahash_request_set_crypt(req, NULL, md5_hash, 0); if (crypto_ahash_final(req)) From patchwork Thu Dec 23 15:40:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698417 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 30AC4C433FE for ; Thu, 23 Dec 2021 15:41:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349099AbhLWPk7 (ORCPT ); Thu, 23 Dec 2021 10:40:59 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33822 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349042AbhLWPkr (ORCPT ); Thu, 23 Dec 2021 10:40:47 -0500 Received: from mail-ed1-x530.google.com (mail-ed1-x530.google.com [IPv6:2a00:1450:4864:20::530]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 32387C061759; Thu, 23 Dec 2021 07:40:47 -0800 (PST) Received: by mail-ed1-x530.google.com with SMTP id o6so22973634edc.4; Thu, 23 Dec 2021 07:40:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ijyG4YVZrdYIGC7hCdsBdBrsDOG/QviB0lGYBejnUaE=; b=B3yWWghjNaj2TGvlc6LRGbskPGHdk1+fySeyln5DqyOsbWs2/vRMnelcd0KJ9AMJYx Aaq4ISAMWdb/jYsEJCdGFPRjzCQmeZc/VZxdJCcfSEe1V0oKME1wrFLkeeFDz09uf4e1 gGk0rTdoJVl6Leq6KZ2pQVCt9JKw3BtmEhzHFhIpkPDEL1n42yJpLpia4a9nYSCvG3QM 8yWP2ktKf5mdD8ENlvUOmJiAb9pS0x847J/IOI7s4KM/EJjjBtmwsh7De4CDl1go3UC6 NXVwdFddE//4oSUMmXBpl1Ro8mPFchdUY8dN905FK76PWAs4qksep5posOrltd+GuEfR 9Tew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ijyG4YVZrdYIGC7hCdsBdBrsDOG/QviB0lGYBejnUaE=; b=31EQrawA1p8Yne3r1DjY931ffTNouNitZK1sxDbE6YSps8oP+jXD4+0e0MYj+byKyS 88ts7R9G8epAKTFlZWOeiTWmTPf1UG995EsPW09imHTAfvNJHJq6d8VDrlK/Jr+pL/cJ wW3dhc6HachcqhUcv8MDXtewUcfC0DzfvAGpRCoso57fogtm4W8u3YkCwv9VN+rkOgMW oPWQokmntGbAn8IzUtbWzNcwyWgxRuD3NIuLQtOBfYBK+dDCTIWv8Q9dTRX2opNU7BSq b7J/ycu52xP/Yfu1fc9BsQ8TVOJax0eIIpMjHWiJjGqP5gEtpw6h1UYpEMClkzMTvoNc sYGA== X-Gm-Message-State: AOAM530fHOR3Db8JB64FiQ2SrEW4N6fEcV/KGoOAbaiId5r757g3zmd5 2+yk9A5S5QUSRLtwJNW8eLM= X-Google-Smtp-Source: ABdhPJwGLXCiRRwnoKVXrNCC8COeKwZDXUmseG8bvAkTKG1OJdC3qEelKPCoElShmKSWwqiCvlXVAQ== X-Received: by 2002:a17:907:da0:: with SMTP id go32mr2410273ejc.201.1640274045679; Thu, 23 Dec 2021 07:40:45 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:45 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 05/19] tcp: authopt: Compute packet signatures Date: Thu, 23 Dec 2021 17:40:00 +0200 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Computing tcp authopt packet signatures is a two step process: * traffic key is computed based on tcp 4-tuple, initial sequence numbers and the secret key. * packet mac is computed based on traffic key and content of individual packets. The traffic key could be cached for established sockets but it is not. A single code path exists for ipv4/ipv6 and input/output. This keeps the code short but slightly slower due to lots of conditionals. On output we read remote IP address from socket members on output, we can't use skb network header because it's computed after TCP options. On input we read remote IP address from skb network headers, we can't use socket binding members because those are not available for SYN. Signed-off-by: Leonard Crestez --- include/net/tcp_authopt.h | 9 + net/ipv4/tcp_authopt.c | 441 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 450 insertions(+) diff --git a/include/net/tcp_authopt.h b/include/net/tcp_authopt.h index bbd0c0977954..7d0a66fcde71 100644 --- a/include/net/tcp_authopt.h +++ b/include/net/tcp_authopt.h @@ -68,10 +68,19 @@ struct tcp_authopt_info { u32 src_isn; /** @dst_isn: Remote Initial Sequence Number */ u32 dst_isn; }; +/* TCP authopt as found in header */ +struct tcphdr_authopt { + u8 num; + u8 len; + u8 keyid; + u8 rnextkeyid; + u8 mac[0]; +}; + #ifdef CONFIG_TCP_AUTHOPT void tcp_authopt_clear(struct sock *sk); int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen); int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *key); int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen); diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index 8d04ececf09f..01bc12ad56f1 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -461,10 +461,451 @@ int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) mutex_unlock(&net->mutex); return 0; } +static int tcp_authopt_get_isn(struct sock *sk, + struct tcp_authopt_info *info, + struct sk_buff *skb, + int input, + __be32 *sisn, + __be32 *disn) +{ + struct tcphdr *th = tcp_hdr(skb); + + /* Special cases for SYN and SYN/ACK */ + if (th->syn && !th->ack) { + *sisn = th->seq; + *disn = 0; + return 0; + } + if (th->syn && th->ack) { + *sisn = th->seq; + *disn = htonl(ntohl(th->ack_seq) - 1); + return 0; + } + + if (sk->sk_state == TCP_NEW_SYN_RECV) { + struct tcp_request_sock *rsk = (struct tcp_request_sock *)sk; + + if (WARN_ONCE(!input, "Caller passed wrong socket")) + return -EINVAL; + *sisn = htonl(rsk->rcv_isn); + *disn = htonl(rsk->snt_isn); + return 0; + } else if (sk->sk_state == TCP_LISTEN) { + /* Signature computation for non-syn packet on a listen + * socket is not possible because we lack the initial + * sequence numbers. + * + * Input segments that are not matched by any request, + * established or timewait socket will get here. These + * are not normally sent by peers. + * + * Their signature might be valid but we don't have + * enough state to determine that. TCP-MD5 can attempt + * to validate and reply with a signed RST because it + * doesn't care about ISNs. + * + * Reporting an error from signature code causes the + * packet to be discarded which is good. + */ + if (WARN_ONCE(!input, "Caller passed wrong socket")) + return -EINVAL; + *sisn = 0; + *disn = 0; + return 0; + } + if (WARN_ONCE(!info, "caller did not pass tcp_authopt_info\n")) + return -EINVAL; + /* Initial sequence numbers for ESTABLISHED connections from info */ + if (input) { + *sisn = htonl(info->dst_isn); + *disn = htonl(info->src_isn); + } else { + *sisn = htonl(info->src_isn); + *disn = htonl(info->dst_isn); + } + return 0; +} + +/* Feed one buffer into ahash + * The buffer is assumed to be DMA-able + */ +static int crypto_ahash_buf(struct ahash_request *req, u8 *buf, uint len) +{ + struct scatterlist sg; + + sg_init_one(&sg, buf, len); + ahash_request_set_crypt(req, &sg, NULL, len); + + return crypto_ahash_update(req); +} + +/* feed traffic key into ahash */ +static int tcp_authopt_ahash_traffic_key(struct tcp_authopt_alg_pool *pool, + struct sock *sk, + struct sk_buff *skb, + struct tcp_authopt_info *info, + bool input, + bool ipv6) +{ + struct tcphdr *th = tcp_hdr(skb); + int err; + __be32 sisn, disn; + __be16 digestbits = htons(crypto_ahash_digestsize(pool->tfm) * 8); + + // RFC5926 section 3.1.1.1 + err = crypto_ahash_buf(pool->req, "\x01TCP-AO", 7); + if (err) + return err; + + /* Addresses from packet on input and from sk_common on output + * This is because on output MAC is computed before prepending IP header + */ + if (input) { + if (ipv6) + err = crypto_ahash_buf(pool->req, (u8 *)&ipv6_hdr(skb)->saddr, 32); + else + err = crypto_ahash_buf(pool->req, (u8 *)&ip_hdr(skb)->saddr, 8); + if (err) + return err; + } else { + if (ipv6) { + err = crypto_ahash_buf(pool->req, (u8 *)&sk->sk_v6_rcv_saddr, 16); + if (err) + return err; + err = crypto_ahash_buf(pool->req, (u8 *)&sk->sk_v6_daddr, 16); + if (err) + return err; + } else { + err = crypto_ahash_buf(pool->req, (u8 *)&sk->sk_rcv_saddr, 4); + if (err) + return err; + err = crypto_ahash_buf(pool->req, (u8 *)&sk->sk_daddr, 4); + if (err) + return err; + } + } + + /* TCP ports from header */ + err = crypto_ahash_buf(pool->req, (u8 *)&th->source, 4); + if (err) + return err; + err = tcp_authopt_get_isn(sk, info, skb, input, &sisn, &disn); + if (err) + return err; + err = crypto_ahash_buf(pool->req, (u8 *)&sisn, 4); + if (err) + return err; + err = crypto_ahash_buf(pool->req, (u8 *)&disn, 4); + if (err) + return err; + err = crypto_ahash_buf(pool->req, (u8 *)&digestbits, 2); + if (err) + return err; + + return 0; +} + +/* Convert a variable-length key to a 16-byte fixed-length key for AES-CMAC + * This is described in RFC5926 section 3.1.1.2 + */ +static int aes_setkey_derived(struct crypto_ahash *tfm, struct ahash_request *req, + u8 *key, size_t keylen) +{ + static const u8 zeros[16] = {0}; + struct scatterlist sg; + u8 derived_key[16]; + int err; + + if (WARN_ON_ONCE(crypto_ahash_digestsize(tfm) != sizeof(derived_key))) + return -EINVAL; + err = crypto_ahash_setkey(tfm, zeros, sizeof(zeros)); + if (err) + return err; + err = crypto_ahash_init(req); + if (err) + return err; + sg_init_one(&sg, key, keylen); + ahash_request_set_crypt(req, &sg, derived_key, keylen); + err = crypto_ahash_digest(req); + if (err) + return err; + return crypto_ahash_setkey(tfm, derived_key, sizeof(derived_key)); +} + +static int tcp_authopt_setkey(struct tcp_authopt_alg_pool *pool, struct tcp_authopt_key_info *key) +{ + if (key->alg_id == TCP_AUTHOPT_ALG_AES_128_CMAC_96 && key->keylen != 16) + return aes_setkey_derived(pool->tfm, pool->req, key->key, key->keylen); + else + return crypto_ahash_setkey(pool->tfm, key->key, key->keylen); +} + +static int tcp_authopt_get_traffic_key(struct sock *sk, + struct sk_buff *skb, + struct tcp_authopt_key_info *key, + struct tcp_authopt_info *info, + bool input, + bool ipv6, + u8 *traffic_key) +{ + struct tcp_authopt_alg_pool *pool; + int err; + + pool = tcp_authopt_get_kdf_pool(key); + if (IS_ERR(pool)) + return PTR_ERR(pool); + + err = tcp_authopt_setkey(pool, key); + if (err) + goto out; + err = crypto_ahash_init(pool->req); + if (err) + goto out; + + err = tcp_authopt_ahash_traffic_key(pool, sk, skb, info, input, ipv6); + if (err) + goto out; + + ahash_request_set_crypt(pool->req, NULL, traffic_key, 0); + err = crypto_ahash_final(pool->req); + if (err) + return err; + +out: + tcp_authopt_put_kdf_pool(key, pool); + return err; +} + +static int crypto_ahash_buf_zero(struct ahash_request *req, int len) +{ + u8 zeros[TCP_AUTHOPT_MACLEN] = {0}; + int buflen, err; + + /* In practice this is always called with len exactly 12. + * Even on input we drop unusual signature sizes early. + */ + while (len) { + buflen = min_t(int, len, sizeof(zeros)); + err = crypto_ahash_buf(req, zeros, buflen); + if (err) + return err; + len -= buflen; + } + + return 0; +} + +static int tcp_authopt_hash_tcp4_pseudoheader(struct tcp_authopt_alg_pool *pool, + __be32 saddr, + __be32 daddr, + int nbytes) +{ + struct tcp4_pseudohdr phdr = { + .saddr = saddr, + .daddr = daddr, + .pad = 0, + .protocol = IPPROTO_TCP, + .len = htons(nbytes) + }; + return crypto_ahash_buf(pool->req, (u8 *)&phdr, sizeof(phdr)); +} + +static int tcp_authopt_hash_tcp6_pseudoheader(struct tcp_authopt_alg_pool *pool, + struct in6_addr *saddr, + struct in6_addr *daddr, + u32 plen) +{ + int err; + __be32 buf[2]; + + buf[0] = htonl(plen); + buf[1] = htonl(IPPROTO_TCP); + + err = crypto_ahash_buf(pool->req, (u8 *)saddr, sizeof(*saddr)); + if (err) + return err; + err = crypto_ahash_buf(pool->req, (u8 *)daddr, sizeof(*daddr)); + if (err) + return err; + return crypto_ahash_buf(pool->req, (u8 *)&buf, sizeof(buf)); +} + +/** Hash tcphdr options. + * + * If include_options is false then only the TCPOPT_AUTHOPT option itself is hashed + * Point to AO inside TH is passed by the caller + */ +static int tcp_authopt_hash_opts(struct tcp_authopt_alg_pool *pool, + struct tcphdr *th, + struct tcphdr_authopt *aoptr, + bool include_options) +{ + int err; + /* start of options */ + u8 *tcp_opts = (u8 *)(th + 1); + /* start of options */ + u8 *aobuf = (u8 *)aoptr; + u8 aolen = aoptr->len; + + if (WARN_ONCE(aoptr->num != TCPOPT_AUTHOPT, "Bad aoptr\n")) + return -EINVAL; + + if (include_options) { + /* end of options */ + u8 *tcp_data = ((u8 *)th) + th->doff * 4; + + err = crypto_ahash_buf(pool->req, tcp_opts, aobuf - tcp_opts + 4); + if (err) + return err; + err = crypto_ahash_buf_zero(pool->req, aolen - 4); + if (err) + return err; + err = crypto_ahash_buf(pool->req, aobuf + aolen, tcp_data - (aobuf + aolen)); + if (err) + return err; + } else { + err = crypto_ahash_buf(pool->req, aobuf, 4); + if (err) + return err; + err = crypto_ahash_buf_zero(pool->req, aolen - 4); + if (err) + return err; + } + + return 0; +} + +static int tcp_authopt_hash_packet(struct tcp_authopt_alg_pool *pool, + struct sock *sk, + struct sk_buff *skb, + struct tcphdr_authopt *aoptr, + bool input, + bool ipv6, + bool include_options, + u8 *macbuf) +{ + struct tcphdr *th = tcp_hdr(skb); + int err; + + /* NOTE: SNE unimplemented */ + __be32 sne = 0; + + err = crypto_ahash_init(pool->req); + if (err) + return err; + + err = crypto_ahash_buf(pool->req, (u8 *)&sne, 4); + if (err) + return err; + + if (ipv6) { + struct in6_addr *saddr; + struct in6_addr *daddr; + + if (input) { + saddr = &ipv6_hdr(skb)->saddr; + daddr = &ipv6_hdr(skb)->daddr; + } else { + saddr = &sk->sk_v6_rcv_saddr; + daddr = &sk->sk_v6_daddr; + } + err = tcp_authopt_hash_tcp6_pseudoheader(pool, saddr, daddr, skb->len); + if (err) + return err; + } else { + __be32 saddr; + __be32 daddr; + + if (input) { + saddr = ip_hdr(skb)->saddr; + daddr = ip_hdr(skb)->daddr; + } else { + saddr = sk->sk_rcv_saddr; + daddr = sk->sk_daddr; + } + err = tcp_authopt_hash_tcp4_pseudoheader(pool, saddr, daddr, skb->len); + if (err) + return err; + } + + // TCP header with checksum set to zero + { + struct tcphdr hashed_th = *th; + + hashed_th.check = 0; + err = crypto_ahash_buf(pool->req, (u8 *)&hashed_th, sizeof(hashed_th)); + if (err) + return err; + } + + // TCP options + err = tcp_authopt_hash_opts(pool, th, aoptr, include_options); + if (err) + return err; + + // Rest of SKB->data + err = tcp_sig_hash_skb_data(pool->req, skb, th->doff << 2); + if (err) + return err; + + ahash_request_set_crypt(pool->req, NULL, macbuf, 0); + return crypto_ahash_final(pool->req); +} + +/* __tcp_authopt_calc_mac - Compute packet MAC using key + * + * The macbuf output buffer must be large enough to fit the digestsize of the + * underlying transform before truncation. + * This means TCP_AUTHOPT_MAXMACBUF, not TCP_AUTHOPT_MACLEN + */ +static int __tcp_authopt_calc_mac(struct sock *sk, + struct sk_buff *skb, + struct tcphdr_authopt *aoptr, + struct tcp_authopt_key_info *key, + struct tcp_authopt_info *info, + bool input, + char *macbuf) +{ + struct tcp_authopt_alg_pool *mac_pool; + u8 traffic_key[TCP_AUTHOPT_MAX_TRAFFIC_KEY_LEN]; + int err; + bool ipv6 = (sk->sk_family != AF_INET); + + if (sk->sk_family != AF_INET && sk->sk_family != AF_INET6) + return -EINVAL; + + err = tcp_authopt_get_traffic_key(sk, skb, key, info, input, ipv6, traffic_key); + if (err) + return err; + + mac_pool = tcp_authopt_get_mac_pool(key); + if (IS_ERR(mac_pool)) + return PTR_ERR(mac_pool); + err = crypto_ahash_setkey(mac_pool->tfm, traffic_key, key->alg->traffic_key_len); + if (err) + goto out; + err = crypto_ahash_init(mac_pool->req); + if (err) + return err; + + err = tcp_authopt_hash_packet(mac_pool, + sk, + skb, + aoptr, + input, + ipv6, + !(key->flags & TCP_AUTHOPT_KEY_EXCLUDE_OPTS), + macbuf); + +out: + tcp_authopt_put_mac_pool(key, mac_pool); + return err; +} + static int tcp_authopt_init_net(struct net *full_net) { struct netns_tcp_authopt *net = &full_net->tcp_authopt; mutex_init(&net->mutex); From patchwork Thu Dec 23 15:40:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698418 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AD120C4321E for ; Thu, 23 Dec 2021 15:41:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349109AbhLWPlA (ORCPT ); Thu, 23 Dec 2021 10:41:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33840 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349053AbhLWPku (ORCPT ); Thu, 23 Dec 2021 10:40:50 -0500 Received: from mail-ed1-x529.google.com (mail-ed1-x529.google.com [IPv6:2a00:1450:4864:20::529]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 78923C06175D; Thu, 23 Dec 2021 07:40:49 -0800 (PST) Received: by mail-ed1-x529.google.com with SMTP id b13so22890309edd.8; Thu, 23 Dec 2021 07:40:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=/x/PpeZKrQrS0slQwQGqxQBtuxghwfMqUx1IqVwqbsA=; b=aKadfcMkjgnEfFOLiQ/gv2Mly/z1cd/8jL+fsCxWrB4FGidWR+fPyifPLMTM3edvjo v2JYAOtIOKeF4cYc4n9isrdr/CAcbrb68lqKBfQFWy4ZfO7wqtPs+nXjan/HU1A8yeAT vn/zJBARa4DU+kAECV2TDRLzu2gVTIEMFKMtqsGjiLMJvMkcMZnA7ThRaSbxD+XZQHen zsHBgOrekO8RgJbrNc0DQXNlbFPjNqcKCs5x0mPlYC1NGGUAaXtn1aasd+icYogAr+4D 5L2O6/4Ca1WwuJ2nA2KGkNHhXokmsRS4NmJNCnvnEWTTZrWEZOWJRLl3eq41Yc6HIkk8 7/zg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=/x/PpeZKrQrS0slQwQGqxQBtuxghwfMqUx1IqVwqbsA=; b=Ii6eIljKHkTa0fAmYzjKmzYJ3izyUv/quIvIXA4lBA3nC74z3yahAHEsMo4Tn0sAQl Efpv5399ItxynbE9CyEfWVB3N5eaXe7rxVXyXxzzZrzpR94S8oB38duNKkgKn0KODxIC AAXi2w8zsfBdrKhh7uz/2AhPjyHExxgCciO/fQryEP4N2BmccIayrr8ddQMammgfI4kn hHCK5YuOTcLBycHNcj/DgKceD6DHp/0kH5FrlLxhv+ZUlnTuVsjFb8C6eS8kOVKsnDVH AGAx4AkuMeqToa4MuBQdTmsolfKbkcfcfNbWUaZaWG0iYLHRudYTqHOlC3vAcvpMeTQI StGg== X-Gm-Message-State: AOAM531qaUY+e+Tdq/oCb3hMy4PGa82bIQcpbtJOp+N/6zoRiWZAFQh0 WN6QBlngSiA/iXb84tz2Jzk= X-Google-Smtp-Source: ABdhPJxZ4snSAEnyl6UmdWqcXHRqKDPdXy8gllr6Ovdqi9YivSkRdCGWEx32C+OFD65TMLRqTjKsuQ== X-Received: by 2002:a17:906:9753:: with SMTP id o19mr2339041ejy.243.1640274047573; Thu, 23 Dec 2021 07:40:47 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:47 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 06/19] tcp: authopt: Hook into tcp core Date: Thu, 23 Dec 2021 17:40:01 +0200 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org The tcp_authopt features exposes a minimal interface to the rest of the TCP stack. Only a few functions are exposed and if the feature is disabled they return neutral values, avoiding ifdefs in the rest of the code. This approach is different from MD5. There very few interactions with MD5 but tcp_parse_md5sig_option was modifed to parse AO and MD5 simultaneously. If both are present the packet is droppped as required by RFC5925. Add calls into tcp authopt from send, receive and accept code. Signed-off-by: Leonard Crestez --- include/net/tcp.h | 24 +++- include/net/tcp_authopt.h | 136 +++++++++++++++++++ include/uapi/linux/snmp.h | 1 + net/ipv4/proc.c | 1 + net/ipv4/tcp_authopt.c | 270 ++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_input.c | 40 ++++-- net/ipv4/tcp_ipv4.c | 50 ++++++- net/ipv4/tcp_minisocks.c | 12 ++ net/ipv4/tcp_output.c | 85 +++++++++++- net/ipv6/tcp_ipv6.c | 49 ++++++- 10 files changed, 643 insertions(+), 25 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 1a0513b0ead0..9d22d541fad1 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -422,11 +422,33 @@ int tcp_mmap(struct file *file, struct socket *sock, struct vm_area_struct *vma); #endif void tcp_parse_options(const struct net *net, const struct sk_buff *skb, struct tcp_options_received *opt_rx, int estab, struct tcp_fastopen_cookie *foc); -const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AUTHOPT) +int tcp_parse_sig_options(const struct tcphdr *th, + const u8 **md5ptr, + const u8 **aoptr); +#else +static inline int tcp_parse_sig_options(const struct tcphdr *th, + const u8 **md5ptr, + const u8 **aoptr) +{ + *aoptr = NULL; + *md5ptr = NULL; + return 0; +} +#endif +static inline const u8 *tcp_parse_md5sig_option(const struct tcphdr *th) +{ + const u8 *md5, *ao; + int ret; + + ret = tcp_parse_sig_options(th, &md5, &ao); + + return (md5 && !ao && !ret) ? md5 : NULL; +} /* * BPF SKB-less helpers */ u16 tcp_v4_get_syncookie(struct sock *sk, struct iphdr *iph, diff --git a/include/net/tcp_authopt.h b/include/net/tcp_authopt.h index 7d0a66fcde71..7096e3ad59a6 100644 --- a/include/net/tcp_authopt.h +++ b/include/net/tcp_authopt.h @@ -78,28 +78,164 @@ struct tcphdr_authopt { u8 rnextkeyid; u8 mac[0]; }; #ifdef CONFIG_TCP_AUTHOPT +DECLARE_STATIC_KEY_FALSE(tcp_authopt_needed_key); +#define tcp_authopt_needed (static_branch_unlikely(&tcp_authopt_needed_key)) + +void tcp_authopt_free(struct sock *sk, struct tcp_authopt_info *info); void tcp_authopt_clear(struct sock *sk); int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen); int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *key); int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen); +struct tcp_authopt_key_info *__tcp_authopt_select_key( + const struct sock *sk, + struct tcp_authopt_info *info, + const struct sock *addr_sk, + u8 *rnextkeyid); +static inline struct tcp_authopt_key_info *tcp_authopt_select_key( + const struct sock *sk, + const struct sock *addr_sk, + struct tcp_authopt_info **info, + u8 *rnextkeyid) +{ + if (tcp_authopt_needed) { + *info = rcu_dereference(tcp_sk(sk)->authopt_info); + + if (*info) + return __tcp_authopt_select_key(sk, *info, addr_sk, rnextkeyid); + } + return NULL; +} +int tcp_authopt_hash( + char *hash_location, + struct tcp_authopt_key_info *key, + struct tcp_authopt_info *info, + struct sock *sk, struct sk_buff *skb); +int __tcp_authopt_openreq(struct sock *newsk, const struct sock *oldsk, struct request_sock *req); +static inline int tcp_authopt_openreq( + struct sock *newsk, + const struct sock *oldsk, + struct request_sock *req) +{ + if (!rcu_dereference(tcp_sk(oldsk)->authopt_info)) + return 0; + else + return __tcp_authopt_openreq(newsk, oldsk, req); +} +void __tcp_authopt_finish_connect(struct sock *sk, struct sk_buff *skb, + struct tcp_authopt_info *info); +static inline void tcp_authopt_finish_connect(struct sock *sk, struct sk_buff *skb) +{ + struct tcp_authopt_info *info; + + if (tcp_authopt_needed) { + info = rcu_dereference_protected(tcp_sk(sk)->authopt_info, + lockdep_sock_is_held(sk)); + + if (info) + __tcp_authopt_finish_connect(sk, skb, info); + } +} +static inline void tcp_authopt_time_wait( + struct tcp_timewait_sock *tcptw, + struct tcp_sock *tp) +{ + if (tcp_authopt_needed) { + /* Transfer ownership of authopt_info to the twsk + * This requires no other users of the origin sock. + */ + tcptw->tw_authopt_info = rcu_dereference_protected( + tp->authopt_info, + lockdep_sock_is_held((struct sock *)tp)); + rcu_assign_pointer(tp->authopt_info, NULL); + } else { + tcptw->tw_authopt_info = NULL; + } +} +/** tcp_authopt_inbound_check - check for valid TCP-AO signature. + * + * Return negative ERRNO on error, 0 if not present and 1 if present and valid. + * + * If the AO signature is present and valid then caller skips MD5 check. + */ +int __tcp_authopt_inbound_check( + struct sock *sk, + struct sk_buff *skb, + struct tcp_authopt_info *info, + const u8 *opt); +static inline int tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb, const u8 *opt) +{ + if (tcp_authopt_needed) { + struct tcp_authopt_info *info = rcu_dereference(tcp_sk(sk)->authopt_info); + + if (info) + return __tcp_authopt_inbound_check(sk, skb, info, opt); + } + return 0; +} +static inline int tcp_authopt_inbound_check_req(struct request_sock *req, struct sk_buff *skb, + const u8 *opt) +{ + if (tcp_authopt_needed) { + struct sock *lsk = req->rsk_listener; + struct tcp_authopt_info *info = rcu_dereference(tcp_sk(lsk)->authopt_info); + + if (info) + return __tcp_authopt_inbound_check((struct sock *)req, skb, info, opt); + } + return 0; +} #else static inline int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen) { return -ENOPROTOOPT; } static inline int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *key) { return -ENOPROTOOPT; } +static inline void tcp_authopt_free(struct sock *sk, struct tcp_authopt_info *info) +{ +} static inline void tcp_authopt_clear(struct sock *sk) { } static inline int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) { return -ENOPROTOOPT; } +static inline int tcp_authopt_hash( + char *hash_location, + struct tcp_authopt_key_info *key, + struct tcp_authopt_key *info, + struct sock *sk, struct sk_buff *skb) +{ + return -EINVAL; +} +static inline int tcp_authopt_openreq(struct sock *newsk, + const struct sock *oldsk, + struct request_sock *req) +{ + return 0; +} +static inline void tcp_authopt_finish_connect(struct sock *sk, struct sk_buff *skb) +{ +} +static inline void tcp_authopt_time_wait( + struct tcp_timewait_sock *tcptw, + struct tcp_sock *tp) +{ +} +static inline int tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb, const u8 *opt) +{ + return 0; +} +static inline int tcp_authopt_inbound_check_req(struct request_sock *sk, struct sk_buff *skb, + const u8 *opt) +{ + return 0; +} #endif #endif /* _LINUX_TCP_AUTHOPT_H */ diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index 904909d020e2..1d96030889a1 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -290,10 +290,11 @@ enum LINUX_MIB_TCPDUPLICATEDATAREHASH, /* TCPDuplicateDataRehash */ LINUX_MIB_TCPDSACKRECVSEGS, /* TCPDSACKRecvSegs */ LINUX_MIB_TCPDSACKIGNOREDDUBIOUS, /* TCPDSACKIgnoredDubious */ LINUX_MIB_TCPMIGRATEREQSUCCESS, /* TCPMigrateReqSuccess */ LINUX_MIB_TCPMIGRATEREQFAILURE, /* TCPMigrateReqFailure */ + LINUX_MIB_TCPAUTHOPTFAILURE, /* TCPAuthOptFailure */ __LINUX_MIB_MAX }; /* linux Xfrm mib definitions */ enum diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index f30273afb539..70f7a8a47045 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -295,10 +295,11 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TcpDuplicateDataRehash", LINUX_MIB_TCPDUPLICATEDATAREHASH), SNMP_MIB_ITEM("TCPDSACKRecvSegs", LINUX_MIB_TCPDSACKRECVSEGS), SNMP_MIB_ITEM("TCPDSACKIgnoredDubious", LINUX_MIB_TCPDSACKIGNOREDDUBIOUS), SNMP_MIB_ITEM("TCPMigrateReqSuccess", LINUX_MIB_TCPMIGRATEREQSUCCESS), SNMP_MIB_ITEM("TCPMigrateReqFailure", LINUX_MIB_TCPMIGRATEREQFAILURE), + SNMP_MIB_ITEM("TCPAuthOptFailure", LINUX_MIB_TCPAUTHOPTFAILURE), SNMP_MIB_SENTINEL }; static void icmpmsg_put_line(struct seq_file *seq, unsigned long *vals, unsigned short *type, int count) diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index 01bc12ad56f1..ba20a70359ee 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -4,10 +4,14 @@ #include #include #include #include +/* This is enabled when first struct tcp_authopt_info is allocated and never released */ +DEFINE_STATIC_KEY_FALSE(tcp_authopt_needed_key); +EXPORT_SYMBOL(tcp_authopt_needed_key); + /* All current algorithms have a mac length of 12 but crypto API digestsize can be larger */ #define TCP_AUTHOPT_MAXMACBUF 20 #define TCP_AUTHOPT_MAX_TRAFFIC_KEY_LEN 20 #define TCP_AUTHOPT_MACLEN 12 @@ -261,10 +265,55 @@ static bool tcp_authopt_key_match_exact(struct tcp_authopt_key_info *info, return false; return true; } +static bool tcp_authopt_key_match_skb_addr(struct tcp_authopt_key_info *key, + struct sk_buff *skb) +{ + u16 keyaf = key->addr.ss_family; + struct iphdr *iph = (struct iphdr *)skb_network_header(skb); + + if (keyaf == AF_INET && iph->version == 4) { + struct sockaddr_in *key_addr = (struct sockaddr_in *)&key->addr; + + return iph->saddr == key_addr->sin_addr.s_addr; + } else if (keyaf == AF_INET6 && iph->version == 6) { + struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb); + struct sockaddr_in6 *key_addr = (struct sockaddr_in6 *)&key->addr; + + return ipv6_addr_equal(&ip6h->saddr, &key_addr->sin6_addr); + } + + /* This actually happens with ipv6-mapped-ipv4-addresses + * IPv6 listen sockets will be asked to validate ipv4 packets. + */ + return false; +} + +static bool tcp_authopt_key_match_sk_addr(struct tcp_authopt_key_info *key, + const struct sock *addr_sk) +{ + u16 keyaf = key->addr.ss_family; + + /* This probably can't happen even with ipv4-mapped-ipv6 */ + if (keyaf != addr_sk->sk_family) + return false; + + if (keyaf == AF_INET) { + struct sockaddr_in *key_addr = (struct sockaddr_in *)&key->addr; + + return addr_sk->sk_daddr == key_addr->sin_addr.s_addr; + } else if (keyaf == AF_INET6) { + struct sockaddr_in6 *key_addr = (struct sockaddr_in6 *)&key->addr; + + return ipv6_addr_equal(&addr_sk->sk_v6_daddr, &key_addr->sin6_addr); + } + + return false; +} + static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct sock *sk, struct netns_tcp_authopt *net, struct tcp_authopt_key *ukey) { struct tcp_authopt_key_info *key_info; @@ -274,10 +323,52 @@ static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct so return key_info; return NULL; } +static struct tcp_authopt_key_info *tcp_authopt_lookup_send(struct netns_tcp_authopt *net, + const struct sock *addr_sk, + int send_id) +{ + struct tcp_authopt_key_info *result = NULL; + struct tcp_authopt_key_info *key; + + hlist_for_each_entry_rcu(key, &net->head, node, 0) { + if (send_id >= 0 && key->send_id != send_id) + continue; + if (key->flags & TCP_AUTHOPT_KEY_ADDR_BIND) + if (!tcp_authopt_key_match_sk_addr(key, addr_sk)) + continue; + if (result && net_ratelimit()) + pr_warn("ambiguous tcp authentication keys configured for send\n"); + result = key; + } + + return result; +} + +/** + * __tcp_authopt_select_key - select key for sending + * + * @sk: socket + * @info: socket's tcp_authopt_info + * @addr_sk: socket used for address lookup. Same as sk except for synack case + * @rnextkeyid: value of rnextkeyid caller should write in packet + * + * Result is protected by RCU and can't be stored, it may only be passed to + * tcp_authopt_hash and only under a single rcu_read_lock. + */ +struct tcp_authopt_key_info *__tcp_authopt_select_key(const struct sock *sk, + struct tcp_authopt_info *info, + const struct sock *addr_sk, + u8 *rnextkeyid) +{ + struct netns_tcp_authopt *net = sock_net_tcp_authopt(sk); + + return tcp_authopt_lookup_send(net, addr_sk, -1); +} + static struct tcp_authopt_info *__tcp_authopt_info_get_or_create(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); struct tcp_authopt_info *info; @@ -287,10 +378,12 @@ static struct tcp_authopt_info *__tcp_authopt_info_get_or_create(struct sock *sk info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return ERR_PTR(-ENOMEM); + /* Never released: */ + static_branch_inc(&tcp_authopt_needed_key); sk_gso_disable(sk); rcu_assign_pointer(tp->authopt_info, info); return info; } @@ -539,10 +632,45 @@ static int crypto_ahash_buf(struct ahash_request *req, u8 *buf, uint len) ahash_request_set_crypt(req, &sg, NULL, len); return crypto_ahash_update(req); } +/** Called to create accepted sockets. + * + * Need to copy authopt info from listen socket. + */ +int __tcp_authopt_openreq(struct sock *newsk, const struct sock *oldsk, struct request_sock *req) +{ + struct tcp_authopt_info *old_info; + struct tcp_authopt_info *new_info; + + old_info = rcu_dereference(tcp_sk(oldsk)->authopt_info); + if (!old_info) + return 0; + + /* Clear value copies from oldsk: */ + rcu_assign_pointer(tcp_sk(newsk)->authopt_info, NULL); + + new_info = kzalloc(sizeof(*new_info), GFP_ATOMIC); + if (!new_info) + return -ENOMEM; + + new_info->src_isn = tcp_rsk(req)->snt_isn; + new_info->dst_isn = tcp_rsk(req)->rcv_isn; + sk_gso_disable(newsk); + rcu_assign_pointer(tcp_sk(newsk)->authopt_info, new_info); + + return 0; +} + +void __tcp_authopt_finish_connect(struct sock *sk, struct sk_buff *skb, + struct tcp_authopt_info *info) +{ + info->src_isn = ntohl(tcp_hdr(skb)->ack_seq) - 1; + info->dst_isn = ntohl(tcp_hdr(skb)->seq); +} + /* feed traffic key into ahash */ static int tcp_authopt_ahash_traffic_key(struct tcp_authopt_alg_pool *pool, struct sock *sk, struct sk_buff *skb, struct tcp_authopt_info *info, @@ -779,10 +907,11 @@ static int tcp_authopt_hash_opts(struct tcp_authopt_alg_pool *pool, static int tcp_authopt_hash_packet(struct tcp_authopt_alg_pool *pool, struct sock *sk, struct sk_buff *skb, struct tcphdr_authopt *aoptr, + struct tcp_authopt_info *info, bool input, bool ipv6, bool include_options, u8 *macbuf) { @@ -892,20 +1021,161 @@ static int __tcp_authopt_calc_mac(struct sock *sk, err = tcp_authopt_hash_packet(mac_pool, sk, skb, aoptr, + info, input, ipv6, !(key->flags & TCP_AUTHOPT_KEY_EXCLUDE_OPTS), macbuf); out: tcp_authopt_put_mac_pool(key, mac_pool); return err; } +/* tcp_authopt_hash - fill in the mac + * + * The key must come from tcp_authopt_select_key. + */ +int tcp_authopt_hash(char *hash_location, + struct tcp_authopt_key_info *key, + struct tcp_authopt_info *info, + struct sock *sk, + struct sk_buff *skb) +{ + /* MAC inside option is truncated to 12 bytes but crypto API needs output + * buffer to be large enough so we use a buffer on the stack. + */ + u8 macbuf[TCP_AUTHOPT_MAXMACBUF]; + int err; + struct tcphdr_authopt *aoptr = (struct tcphdr_authopt *)(hash_location - 4); + + err = __tcp_authopt_calc_mac(sk, skb, aoptr, key, info, false, macbuf); + if (err) + goto fail; + memcpy(hash_location, macbuf, TCP_AUTHOPT_MACLEN); + + return 0; + +fail: + /* If mac calculation fails and caller doesn't handle the error + * try to make it obvious inside the packet. + */ + memset(hash_location, 0, TCP_AUTHOPT_MACLEN); + return err; +} + +static struct tcp_authopt_key_info *tcp_authopt_lookup_recv(struct sock *sk, + struct sk_buff *skb, + struct netns_tcp_authopt *net, + int recv_id, + bool *anykey) +{ + struct tcp_authopt_key_info *result = NULL; + struct tcp_authopt_key_info *key; + + *anykey = false; + /* multiple matches will cause occasional failures */ + hlist_for_each_entry_rcu(key, &net->head, node, 0) { + if (key->flags & TCP_AUTHOPT_KEY_ADDR_BIND && + !tcp_authopt_key_match_skb_addr(key, skb)) + continue; + *anykey = true; + if (recv_id >= 0 && key->recv_id != recv_id) + continue; + if (!result) + result = key; + else if (result) + net_warn_ratelimited("ambiguous tcp authentication keys configured for recv\n"); + } + + return result; +} + +/* Show a rate-limited message for authentication fail */ +static void print_tcpao_notice(const char *msg, struct sk_buff *skb) +{ + struct iphdr *iph = (struct iphdr *)skb_network_header(skb); + struct tcphdr *th = (struct tcphdr *)skb_transport_header(skb); + + if (iph->version == 4) { + net_info_ratelimited("%s (%pI4, %d)->(%pI4, %d)\n", msg, + &iph->saddr, ntohs(th->source), + &iph->daddr, ntohs(th->dest)); + } else if (iph->version == 6) { + struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb); + + net_info_ratelimited("%s (%pI6, %d)->(%pI6, %d)\n", msg, + &ip6h->saddr, ntohs(th->source), + &ip6h->daddr, ntohs(th->dest)); + } else { + WARN_ONCE(1, "%s unknown IP version\n", msg); + } +} + +int __tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb, + struct tcp_authopt_info *info, const u8 *_opt) +{ + struct netns_tcp_authopt *net = sock_net_tcp_authopt(sk); + struct tcphdr_authopt *opt = (struct tcphdr_authopt *)_opt; + struct tcp_authopt_key_info *key; + bool anykey; + u8 macbuf[TCP_AUTHOPT_MAXMACBUF]; + int err; + + key = tcp_authopt_lookup_recv(sk, skb, net, opt ? opt->keyid : -1, &anykey); + + /* nothing found or expected */ + if (!opt && !key) + return 0; + if (!opt && key) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTHOPTFAILURE); + print_tcpao_notice("TCP Authentication Missing", skb); + return -EINVAL; + } + if (opt && !anykey) { + /* RFC5925 Section 7.3: + * A TCP-AO implementation MUST allow for configuration of the behavior + * of segments with TCP-AO but that do not match an MKT. The initial + * default of this configuration SHOULD be to silently accept such + * connections. + */ + if (info->flags & TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTHOPTFAILURE); + print_tcpao_notice("TCP Authentication Unexpected: Rejected", skb); + return -EINVAL; + } + print_tcpao_notice("TCP Authentication Unexpected: Accepted", skb); + return 0; + } + if (opt && !key) { + /* Keys are configured for peer but with different keyid than packet */ + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTHOPTFAILURE); + print_tcpao_notice("TCP Authentication Failed", skb); + return -EINVAL; + } + + /* bad inbound key len */ + if (opt->len != TCPOLEN_AUTHOPT_OUTPUT) + return -EINVAL; + + err = __tcp_authopt_calc_mac(sk, skb, opt, key, info, true, macbuf); + if (err) + return err; + + if (memcmp(macbuf, opt->mac, TCP_AUTHOPT_MACLEN)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTHOPTFAILURE); + print_tcpao_notice("TCP Authentication Failed", skb); + return -EINVAL; + } + + return 1; +} +EXPORT_SYMBOL(__tcp_authopt_inbound_check); + static int tcp_authopt_init_net(struct net *full_net) { struct netns_tcp_authopt *net = &full_net->tcp_authopt; mutex_init(&net->mutex); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 3658b9c3dd2b..4c9e403971fb 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -70,10 +70,11 @@ #include #include #include #include #include +#include #include #include #include #include #include @@ -4173,43 +4174,60 @@ static bool tcp_fast_parse_options(const struct net *net, tp->rx_opt.rcv_tsecr -= tp->tsoffset; return true; } -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AUTHOPT) /* - * Parse MD5 Signature option + * Parse MD5 and AO options + * + * md5ptr: pointer to content of MD5 option (16-byte hash) + * aoptr: pointer to start of AO option (variable length) */ -const u8 *tcp_parse_md5sig_option(const struct tcphdr *th) +int tcp_parse_sig_options(const struct tcphdr *th, const u8 **md5ptr, const u8 **aoptr) { int length = (th->doff << 2) - sizeof(*th); const u8 *ptr = (const u8 *)(th + 1); + *md5ptr = NULL; + *aoptr = NULL; + /* If not enough data remaining, we can short cut */ - while (length >= TCPOLEN_MD5SIG) { + while (length >= 4) { int opcode = *ptr++; int opsize; switch (opcode) { case TCPOPT_EOL: - return NULL; + goto out; case TCPOPT_NOP: length--; continue; default: opsize = *ptr++; if (opsize < 2 || opsize > length) - return NULL; - if (opcode == TCPOPT_MD5SIG) - return opsize == TCPOLEN_MD5SIG ? ptr : NULL; + goto out; + if (opcode == TCPOPT_MD5SIG && opsize == TCPOLEN_MD5SIG) + *md5ptr = ptr; + if (opcode == TCPOPT_AUTHOPT) + *aoptr = ptr - 2; } ptr += opsize - 2; length -= opsize; } - return NULL; + +out: + /* RFC5925 2.2: An endpoint MUST NOT use TCP-AO for the same connection + * in which TCP MD5 is used. When both options appear, TCP MUST silently + * discard the segment. + */ + if (*md5ptr && *aoptr) + return -EINVAL; + + return 0; } -EXPORT_SYMBOL(tcp_parse_md5sig_option); +EXPORT_SYMBOL(tcp_parse_sig_options); #endif /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM * * It is not fatal. If this ACK does _not_ change critical state (seqs, window) @@ -5992,10 +6010,12 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) struct inet_connection_sock *icsk = inet_csk(sk); tcp_set_state(sk, TCP_ESTABLISHED); icsk->icsk_ack.lrcvtime = tcp_jiffies32; + tcp_authopt_finish_connect(sk, skb); + if (skb) { icsk->icsk_af_ops->sk_rx_dst_set(sk, skb); security_inet_conn_established(sk, skb); sk_mark_napi_id(sk, skb); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 91cad11db32e..b16f263c3121 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1405,22 +1405,20 @@ EXPORT_SYMBOL(tcp_v4_md5_hash_skb); #endif /* Called with rcu_read_lock() */ static bool tcp_v4_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, - int dif, int sdif) + int dif, int sdif, + const u8 *hash_location) { #ifdef CONFIG_TCP_MD5SIG /* - * This gets called for each TCP segment that arrives - * so we want to be efficient. * We have 3 drop cases: * o No MD5 hash and one expected. * o MD5 hash and we're not expecting one. * o MD5 hash and its wrong. */ - const __u8 *hash_location = NULL; struct tcp_md5sig_key *hash_expected; const struct iphdr *iph = ip_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); const union tcp_md5_addr *addr; unsigned char newhash[16]; @@ -1431,11 +1429,10 @@ static bool tcp_v4_inbound_md5_hash(const struct sock *sk, */ l3index = sdif ? dif : 0; addr = (union tcp_md5_addr *)&iph->saddr; hash_expected = tcp_md5_do_lookup(sk, l3index, addr, AF_INET); - hash_location = tcp_parse_md5sig_option(th); /* We've parsed the options - do we have a hash? */ if (!hash_expected && !hash_location) return false; @@ -1954,10 +1951,49 @@ static void tcp_v4_fill_cb(struct sk_buff *skb, const struct iphdr *iph, TCP_SKB_CB(skb)->sacked = 0; TCP_SKB_CB(skb)->has_rxtstamp = skb->tstamp || skb_hwtstamps(skb)->hwtstamp; } +static int tcp_v4_sig_check(struct sock *sk, + struct sk_buff *skb, + int dif, + int sdif) +{ + const u8 *md5, *ao; + int ret; + + ret = tcp_parse_sig_options(tcp_hdr(skb), &md5, &ao); + if (ret) + return ret; + ret = tcp_authopt_inbound_check(sk, skb, ao); + if (ret < 0) + return ret; + if (ret == 1) + return 0; + return tcp_v4_inbound_md5_hash(sk, skb, dif, sdif, md5); +} + +static int tcp_v4_sig_check_req(struct request_sock *req, + struct sk_buff *skb, + int dif, + int sdif) +{ + struct sock *lsk = req->rsk_listener; + const u8 *md5, *ao; + int ret; + + ret = tcp_parse_sig_options(tcp_hdr(skb), &md5, &ao); + if (ret) + return ret; + ret = tcp_authopt_inbound_check_req(req, skb, ao); + if (ret < 0) + return ret; + if (ret == 1) + return 0; + return tcp_v4_inbound_md5_hash(lsk, skb, dif, sdif, md5); +} + /* * From tcp_input.c */ int tcp_v4_rcv(struct sk_buff *skb) @@ -2011,11 +2047,11 @@ int tcp_v4_rcv(struct sk_buff *skb) struct request_sock *req = inet_reqsk(sk); bool req_stolen = false; struct sock *nsk; sk = req->rsk_listener; - if (unlikely(tcp_v4_inbound_md5_hash(sk, skb, dif, sdif))) { + if (unlikely(tcp_v4_sig_check_req(req, skb, dif, sdif))) { sk_drops_add(sk, skb); reqsk_put(req); goto discard_it; } if (tcp_checksum_complete(skb)) { @@ -2081,11 +2117,11 @@ int tcp_v4_rcv(struct sk_buff *skb) } if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_and_relse; - if (tcp_v4_inbound_md5_hash(sk, skb, dif, sdif)) + if (tcp_v4_sig_check(sk, skb, dif, sdif)) goto discard_and_relse; nf_reset_ct(skb); if (tcp_filter(sk, skb)) diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 7c2d3ac2363a..61b8fa671a8f 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -18,10 +18,11 @@ * Arnt Gulbrandsen, * Jorge Cwik, */ #include +#include #include #include static bool tcp_in_window(u32 seq, u32 end_seq, u32 s_win, u32 e_win) { @@ -300,10 +301,11 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) BUG_ON(tcptw->tw_md5_key && !tcp_alloc_md5sig_pool()); } } } while (0); #endif + tcp_authopt_time_wait(tcptw, tcp_sk(sk)); /* Get the TIME_WAIT timeout firing. */ if (timeo < rto) timeo = rto; @@ -342,10 +344,19 @@ void tcp_twsk_destructor(struct sock *sk) if (twsk->tw_md5_key) kfree_rcu(twsk->tw_md5_key, rcu); } #endif +#ifdef CONFIG_TCP_AUTHOPT + if (tcp_authopt_needed) { + struct tcp_timewait_sock *twsk = tcp_twsk(sk); + + /* twsk only contains sock_common so pass NULL as sk. */ + if (twsk->tw_authopt_info) + tcp_authopt_free(NULL, twsk->tw_authopt_info); + } +#endif } EXPORT_SYMBOL_GPL(tcp_twsk_destructor); /* Warning : This function is called without sk_listener being locked. * Be sure to read socket fields once, as their value could change under us. @@ -532,10 +543,11 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, #ifdef CONFIG_TCP_MD5SIG newtp->md5sig_info = NULL; /*XXX*/ if (newtp->af_specific->md5_lookup(sk, newsk)) newtp->tcp_header_len += TCPOLEN_MD5SIG_ALIGNED; #endif + tcp_authopt_openreq(newsk, sk, req); if (skb->len >= TCP_MSS_DEFAULT + newtp->tcp_header_len) newicsk->icsk_ack.last_seg_size = skb->len - newtp->tcp_header_len; newtp->rx_opt.mss_clamp = req->mss; tcp_ecn_openreq_child(newtp, req); newtp->fastopen_req = NULL; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 5079832af5c1..b959e8b949b6 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -37,10 +37,11 @@ #define pr_fmt(fmt) "TCP: " fmt #include #include +#include #include #include #include #include @@ -410,10 +411,11 @@ static inline bool tcp_urg_mode(const struct tcp_sock *tp) #define OPTION_SACK_ADVERTISE BIT(0) #define OPTION_TS BIT(1) #define OPTION_MD5 BIT(2) #define OPTION_WSCALE BIT(3) +#define OPTION_AUTHOPT BIT(4) #define OPTION_FAST_OPEN_COOKIE BIT(8) #define OPTION_SMC BIT(9) #define OPTION_MPTCP BIT(10) static void smc_options_write(__be32 *ptr, u16 *options) @@ -434,16 +436,22 @@ static void smc_options_write(__be32 *ptr, u16 *options) struct tcp_out_options { u16 options; /* bit field of OPTION_* */ u16 mss; /* 0 to disable */ u8 ws; /* window scale, 0 to disable */ u8 num_sack_blocks; /* number of SACK blocks to include */ - u8 hash_size; /* bytes in hash_location */ u8 bpf_opt_len; /* length of BPF hdr option */ +#ifdef CONFIG_TCP_AUTHOPT + u8 authopt_rnextkeyid; /* rnextkey */ +#endif __u8 *hash_location; /* temporary pointer, overloaded */ __u32 tsval, tsecr; /* need to include OPTION_TS */ struct tcp_fastopen_cookie *fastopen_cookie; /* Fast open cookie */ struct mptcp_out_options mptcp; +#ifdef CONFIG_TCP_AUTHOPT + struct tcp_authopt_info *authopt_info; + struct tcp_authopt_key_info *authopt_key; +#endif }; static void mptcp_options_write(__be32 *ptr, const struct tcp_sock *tp, struct tcp_out_options *opts) { @@ -616,10 +624,25 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, /* overload cookie hash location */ opts->hash_location = (__u8 *)ptr; ptr += 4; } +#ifdef CONFIG_TCP_AUTHOPT + if (unlikely(OPTION_AUTHOPT & options)) { + struct tcp_authopt_key_info *key = opts->authopt_key; + + WARN_ON(!key); + *ptr = htonl((TCPOPT_AUTHOPT << 24) | + (TCPOLEN_AUTHOPT_OUTPUT << 16) | + (key->send_id << 8) | + opts->authopt_rnextkeyid); + /* overload cookie hash location */ + opts->hash_location = (__u8 *)(ptr + 1); + ptr += TCPOLEN_AUTHOPT_OUTPUT / 4; + } +#endif + if (unlikely(opts->mss)) { *ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | opts->mss); } @@ -751,10 +774,28 @@ static void mptcp_set_option_cond(const struct request_sock *req, } } } } +static int tcp_authopt_init_options(const struct sock *sk, + const struct sock *addr_sk, + struct tcp_out_options *opts) +{ +#ifdef CONFIG_TCP_AUTHOPT + struct tcp_authopt_key_info *key; + + key = tcp_authopt_select_key(sk, addr_sk, &opts->authopt_info, &opts->authopt_rnextkeyid); + if (key) { + opts->options |= OPTION_AUTHOPT; + opts->authopt_key = key; + return TCPOLEN_AUTHOPT_OUTPUT; + } +#endif + + return 0; +} + /* Compute TCP options for SYN packets. This is not the final * network wire format yet. */ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, struct tcp_out_options *opts, @@ -763,12 +804,15 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, struct tcp_sock *tp = tcp_sk(sk); unsigned int remaining = MAX_TCP_OPTION_SPACE; struct tcp_fastopen_request *fastopen = tp->fastopen_req; *md5 = NULL; + + remaining -= tcp_authopt_init_options(sk, sk, opts); #ifdef CONFIG_TCP_MD5SIG if (static_branch_unlikely(&tcp_md5_needed) && + !(opts->options & OPTION_AUTHOPT) && rcu_access_pointer(tp->md5sig_info)) { *md5 = tp->af_specific->md5_lookup(sk, sk); if (*md5) { opts->options |= OPTION_MD5; remaining -= TCPOLEN_MD5SIG_ALIGNED; @@ -847,12 +891,13 @@ static unsigned int tcp_synack_options(const struct sock *sk, struct sk_buff *syn_skb) { struct inet_request_sock *ireq = inet_rsk(req); unsigned int remaining = MAX_TCP_OPTION_SPACE; + remaining -= tcp_authopt_init_options(sk, req_to_sk(req), opts); #ifdef CONFIG_TCP_MD5SIG - if (md5) { + if (md5 && !(opts->options & OPTION_AUTHOPT)) { opts->options |= OPTION_MD5; remaining -= TCPOLEN_MD5SIG_ALIGNED; /* We can't fit any SACK blocks in a packet with MD5 + TS * options. There was discussion about disabling SACK @@ -918,13 +963,15 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb unsigned int size = 0; unsigned int eff_sacks; opts->options = 0; + size += tcp_authopt_init_options(sk, sk, opts); *md5 = NULL; #ifdef CONFIG_TCP_MD5SIG if (static_branch_unlikely(&tcp_md5_needed) && + !(opts->options & OPTION_AUTHOPT) && rcu_access_pointer(tp->md5sig_info)) { *md5 = tp->af_specific->md5_lookup(sk, sk); if (*md5) { opts->options |= OPTION_MD5; size += TCPOLEN_MD5SIG_ALIGNED; @@ -1274,10 +1321,14 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, inet = inet_sk(sk); tcb = TCP_SKB_CB(skb); memset(&opts, 0, sizeof(opts)); +#ifdef CONFIG_TCP_AUTHOPT + /* for tcp_authopt_init_options inside tcp_syn_options or tcp_established_options */ + rcu_read_lock(); +#endif if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) { tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5); } else { tcp_options_size = tcp_established_options(sk, skb, &opts, &md5); @@ -1362,10 +1413,17 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, sk_gso_disable(sk); tp->af_specific->calc_md5_hash(opts.hash_location, md5, sk, skb); } #endif +#ifdef CONFIG_TCP_AUTHOPT + if (opts.authopt_key) { + sk_gso_disable(sk); + tcp_authopt_hash(opts.hash_location, opts.authopt_key, opts.authopt_info, sk, skb); + } + rcu_read_unlock(); +#endif /* BPF prog is the last one writing header option */ bpf_skops_write_hdr_opt(sk, skb, NULL, NULL, 0, &opts); INDIRECT_CALL_INET(icsk->icsk_af_ops->send_check, @@ -1831,12 +1889,21 @@ unsigned int tcp_current_mss(struct sock *sk) u32 mtu = dst_mtu(dst); if (mtu != inet_csk(sk)->icsk_pmtu_cookie) mss_now = tcp_sync_mss(sk, mtu); } +#ifdef CONFIG_TCP_AUTHOPT + /* Even if the result is not used rcu_read_lock is required when scanning for + * tcp authentication keys. Otherwise lockdep will complain. + */ + rcu_read_lock(); +#endif header_len = tcp_established_options(sk, NULL, &opts, &md5) + sizeof(struct tcphdr); +#ifdef CONFIG_TCP_AUTHOPT + rcu_read_unlock(); +#endif /* The mss_cache is sized based on tp->tcp_header_len, which assumes * some common options. If this is an odd packet (because we have SACK * blocks etc) then our calculated header_len will be different, and * we have to adjust mss_now correspondingly */ if (header_len != tp->tcp_header_len) { @@ -3551,10 +3618,14 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, } #ifdef CONFIG_TCP_MD5SIG rcu_read_lock(); md5 = tcp_rsk(req)->af_specific->req_md5_lookup(sk, req_to_sk(req)); +#endif +#ifdef CONFIG_TCP_AUTHOPT + /* for tcp_authopt_init_options inside tcp_synack_options */ + rcu_read_lock(); #endif skb_set_hash(skb, tcp_rsk(req)->txhash, PKT_HASH_TYPE_L4); /* bpf program will be interested in the tcp_flags */ TCP_SKB_CB(skb)->tcp_flags = TCPHDR_SYN | TCPHDR_ACK; tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, md5, @@ -3588,10 +3659,20 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, if (md5) tcp_rsk(req)->af_specific->calc_md5_hash(opts.hash_location, md5, req_to_sk(req), skb); rcu_read_unlock(); #endif +#ifdef CONFIG_TCP_AUTHOPT + /* If signature fails we do nothing */ + if (opts.authopt_key) + tcp_authopt_hash(opts.hash_location, + opts.authopt_key, + opts.authopt_info, + req_to_sk(req), + skb); + rcu_read_unlock(); +#endif bpf_skops_write_hdr_opt((struct sock *)sk, skb, req, syn_skb, synack_type, &opts); skb->skb_mstamp_ns = now; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index e98fc6f12c61..3105a367d6b5 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -40,10 +40,11 @@ #include #include #include #include +#include #include #include #include #include #include @@ -772,14 +773,14 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, #endif static bool tcp_v6_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, - int dif, int sdif) + int dif, int sdif, + const u8 *hash_location) { #ifdef CONFIG_TCP_MD5SIG - const __u8 *hash_location = NULL; struct tcp_md5sig_key *hash_expected; const struct ipv6hdr *ip6h = ipv6_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); int genhash, l3index; u8 newhash[16]; @@ -788,11 +789,10 @@ static bool tcp_v6_inbound_md5_hash(const struct sock *sk, * in an L3 domain and dif is set to the l3mdev */ l3index = sdif ? dif : 0; hash_expected = tcp_v6_md5_do_lookup(sk, &ip6h->saddr, l3index); - hash_location = tcp_parse_md5sig_option(th); /* We've parsed the options - do we have a hash? */ if (!hash_expected && !hash_location) return false; @@ -1619,10 +1619,49 @@ static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr, TCP_SKB_CB(skb)->sacked = 0; TCP_SKB_CB(skb)->has_rxtstamp = skb->tstamp || skb_hwtstamps(skb)->hwtstamp; } +static int tcp_v6_sig_check(struct sock *sk, + struct sk_buff *skb, + int dif, + int sdif) +{ + const u8 *md5, *ao; + int ret; + + ret = tcp_parse_sig_options(tcp_hdr(skb), &md5, &ao); + if (ret) + return ret; + ret = tcp_authopt_inbound_check(sk, skb, ao); + if (ret < 0) + return ret; + if (ret == 1) + return 0; + return tcp_v6_inbound_md5_hash(sk, skb, dif, sdif, md5); +} + +static int tcp_v6_sig_check_req(struct request_sock *req, + struct sk_buff *skb, + int dif, + int sdif) +{ + struct sock *lsk = req->rsk_listener; + const u8 *md5, *ao; + int ret; + + ret = tcp_parse_sig_options(tcp_hdr(skb), &md5, &ao); + if (ret) + return ret; + ret = tcp_authopt_inbound_check_req(req, skb, ao); + if (ret < 0) + return ret; + if (ret == 1) + return 0; + return tcp_v6_inbound_md5_hash(lsk, skb, dif, sdif, md5); +} + INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) { int sdif = inet6_sdif(skb); int dif = inet6_iif(skb); const struct tcphdr *th; @@ -1671,11 +1710,11 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) struct request_sock *req = inet_reqsk(sk); bool req_stolen = false; struct sock *nsk; sk = req->rsk_listener; - if (tcp_v6_inbound_md5_hash(sk, skb, dif, sdif)) { + if (tcp_v6_sig_check_req(req, skb, dif, sdif)) { sk_drops_add(sk, skb); reqsk_put(req); goto discard_it; } if (tcp_checksum_complete(skb)) { @@ -1738,11 +1777,11 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) } if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_and_relse; - if (tcp_v6_inbound_md5_hash(sk, skb, dif, sdif)) + if (tcp_v6_sig_check(sk, skb, dif, sdif)) goto discard_and_relse; if (tcp_filter(sk, skb)) goto discard_and_relse; th = (const struct tcphdr *)skb->data; From patchwork Thu Dec 23 15:40:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698426 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DD62BC43219 for ; Thu, 23 Dec 2021 15:41:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349252AbhLWPlX (ORCPT ); Thu, 23 Dec 2021 10:41:23 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33808 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349080AbhLWPk5 (ORCPT ); Thu, 23 Dec 2021 10:40:57 -0500 Received: from mail-ed1-x534.google.com (mail-ed1-x534.google.com [IPv6:2a00:1450:4864:20::534]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AD2D6C061763; Thu, 23 Dec 2021 07:40:50 -0800 (PST) Received: by mail-ed1-x534.google.com with SMTP id o6so22974342edc.4; Thu, 23 Dec 2021 07:40:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=D9NCFWuovE5/gne38khmJwHLjO1KbxvtMh1bmnt68Y8=; b=p092mNJqUcXIERjF1bU0yMXrGueKvKfHmiFQ+kpFtp+kEInGvyYy6jk3Vfnkimowu2 qFXyHxML4DT9+d5NxS7Xh3Cx//7Hi/8V1Fesfux5CdenO9J6ICXes2VvuaTrpHGoSGwi 68Ucvp1dQdzoMr4EcmWTYewoNi8KVo5GJo1JGt0Npjy4zBvFHN2+bld076Kbp6NTObiK vQXOtFqVaafRbgyHfORwuRjMDIs1cTO2N4/qeZqH3k23jPKUans5Mx+xyjD3rfZirZGX yQS1FWJwZZeoUaAGxUVxbLzQScCMZRDOZiAf3XMsXYBVsurdb0Pmif+QC3Ru/8ajFM2x 0jrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=D9NCFWuovE5/gne38khmJwHLjO1KbxvtMh1bmnt68Y8=; b=jhoumE3M0s6818otvN6BcG3TTjdw54a9KlygQJmtc82pctPybSo0dO6y+MNA4puYLA 9VlKjyvPAEcV5MHMMfdiZfiW+7XUsON+JNZFhB0zqHJtJYGiuj9YvNrianqnree7Sso6 3m11NM1KXM5PN9PX4pwWMe5gyb2spCZRrKcumMK1x825IftTzighSh2w9T8kV6lalgyb 1kOM/qnHjktySKhjyBR+s+zbaYWUnKnrQ/wH8LPP94mak9oarRIpFfNzMHmWjxc7TnGb 2pQ3TM2a2QhTadsEi88Ghhv3h+LF46YKrv+uuQrNoAbBVBEqwi6CCADvk0Kjseo/LmTP Gm2Q== X-Gm-Message-State: AOAM530DFC/1Qh+OLoXw7qS8iXRwY+rOJKWTExkoJ1DNtJ8RzwChFp4D WF6435Qyg+9uPxHREIYOsnw= X-Google-Smtp-Source: ABdhPJyTCjyZ/as3+omHCjOhOt7qp4EcKlC6BQjiLKryJvs57IpQZCPF+NvfGVRvcupgZNpglKZU2Q== X-Received: by 2002:aa7:cfd5:: with SMTP id r21mr2464790edy.98.1640274049264; Thu, 23 Dec 2021 07:40:49 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:48 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 07/19] tcp: authopt: Disable via sysctl by default Date: Thu, 23 Dec 2021 17:40:02 +0200 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org This is mainly intended to protect against local privilege escalations through a rarely used feature so it is deliberately not namespaced. Enforcement is only at the setsockopt level, this should be enough to ensure that the tcp_authopt_needed static key never turns on. No effort is made to handle disabling when the feature is already in use. Signed-off-by: Leonard Crestez --- Documentation/networking/ip-sysctl.rst | 6 ++++ include/net/tcp_authopt.h | 1 + net/ipv4/sysctl_net_ipv4.c | 39 ++++++++++++++++++++++++++ net/ipv4/tcp_authopt.c | 27 +++++++++++++++++- 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index c04431144f7a..2fa992ef4e02 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -987,10 +987,16 @@ tcp_limit_output_bytes - INTEGER tcp_challenge_ack_limit - INTEGER Limits number of Challenge ACK sent per second, as recommended in RFC 5961 (Improving TCP's Robustness to Blind In-Window Attacks) Default: 1000 +tcp_authopt - BOOLEAN + Enable the TCP Authentication Option (RFC5925), a replacement for TCP + MD5 Signatures (RFC2835). + + Default: 0 + UDP variables ============= udp_l3mdev_accept - BOOLEAN Enabling this option allows a "global" bound socket to work diff --git a/include/net/tcp_authopt.h b/include/net/tcp_authopt.h index 7096e3ad59a6..4c9ec1f39932 100644 --- a/include/net/tcp_authopt.h +++ b/include/net/tcp_authopt.h @@ -80,10 +80,11 @@ struct tcphdr_authopt { }; #ifdef CONFIG_TCP_AUTHOPT DECLARE_STATIC_KEY_FALSE(tcp_authopt_needed_key); #define tcp_authopt_needed (static_branch_unlikely(&tcp_authopt_needed_key)) +extern int sysctl_tcp_authopt; void tcp_authopt_free(struct sock *sk, struct tcp_authopt_info *info); void tcp_authopt_clear(struct sock *sk); int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen); int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *key); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 97eb54774924..07de2666314c 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -17,10 +17,11 @@ #include #include #include #include #include +#include static int two = 2; static int three __maybe_unused = 3; static int four = 4; static int thousand = 1000; @@ -472,10 +473,37 @@ static int proc_fib_multipath_hash_fields(struct ctl_table *table, int write, return ret; } #endif +#ifdef CONFIG_TCP_AUTHOPT +static int proc_tcp_authopt(struct ctl_table *ctl, + int write, void *buffer, size_t *lenp, + loff_t *ppos) +{ + int val = sysctl_tcp_authopt; + struct ctl_table tmp = { + .data = &val, + .mode = ctl->mode, + .maxlen = sizeof(val), + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }; + int err; + + err = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); + if (err) + return err; + if (sysctl_tcp_authopt && !val) { + net_warn_ratelimited("Enabling TCP Authentication Option is permanent\n"); + return -EINVAL; + } + sysctl_tcp_authopt = val; + return 0; +} +#endif + static struct ctl_table ipv4_table[] = { { .procname = "tcp_max_orphans", .data = &sysctl_tcp_max_orphans, .maxlen = sizeof(int), @@ -583,10 +611,21 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_douintvec_minmax, .extra1 = &sysctl_fib_sync_mem_min, .extra2 = &sysctl_fib_sync_mem_max, }, +#ifdef CONFIG_TCP_AUTHOPT + { + .procname = "tcp_authopt", + .data = &sysctl_tcp_authopt, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_tcp_authopt, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, +#endif { } }; static struct ctl_table ipv4_net_table[] = { { diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index ba20a70359ee..6be5dbcd530e 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -4,10 +4,15 @@ #include #include #include #include +/* This is mainly intended to protect against local privilege escalations through + * a rarely used feature so it is deliberately not namespaced. + */ +int sysctl_tcp_authopt; + /* This is enabled when first struct tcp_authopt_info is allocated and never released */ DEFINE_STATIC_KEY_FALSE(tcp_authopt_needed_key); EXPORT_SYMBOL(tcp_authopt_needed_key); /* All current algorithms have a mac length of 12 but crypto API digestsize can be larger */ @@ -425,17 +430,30 @@ static int _copy_from_sockptr_tolerant(u8 *dst, memset(dst + srclen, 0, dstlen - srclen); return err; } +static int check_sysctl_tcp_authopt(void) +{ + if (!sysctl_tcp_authopt) { + net_warn_ratelimited("TCP Authentication Option disabled by sysctl.\n"); + return -EPERM; + } + + return 0; +} + int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen) { struct tcp_authopt opt; struct tcp_authopt_info *info; int err; sock_owned_by_me(sk); + err = check_sysctl_tcp_authopt(); + if (err) + return err; err = _copy_from_sockptr_tolerant((u8 *)&opt, sizeof(opt), optval, optlen); if (err) return err; @@ -453,14 +471,18 @@ int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen) int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *opt) { struct tcp_sock *tp = tcp_sk(sk); struct tcp_authopt_info *info; + int err; + memset(opt, 0, sizeof(*opt)); sock_owned_by_me(sk); + err = check_sysctl_tcp_authopt(); + if (err) + return err; - memset(opt, 0, sizeof(*opt)); info = rcu_dereference_check(tp->authopt_info, lockdep_sock_is_held(sk)); if (!info) return -ENOENT; opt->flags = info->flags & TCP_AUTHOPT_KNOWN_FLAGS; @@ -481,10 +503,13 @@ int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) struct netns_tcp_authopt *net = sock_net_tcp_authopt(sk); struct tcp_authopt_alg_imp *alg; int err; sock_owned_by_me(sk); + err = check_sysctl_tcp_authopt(); + if (err) + return err; if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; err = _copy_from_sockptr_tolerant((u8 *)&opt, sizeof(opt), optval, optlen); if (err) From patchwork Thu Dec 23 15:40:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698424 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 693BAC433EF for ; Thu, 23 Dec 2021 15:41:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349235AbhLWPlV (ORCPT ); Thu, 23 Dec 2021 10:41:21 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33822 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349085AbhLWPk5 (ORCPT ); Thu, 23 Dec 2021 10:40:57 -0500 Received: from mail-ed1-x531.google.com (mail-ed1-x531.google.com [IPv6:2a00:1450:4864:20::531]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 85930C06179C; Thu, 23 Dec 2021 07:40:52 -0800 (PST) Received: by mail-ed1-x531.google.com with SMTP id j21so22920348edt.9; Thu, 23 Dec 2021 07:40:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=LRGwolcJOYidDxYss3ob3FNIDs8LTy/+3TFunohiWFs=; b=Y2JmeY7TS++YgnizYaWK3qTpCY8gkFES+d60o/ZzRBBImp+wBcS3EMZJO8qMdbij4o E/QdoVZHKZjLkvlm7mi+2kttKO2T88/ZgMDpO7hLqBWECOzI+GF61+xZTl35N9dW4szl OLkltoFXzGTDOJ4FtrW6ZgjqhzrbEHeR3wnPY6V90RRPgIZN+dRNSRNTMls1c6etq04a dqZ0uxNCatJpHGjwnXrTGxtHMT4mr0dA5jCIQTuu/1MUOZeMXofmoPscMvyj4dhBawlL WLcGjBKyR5Ek8LnMFOiYXxkpmeczv4ukvSIrS2CRjmSqdZP85RXWpn06JBEz3HPVEppx 0KGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=LRGwolcJOYidDxYss3ob3FNIDs8LTy/+3TFunohiWFs=; b=pNmBFJPEEezK7olTrhK+QfJWwQq7jy6BUkzFYKCGsJb9c46lwbV0oxTYThlasIw82E Mu8iQx91676LUdZblQonoh/+EexwUfs/0y+LtrSlbwea3U+xTTTU9TDc6QQsgoddAtqL BD9YOF6bcOiZ8QhE8lvHHJk46K8vmV56iSlddRo7HtdO7h+UV/7BBGIBsBlRxYkKf/jY 1WK2v3rYh2P8DlngDkzj/DLd3NY0UloO4FWJWqJIaeSPatTghuOtVFZOBq5JP81tVpVD LlecwHzcYkx7gAlnlvLpg/wVBF4B/y3XpZbNekmu1114jEgdb4UDka3YYTiLihy5+bPt 1dHA== X-Gm-Message-State: AOAM530tzV0ixXKWEPrSsDYVa8uDwx+5OOxhL/nH3CT4g12DThBH6qUB u9AfyYD6ZYGqExptkjbfK0k= X-Google-Smtp-Source: ABdhPJwlUMR47WGCCYDL6ptOccVfj+9SE+TW+d+N684SAfTCoFfjYqWlAIF7zbUlWzmZal8PKLQB1A== X-Received: by 2002:a17:907:72c5:: with SMTP id du5mr2379890ejc.43.1640274050918; Thu, 23 Dec 2021 07:40:50 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:50 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 08/19] tcp: authopt: Implement Sequence Number Extension Date: Thu, 23 Dec 2021 17:40:03 +0200 Message-Id: <1297bfa0297d28d7b62ae6e9117f0c62dee09561.1640273966.git.cdleonard@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Add a compute_sne function which finds the value of SNE for a certain SEQ given an already known "recent" SNE/SEQ. This is implemented using the standard tcp before/after macro and will work for SEQ values that are without 2^31 of the SEQ for which we know the SNE. For updating we advance the value for rcv_sne at the same time as rcv_nxt and for snd_sne at the same time as snd_nxt. We could track other values (for example snd_una) but this is good enough and works very easily for timewait socket. This implementation is different from RFC suggestions and doesn't require additional flags. It does pass tests from this draft: https://datatracker.ietf.org/doc/draft-touch-sne/ Signed-off-by: Leonard Crestez --- include/net/tcp_authopt.h | 34 ++++++++++++++ net/ipv4/tcp_authopt.c | 98 ++++++++++++++++++++++++++++++++++++++- net/ipv4/tcp_input.c | 1 + net/ipv4/tcp_output.c | 1 + 4 files changed, 132 insertions(+), 2 deletions(-) diff --git a/include/net/tcp_authopt.h b/include/net/tcp_authopt.h index 4c9ec1f39932..6e9b5ca22f62 100644 --- a/include/net/tcp_authopt.h +++ b/include/net/tcp_authopt.h @@ -66,10 +66,14 @@ struct tcp_authopt_info { u32 flags; /** @src_isn: Local Initial Sequence Number */ u32 src_isn; /** @dst_isn: Remote Initial Sequence Number */ u32 dst_isn; + /** @rcv_sne: Recv-side Sequence Number Extension tracking tcp_sock.rcv_nxt */ + u32 rcv_sne; + /** @snd_sne: Send-side Sequence Number Extension tracking tcp_sock.snd_nxt */ + u32 snd_sne; }; /* TCP authopt as found in header */ struct tcphdr_authopt { u8 num; @@ -185,10 +189,34 @@ static inline int tcp_authopt_inbound_check_req(struct request_sock *req, struct if (info) return __tcp_authopt_inbound_check((struct sock *)req, skb, info, opt); } return 0; } +void __tcp_authopt_update_rcv_sne(struct tcp_sock *tp, struct tcp_authopt_info *info, u32 seq); +static inline void tcp_authopt_update_rcv_sne(struct tcp_sock *tp, u32 seq) +{ + struct tcp_authopt_info *info; + + if (tcp_authopt_needed) { + info = rcu_dereference_protected(tp->authopt_info, + lockdep_sock_is_held((struct sock *)tp)); + if (info) + __tcp_authopt_update_rcv_sne(tp, info, seq); + } +} +void __tcp_authopt_update_snd_sne(struct tcp_sock *tp, struct tcp_authopt_info *info, u32 seq); +static inline void tcp_authopt_update_snd_sne(struct tcp_sock *tp, u32 seq) +{ + struct tcp_authopt_info *info; + + if (tcp_authopt_needed) { + info = rcu_dereference_protected(tp->authopt_info, + lockdep_sock_is_held((struct sock *)tp)); + if (info) + __tcp_authopt_update_snd_sne(tp, info, seq); + } +} #else static inline int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen) { return -ENOPROTOOPT; } @@ -235,8 +263,14 @@ static inline int tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb static inline int tcp_authopt_inbound_check_req(struct request_sock *sk, struct sk_buff *skb, const u8 *opt) { return 0; } +static inline void tcp_authopt_update_rcv_sne(struct tcp_sock *tp, u32 seq) +{ +} +static inline void tcp_authopt_update_snd_sne(struct tcp_sock *tp, u32 seq) +{ +} #endif #endif /* _LINUX_TCP_AUTHOPT_H */ diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index 6be5dbcd530e..4a624a3a572d 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -644,10 +644,97 @@ static int tcp_authopt_get_isn(struct sock *sk, *disn = htonl(info->dst_isn); } return 0; } +/* compute_sne - Calculate Sequence Number Extension + * + * Give old upper/lower 32bit values and a new lower 32bit value determine the + * new value of the upper 32 bit. The new sequence number can be 2^31 before or + * after prev_seq but TCP window scaling should limit this further. + * + * For correct accounting the stored SNE value should be only updated together + * with the SEQ. + */ +static u32 compute_sne(u32 sne, u32 prev_seq, u32 seq) +{ + if (before(seq, prev_seq)) { + if (seq > prev_seq) + --sne; + } else { + if (seq < prev_seq) + ++sne; + } + + return sne; +} + +/* Update rcv_sne, must be called immediately before rcv_nxt update */ +void __tcp_authopt_update_rcv_sne(struct tcp_sock *tp, + struct tcp_authopt_info *info, u32 seq) +{ + info->rcv_sne = compute_sne(info->rcv_sne, tp->rcv_nxt, seq); +} + +/* Update snd_sne, must be called immediately before snd_nxt update */ +void __tcp_authopt_update_snd_sne(struct tcp_sock *tp, + struct tcp_authopt_info *info, u32 seq) +{ + info->snd_sne = compute_sne(info->snd_sne, tp->snd_nxt, seq); +} + +/* Compute SNE for a specific packet (by seq). */ +static int compute_packet_sne(struct sock *sk, struct tcp_authopt_info *info, + u32 seq, bool input, __be32 *sne) +{ + u32 rcv_nxt, snd_nxt; + + // For TCP_NEW_SYN_RECV we have no tcp_authopt_info but tcp_request_sock holds ISN. + if (sk->sk_state == TCP_NEW_SYN_RECV) { + struct tcp_request_sock *rsk = tcp_rsk((struct request_sock *)sk); + + if (input) + *sne = htonl(compute_sne(0, rsk->rcv_isn, seq)); + else + *sne = htonl(compute_sne(0, rsk->snt_isn, seq)); + return 0; + } + + /* TCP_LISTEN only receives SYN */ + if (sk->sk_state == TCP_LISTEN && input) + return 0; + + /* TCP_SYN_SENT only sends SYN and receives SYN/ACK + * For the input case rcv_nxt is initialized after the packet is + * validated so tcp_sk(sk)->rcv_nxt is not initialized. + */ + if (sk->sk_state == TCP_SYN_SENT) + return 0; + + if (sk->sk_state == TCP_TIME_WAIT) { + rcv_nxt = tcp_twsk(sk)->tw_rcv_nxt; + snd_nxt = tcp_twsk(sk)->tw_snd_nxt; + } else { + if (WARN_ONCE(!sk_fullsock(sk), + "unexpected minisock sk=%p state=%d", sk, + sk->sk_state)) + return -EINVAL; + rcv_nxt = tcp_sk(sk)->rcv_nxt; + snd_nxt = tcp_sk(sk)->snd_nxt; + } + + if (WARN_ONCE(!info, "unexpected missing info for sk=%p sk_state=%d", sk, sk->sk_state)) + return -EINVAL; + + if (input) + *sne = htonl(compute_sne(info->rcv_sne, rcv_nxt, seq)); + else + *sne = htonl(compute_sne(info->snd_sne, snd_nxt, seq)); + + return 0; +} + /* Feed one buffer into ahash * The buffer is assumed to be DMA-able */ static int crypto_ahash_buf(struct ahash_request *req, u8 *buf, uint len) { @@ -679,10 +766,13 @@ int __tcp_authopt_openreq(struct sock *newsk, const struct sock *oldsk, struct r if (!new_info) return -ENOMEM; new_info->src_isn = tcp_rsk(req)->snt_isn; new_info->dst_isn = tcp_rsk(req)->rcv_isn; + /* Caller is tcp_create_openreq_child and already initializes snd_nxt/rcv_nxt */ + new_info->snd_sne = compute_sne(0, new_info->src_isn, tcp_sk(newsk)->snd_nxt); + new_info->rcv_sne = compute_sne(0, new_info->dst_isn, tcp_sk(newsk)->rcv_nxt); sk_gso_disable(newsk); rcu_assign_pointer(tcp_sk(newsk)->authopt_info, new_info); return 0; } @@ -690,10 +780,12 @@ int __tcp_authopt_openreq(struct sock *newsk, const struct sock *oldsk, struct r void __tcp_authopt_finish_connect(struct sock *sk, struct sk_buff *skb, struct tcp_authopt_info *info) { info->src_isn = ntohl(tcp_hdr(skb)->ack_seq) - 1; info->dst_isn = ntohl(tcp_hdr(skb)->seq); + info->snd_sne = compute_sne(0, info->src_isn, tcp_sk(sk)->snd_nxt); + info->rcv_sne = compute_sne(0, info->dst_isn, tcp_sk(sk)->rcv_nxt); } /* feed traffic key into ahash */ static int tcp_authopt_ahash_traffic_key(struct tcp_authopt_alg_pool *pool, struct sock *sk, @@ -939,14 +1031,16 @@ static int tcp_authopt_hash_packet(struct tcp_authopt_alg_pool *pool, bool ipv6, bool include_options, u8 *macbuf) { struct tcphdr *th = tcp_hdr(skb); + __be32 sne = 0; int err; - /* NOTE: SNE unimplemented */ - __be32 sne = 0; + err = compute_packet_sne(sk, info, ntohl(th->seq), input, &sne); + if (err) + return err; err = crypto_ahash_init(pool->req); if (err) return err; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4c9e403971fb..bc0a90c72391 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3517,10 +3517,11 @@ static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack) static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq) { u32 delta = seq - tp->rcv_nxt; sock_owned_by_me((struct sock *)tp); + tcp_authopt_update_rcv_sne(tp, seq); tp->bytes_received += delta; WRITE_ONCE(tp->rcv_nxt, seq); } /* Update our send window. diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index b959e8b949b6..6a6024a0b9e9 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -67,10 +67,11 @@ static void tcp_event_new_data_sent(struct sock *sk, struct sk_buff *skb) { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); unsigned int prior_packets = tp->packets_out; + tcp_authopt_update_snd_sne(tp, TCP_SKB_CB(skb)->end_seq); WRITE_ONCE(tp->snd_nxt, TCP_SKB_CB(skb)->end_seq); __skb_unlink(skb, &sk->sk_write_queue); tcp_rbtree_insert(&sk->tcp_rtx_queue, skb); From patchwork Thu Dec 23 15:40:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698423 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 485ACC433EF for ; Thu, 23 Dec 2021 15:41:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349225AbhLWPlU (ORCPT ); Thu, 23 Dec 2021 10:41:20 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33834 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349086AbhLWPk5 (ORCPT ); Thu, 23 Dec 2021 10:40:57 -0500 Received: from mail-ed1-x52d.google.com (mail-ed1-x52d.google.com [IPv6:2a00:1450:4864:20::52d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 18581C06179E; Thu, 23 Dec 2021 07:40:54 -0800 (PST) Received: by mail-ed1-x52d.google.com with SMTP id bm14so22945106edb.5; Thu, 23 Dec 2021 07:40:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Z9+VVX02Zxra1y+xBUkqOBg1LWIZhyBsjoGF+s7cRTo=; b=JCCIQ84tLkPY7tor9teeVoP1UnFnmygyVLgQSyiUumutPh+6t8pKAn7Na+V8S2dS+i vWJ0cbA+jsedgtQU70OdZ6b8k8D51rETbbBbkiCLz/cpwID6jIaYAmWr0OwmeqCzoR56 AEng+GRddx72BIGa1zbFYw9AAEOL4GzzpoEzpKVRMJIYFdR0cOhC8S52VxBD8ZLsQByp 8C/GNVuHrZfV0QTQ8eVh4igPb5PSjkzcsHcO9ZBqy3AovGAfmPebhWtojgzLpFZtiNC0 AksRD3eX1cEnqm6XScCCZgF7hoR6XHsBuZGyCQyyoeQS2sVs9NtLnuR8FFvMWVMELRDZ sjyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Z9+VVX02Zxra1y+xBUkqOBg1LWIZhyBsjoGF+s7cRTo=; b=rbj7sQtF2/lm1TFppM/1JdfTaRYxx/uUeWESmz28YkweMt40hepcP4YlpPzKelsgN8 MQn1bRh6D+FIp5Lgw1GjnXvG8XQMkX9sHFdGKVjDvm4Gwqo07ni0Aioo3UEK83NIrCcH Fyz4pfXs58VZd9ys4OigepmfUHf2x/6GVMcv8QaaovlP+ySQ6IgRLo9c1ziTYUQ2Di33 N90+gXizN7GO2kWRdOJtYvhKcbGuC4X1uAitLHIG8c/BC34dKRorgPQbj8qSxTMIlVQk AlVkbKZrbOcm6M9cRs3ZJJ+QU2NG2linlYqwYjNDoS/UyWVsJh2WF815C9R/KRTnsAmc Yj5g== X-Gm-Message-State: AOAM532yozdvc4oSBAJulOmX0NM+lmk2/VqAKf1PutzSBVvxf1OHINGO PFfHdi46rQVIci84Bd3YuvM= X-Google-Smtp-Source: ABdhPJynOCWb4csRGcnHRZFHUUvoucEokaV5h36cNp8gG1xw+k51SHveDrue8ZDhv+a930PH2fTOoQ== X-Received: by 2002:a17:907:da1:: with SMTP id go33mr2462187ejc.280.1640274052683; Thu, 23 Dec 2021 07:40:52 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:52 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 09/19] tcp: ipv6: Add AO signing for tcp_v6_send_response Date: Thu, 23 Dec 2021 17:40:04 +0200 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org This is a special code path for acks and resets outside of normal connection establishment and closing. Signed-off-by: Leonard Crestez --- net/ipv4/tcp_authopt.c | 2 ++ net/ipv6/tcp_ipv6.c | 59 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index 4a624a3a572d..dbe92c87af5a 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -369,10 +369,11 @@ struct tcp_authopt_key_info *__tcp_authopt_select_key(const struct sock *sk, { struct netns_tcp_authopt *net = sock_net_tcp_authopt(sk); return tcp_authopt_lookup_send(net, addr_sk, -1); } +EXPORT_SYMBOL(__tcp_authopt_select_key); static struct tcp_authopt_info *__tcp_authopt_info_get_or_create(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); struct tcp_authopt_info *info; @@ -1182,10 +1183,11 @@ int tcp_authopt_hash(char *hash_location, * try to make it obvious inside the packet. */ memset(hash_location, 0, TCP_AUTHOPT_MACLEN); return err; } +EXPORT_SYMBOL(tcp_authopt_hash); static struct tcp_authopt_key_info *tcp_authopt_lookup_recv(struct sock *sk, struct sk_buff *skb, struct netns_tcp_authopt *net, int recv_id, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3105a367d6b5..a51fb759ecf2 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -886,10 +886,48 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { .init_seq = tcp_v6_init_seq, .init_ts_off = tcp_v6_init_ts_off, .send_synack = tcp_v6_send_synack, }; +#ifdef CONFIG_TCP_AUTHOPT +static int tcp_v6_send_response_init_authopt(const struct sock *sk, + struct tcp_authopt_info **info, + struct tcp_authopt_key_info **key, + u8 *rnextkeyid) +{ + /* Key lookup before SKB allocation */ + if (!(tcp_authopt_needed && sk)) + return 0; + if (sk->sk_state == TCP_TIME_WAIT) + *info = tcp_twsk(sk)->tw_authopt_info; + else + *info = rcu_dereference(tcp_sk(sk)->authopt_info); + if (!*info) + return 0; + *key = __tcp_authopt_select_key(sk, *info, sk, rnextkeyid); + if (*key) + return TCPOLEN_AUTHOPT_OUTPUT; + return 0; +} + +static void tcp_v6_send_response_sign_authopt(const struct sock *sk, + struct tcp_authopt_info *info, + struct tcp_authopt_key_info *key, + struct sk_buff *skb, + struct tcphdr_authopt *ptr, + u8 rnextkeyid) +{ + if (!(tcp_authopt_needed && key)) + return; + ptr->num = TCPOPT_AUTHOPT; + ptr->len = TCPOLEN_AUTHOPT_OUTPUT; + ptr->keyid = key->send_id; + ptr->rnextkeyid = rnextkeyid; + tcp_authopt_hash(ptr->mac, key, info, (struct sock *)sk, skb); +} +#endif + static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 tsval, u32 tsecr, int oif, struct tcp_md5sig_key *key, int rst, u8 tclass, __be32 label, u32 priority) { @@ -901,13 +939,30 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 struct sock *ctl_sk = net->ipv6.tcp_sk; unsigned int tot_len = sizeof(struct tcphdr); __be32 mrst = 0, *topt; struct dst_entry *dst; __u32 mark = 0; +#ifdef CONFIG_TCP_AUTHOPT + struct tcp_authopt_info *aoinfo; + struct tcp_authopt_key_info *aokey; + u8 aornextkeyid; + int aolen; +#endif if (tsecr) tot_len += TCPOLEN_TSTAMP_ALIGNED; +#ifdef CONFIG_TCP_AUTHOPT + /* Key lookup before SKB allocation */ + aolen = tcp_v6_send_response_init_authopt(sk, &aoinfo, &aokey, &aornextkeyid); + if (aolen) { + tot_len += aolen; +#ifdef CONFIG_TCP_MD5SIG + /* Don't use MD5 */ + key = NULL; +#endif + } +#endif #ifdef CONFIG_TCP_MD5SIG if (key) tot_len += TCPOLEN_MD5SIG_ALIGNED; #endif @@ -960,10 +1015,14 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 tcp_v6_md5_hash_hdr((__u8 *)topt, key, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, t1); } #endif +#ifdef CONFIG_TCP_AUTHOPT + tcp_v6_send_response_sign_authopt(sk, aoinfo, aokey, buff, + (struct tcphdr_authopt *)topt, aornextkeyid); +#endif memset(&fl6, 0, sizeof(fl6)); fl6.daddr = ipv6_hdr(skb)->saddr; fl6.saddr = ipv6_hdr(skb)->daddr; fl6.flowlabel = label; From patchwork Thu Dec 23 15:40:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698422 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1B988C433FE for ; Thu, 23 Dec 2021 15:41:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349214AbhLWPlS (ORCPT ); Thu, 23 Dec 2021 10:41:18 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33878 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349012AbhLWPk5 (ORCPT ); Thu, 23 Dec 2021 10:40:57 -0500 Received: from mail-ed1-x534.google.com (mail-ed1-x534.google.com [IPv6:2a00:1450:4864:20::534]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B4B2FC0617A1; Thu, 23 Dec 2021 07:40:55 -0800 (PST) Received: by mail-ed1-x534.google.com with SMTP id j21so22920883edt.9; Thu, 23 Dec 2021 07:40:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=QbGn+12SmWm9XZSkxI+H9M/uSYS0f/ErlL5WlEkQuTY=; b=adsLgII17gxA8WfT3MHq0YtX5IWhIk3It+CB9roewYAaJ6YTFssvKYEv6jG/TFZ1Pi GBxoZZRLm+DQNLG0tvZpG/A8vbpbSIn/o2MrOBVNm6zkbtcSLzLI23dGPRWponHAuzJI 6MYDCpqMccmhE/PVRunZrqFOMHrAtBwhQfr5XW1SyWf03tSkx0Nl750+Yf17VG8r/ufb CRKXf4NnWC6X2ioHI0iEYG0OFuDSODjT0muOUySGPh2nGYBN9kC0X0kA4fDmDaUHka7G fYN26/RdBGNhQvSX02kNZiYDqJwhNJma6wB7U2pM7uzt875V5vav2XuSj+1apU4NRaR0 zo/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=QbGn+12SmWm9XZSkxI+H9M/uSYS0f/ErlL5WlEkQuTY=; b=70xeKX6JwahQNuFKBu0stotyH9GO3Ek6BRkNRzv5933BXb03jwsfrSMFlAygm7PLeF bQvPBgM12Fb+ew3ZlH/n4JbpOVa5oNCBd3ADHGYRb+vtZfjxv1tN3qHDZFud1nLff+Vw cv0L1ay40btgrxb0a5ld9+vpSYX/nJAlv2Xpbcq9aH0ySDWPusmfk7WXzV1TUEEOJtoX lZFlYHTJIEDp3pe4v1ZIkkPSOoLQtsoidbW8qPu1+uIag5JZuTnmySal1ZCTBWXwAd1U MAzfg8m1OfHri8XsDXRmx0Hksh8o6eqJASeeLxJ0/9q4mW6QmZvCZz1Vo+za1Uzi8E/G P3tA== X-Gm-Message-State: AOAM531YJZTdmBOWNPGWlV0xEoUgjQfgbjn9pbtF5Pk2H6NnP9hSUhN8 DH2XOL5k1SVyWucuPVY9RHo= X-Google-Smtp-Source: ABdhPJyLwLWPO0ernvKv6pby6H04cOtGTdeAie5twflvYJL6MdBAbl7XJ6oS5pS8vUi4NUICM9VUwg== X-Received: by 2002:a17:907:2be9:: with SMTP id gv41mr2438145ejc.468.1640274054324; Thu, 23 Dec 2021 07:40:54 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:53 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 10/19] tcp: authopt: Add support for signing skb-less replies Date: Thu, 23 Dec 2021 17:40:05 +0200 Message-Id: <95cd9a5582e868083d742548d35f39eb6584211b.1640273966.git.cdleonard@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org This is required because tcp ipv4 sometimes sends replies without allocating a full skb that can be signed by tcp authopt. Handle this with additional code in tcp authopt. Signed-off-by: Leonard Crestez --- include/net/tcp_authopt.h | 7 ++ net/ipv4/tcp_authopt.c | 144 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) diff --git a/include/net/tcp_authopt.h b/include/net/tcp_authopt.h index 6e9b5ca22f62..9ee5165388b1 100644 --- a/include/net/tcp_authopt.h +++ b/include/net/tcp_authopt.h @@ -115,10 +115,17 @@ static inline struct tcp_authopt_key_info *tcp_authopt_select_key( int tcp_authopt_hash( char *hash_location, struct tcp_authopt_key_info *key, struct tcp_authopt_info *info, struct sock *sk, struct sk_buff *skb); +int tcp_v4_authopt_hash_reply( + char *hash_location, + struct tcp_authopt_info *info, + struct tcp_authopt_key_info *key, + __be32 saddr, + __be32 daddr, + struct tcphdr *th); int __tcp_authopt_openreq(struct sock *newsk, const struct sock *oldsk, struct request_sock *req); static inline int tcp_authopt_openreq( struct sock *newsk, const struct sock *oldsk, struct request_sock *req) diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index dbe92c87af5a..6803fc39f9b6 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -922,10 +922,72 @@ static int tcp_authopt_get_traffic_key(struct sock *sk, out: tcp_authopt_put_kdf_pool(key, pool); return err; } +struct tcp_v4_authopt_context_data { + __be32 saddr; + __be32 daddr; + __be16 sport; + __be16 dport; + __be32 sisn; + __be32 disn; + __be16 digestbits; +} __packed; + +static int tcp_v4_authopt_get_traffic_key_noskb(struct tcp_authopt_key_info *key, + __be32 saddr, + __be32 daddr, + __be16 sport, + __be16 dport, + __be32 sisn, + __be32 disn, + u8 *traffic_key) +{ + int err; + struct tcp_authopt_alg_pool *pool; + struct tcp_v4_authopt_context_data data; + + BUILD_BUG_ON(sizeof(data) != 22); + + pool = tcp_authopt_get_kdf_pool(key); + if (IS_ERR(pool)) + return PTR_ERR(pool); + + err = tcp_authopt_setkey(pool, key); + if (err) + goto out; + err = crypto_ahash_init(pool->req); + if (err) + goto out; + + // RFC5926 section 3.1.1.1 + // Separate to keep alignment semi-sane + err = crypto_ahash_buf(pool->req, "\x01TCP-AO", 7); + if (err) + return err; + data.saddr = saddr; + data.daddr = daddr; + data.sport = sport; + data.dport = dport; + data.sisn = sisn; + data.disn = disn; + data.digestbits = htons(crypto_ahash_digestsize(pool->tfm) * 8); + + err = crypto_ahash_buf(pool->req, (u8 *)&data, sizeof(data)); + if (err) + goto out; + ahash_request_set_crypt(pool->req, NULL, traffic_key, 0); + err = crypto_ahash_final(pool->req); + if (err) + goto out; + +out: + tcp_authopt_put_kdf_pool(key, pool); + return err; +} + static int crypto_ahash_buf_zero(struct ahash_request *req, int len) { u8 zeros[TCP_AUTHOPT_MACLEN] = {0}; int buflen, err; @@ -1185,10 +1247,92 @@ int tcp_authopt_hash(char *hash_location, memset(hash_location, 0, TCP_AUTHOPT_MACLEN); return err; } EXPORT_SYMBOL(tcp_authopt_hash); +/** + * tcp_v4_authopt_hash_reply - Hash tcp+ipv4 header without SKB + * + * @hash_location: output buffer + * @info: sending socket's tcp_authopt_info + * @key: signing key, from tcp_authopt_select_key. + * @saddr: source address + * @daddr: destination address + * @th: Pointer to TCP header and options + */ +int tcp_v4_authopt_hash_reply(char *hash_location, + struct tcp_authopt_info *info, + struct tcp_authopt_key_info *key, + __be32 saddr, + __be32 daddr, + struct tcphdr *th) +{ + struct tcp_authopt_alg_pool *pool; + u8 macbuf[TCP_AUTHOPT_MAXMACBUF]; + u8 traffic_key[TCP_AUTHOPT_MAX_TRAFFIC_KEY_LEN]; + __be32 sne = 0; + int err; + + /* Call special code path for computing traffic key without skb + * This can be called from tcp_v4_reqsk_send_ack so caching would be + * difficult here. + */ + err = tcp_v4_authopt_get_traffic_key_noskb(key, saddr, daddr, + th->source, th->dest, + htonl(info->src_isn), htonl(info->dst_isn), + traffic_key); + if (err) + goto out_err_traffic_key; + + /* Init mac shash */ + pool = tcp_authopt_get_mac_pool(key); + if (IS_ERR(pool)) + return PTR_ERR(pool); + err = crypto_ahash_setkey(pool->tfm, traffic_key, key->alg->traffic_key_len); + if (err) + goto out_err; + err = crypto_ahash_init(pool->req); + if (err) + return err; + + err = crypto_ahash_buf(pool->req, (u8 *)&sne, 4); + if (err) + return err; + + err = tcp_authopt_hash_tcp4_pseudoheader(pool, saddr, daddr, th->doff * 4); + if (err) + return err; + + // TCP header with checksum set to zero. Caller ensures this. + if (WARN_ON_ONCE(th->check != 0)) + goto out_err; + err = crypto_ahash_buf(pool->req, (u8 *)th, sizeof(*th)); + if (err) + goto out_err; + + // TCP options + err = tcp_authopt_hash_opts(pool, th, (struct tcphdr_authopt *)(hash_location - 4), + !(key->flags & TCP_AUTHOPT_KEY_EXCLUDE_OPTS)); + if (err) + goto out_err; + + ahash_request_set_crypt(pool->req, NULL, macbuf, 0); + err = crypto_ahash_final(pool->req); + if (err) + goto out_err; + memcpy(hash_location, macbuf, TCP_AUTHOPT_MACLEN); + + tcp_authopt_put_mac_pool(key, pool); + return 0; + +out_err: + tcp_authopt_put_mac_pool(key, pool); +out_err_traffic_key: + memset(hash_location, 0, TCP_AUTHOPT_MACLEN); + return err; +} + static struct tcp_authopt_key_info *tcp_authopt_lookup_recv(struct sock *sk, struct sk_buff *skb, struct netns_tcp_authopt *net, int recv_id, bool *anykey) From patchwork Thu Dec 23 15:40:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698420 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BF691C433FE for ; Thu, 23 Dec 2021 15:41:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349171AbhLWPlJ (ORCPT ); Thu, 23 Dec 2021 10:41:09 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33840 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349092AbhLWPk6 (ORCPT ); Thu, 23 Dec 2021 10:40:58 -0500 Received: from mail-ed1-x52e.google.com (mail-ed1-x52e.google.com [IPv6:2a00:1450:4864:20::52e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 89363C061756; Thu, 23 Dec 2021 07:40:57 -0800 (PST) Received: by mail-ed1-x52e.google.com with SMTP id b13so22891691edd.8; Thu, 23 Dec 2021 07:40:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=wR6ufi53cLMSqSVTH/P70HPPxbJlp15sO2dGTr1g/e8=; b=SYzgokc6zSrFJI5WXn8Kcz9OVGzs9378Bmzq3rNzl5p4pTTeRCXZCrkBQ2OWu5a1vm bGM5m1v0tHIr/15WOEwA2MguwbSe9Y8P/Ot56QC0vK77nED97mjrnsJsTg92UftYFvsS zFNOAWYaB4rf/u29q5kqj+NtQEK7HiqgPJ3mTJhxJg8/NkZjXNblLgvtXMrKUoIcBaUC Lpwv068BeHwXnvnlPP83JLsFuvjdR+gtSeL6GrpsCUf9nudy04wmciNPxzWzGSRAvGof pfx95/TwH+6I8gzyGkr3Y2TQa/DIxUPOYSCnhJL4E+lXsbBBW5m3tosYAgKh0RBq96Pq BYMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=wR6ufi53cLMSqSVTH/P70HPPxbJlp15sO2dGTr1g/e8=; b=IoqO74U9qFBBFzIZZJefmWJoGQYq3NeQYQbdXx7Wt+6rf5sUhDgLDkDNd4Hetv0PUL l6L4/1ZyyGoJMWlvAa5DKSmZWgm55HoERxEyUyl0sbC3WsR+igMqggc987ov2l6lx/HH fIPBnUfioa+kQny5PJacSMVlDUtM7bZgEllc++x4QmsS1nYP8PZzRFMAywVRasRHRX1W nAQbRl4fXFqwgPPF/OVDk43gOtarUwIob7imQdEEwmOpFgWgB4CibmDAqsdy0LXyTxVk pKQIFiv5xckAYq6h+pBBJUjZNrwQa+NDLWC9J4V6gKRDSQGiNGw6QlUuee0prDJKfkEu Fhsg== X-Gm-Message-State: AOAM532J9KpRGTkZtwOrOkgoy+phos5afes3qQ1sbtfR+1d+qOdybDic y8IDgQPwHSmhJjyVhGwL+dI= X-Google-Smtp-Source: ABdhPJxXQpCdWjCb2zta/GL+BOlEJgMxQUbdiRoe962aOSsCcYh3NegFYKSVu9VjvJVyovCNEzxcBw== X-Received: by 2002:a50:a41e:: with SMTP id u30mr2500170edb.388.1640274056079; Thu, 23 Dec 2021 07:40:56 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:55 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 11/19] tcp: ipv4: Add AO signing for skb-less replies Date: Thu, 23 Dec 2021 17:40:06 +0200 Message-Id: <695e6c5bd1bef2b3b93c123c7b595f4c625c27b1.1640273966.git.cdleonard@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org The code in tcp_v4_send_ack and tcp_v4_send_reset does not allocate a full skb so special handling is required for tcp-authopt handling. Signed-off-by: Leonard Crestez --- net/ipv4/tcp_ipv4.c | 84 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b16f263c3121..6b63175d0221 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -644,10 +644,50 @@ void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb) __tcp_v4_send_check(skb, inet->inet_saddr, inet->inet_daddr); } EXPORT_SYMBOL(tcp_v4_send_check); +#ifdef CONFIG_TCP_AUTHOPT +/** tcp_v4_authopt_handle_reply - Insert TCPOPT_AUTHOPT if required + * + * returns number of bytes (always aligned to 4) or zero + */ +static int tcp_v4_authopt_handle_reply(const struct sock *sk, + struct sk_buff *skb, + __be32 *optptr, + struct tcphdr *th) +{ + struct tcp_authopt_info *info; + struct tcp_authopt_key_info *key_info; + u8 rnextkeyid; + + if (sk->sk_state == TCP_TIME_WAIT) + info = tcp_twsk(sk)->tw_authopt_info; + else + info = rcu_dereference_check(tcp_sk(sk)->authopt_info, lockdep_sock_is_held(sk)); + if (!info) + return 0; + key_info = __tcp_authopt_select_key(sk, info, sk, &rnextkeyid); + if (!key_info) + return 0; + *optptr = htonl((TCPOPT_AUTHOPT << 24) | + (TCPOLEN_AUTHOPT_OUTPUT << 16) | + (key_info->send_id << 8) | + (rnextkeyid)); + /* must update doff before signature computation */ + th->doff += TCPOLEN_AUTHOPT_OUTPUT / 4; + tcp_v4_authopt_hash_reply((char *)(optptr + 1), + info, + key_info, + ip_hdr(skb)->daddr, + ip_hdr(skb)->saddr, + th); + + return TCPOLEN_AUTHOPT_OUTPUT; +} +#endif + /* * This routine will send an RST to the other tcp. * * Someone asks: why I NEVER use socket parameters (TOS, TTL etc.) * for reset. @@ -659,10 +699,12 @@ EXPORT_SYMBOL(tcp_v4_send_check); * Exception: precedence violation. We do not implement it in any case. */ #ifdef CONFIG_TCP_MD5SIG #define OPTION_BYTES TCPOLEN_MD5SIG_ALIGNED +#elif defined(OPTION_BYTES_TCP_AUTHOPT) +#define OPTION_BYTES TCPOLEN_AUTHOPT_OUTPUT #else #define OPTION_BYTES sizeof(__be32) #endif static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) @@ -712,12 +754,29 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) memset(&arg, 0, sizeof(arg)); arg.iov[0].iov_base = (unsigned char *)&rep; arg.iov[0].iov_len = sizeof(rep.th); net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AUTHOPT) rcu_read_lock(); +#endif +#ifdef CONFIG_TCP_AUTHOPT + /* Unlike TCP-MD5 the signatures for TCP-AO depend on initial sequence + * numbers so we can only handle established and time-wait sockets. + */ + if (tcp_authopt_needed && sk && + sk->sk_state != TCP_NEW_SYN_RECV && + sk->sk_state != TCP_LISTEN) { + int tcp_authopt_ret = tcp_v4_authopt_handle_reply(sk, skb, rep.opt, &rep.th); + + if (tcp_authopt_ret) { + arg.iov[0].iov_len += tcp_authopt_ret; + goto skip_md5sig; + } + } +#endif +#ifdef CONFIG_TCP_MD5SIG hash_location = tcp_parse_md5sig_option(th); if (sk && sk_fullsock(sk)) { const union tcp_md5_addr *addr; int l3index; @@ -755,11 +814,10 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr; key = tcp_md5_do_lookup(sk1, l3index, addr, AF_INET); if (!key) goto out; - genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb); if (genhash || memcmp(hash_location, newhash, 16) != 0) goto out; } @@ -775,10 +833,13 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) tcp_v4_md5_hash_hdr((__u8 *) &rep.opt[1], key, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, &rep.th); } +#endif +#ifdef CONFIG_TCP_AUTHOPT +skip_md5sig: #endif /* Can't co-exist with TCPMD5, hence check rep.opt[0] */ if (rep.opt[0] == 0) { __be32 mrst = mptcp_reset_option(skb); @@ -828,12 +889,14 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) ctl_sk->sk_mark = 0; __TCP_INC_STATS(net, TCP_MIB_OUTSEGS); __TCP_INC_STATS(net, TCP_MIB_OUTRSTS); local_bh_enable(); -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) out: +#endif +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AUTHOPT) rcu_read_unlock(); #endif } /* The code following below sending ACKs in SYN-RECV and TIME-WAIT states @@ -850,10 +913,12 @@ static void tcp_v4_send_ack(const struct sock *sk, struct { struct tcphdr th; __be32 opt[(TCPOLEN_TSTAMP_ALIGNED >> 2) #ifdef CONFIG_TCP_MD5SIG + (TCPOLEN_MD5SIG_ALIGNED >> 2) +#elif defined(CONFIG_TCP_AUTHOPT) + + (TCPOLEN_AUTHOPT_OUTPUT >> 2) #endif ]; } rep; struct net *net = sock_net(sk); struct ip_reply_arg arg; @@ -881,10 +946,23 @@ static void tcp_v4_send_ack(const struct sock *sk, rep.th.seq = htonl(seq); rep.th.ack_seq = htonl(ack); rep.th.ack = 1; rep.th.window = htons(win); +#ifdef CONFIG_TCP_AUTHOPT + if (tcp_authopt_needed) { + int aoret, offset = (tsecr) ? 3 : 0; + + aoret = tcp_v4_authopt_handle_reply(sk, skb, &rep.opt[offset], &rep.th); + if (aoret) { + arg.iov[0].iov_len += aoret; +#ifdef CONFIG_TCP_MD5SIG + key = NULL; +#endif + } + } +#endif #ifdef CONFIG_TCP_MD5SIG if (key) { int offset = (tsecr) ? 3 : 0; rep.opt[offset++] = htonl((TCPOPT_NOP << 24) | From patchwork Thu Dec 23 15:40:07 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698419 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2F985C433F5 for ; Thu, 23 Dec 2021 15:41:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349123AbhLWPlE (ORCPT ); Thu, 23 Dec 2021 10:41:04 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33828 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349128AbhLWPlC (ORCPT ); Thu, 23 Dec 2021 10:41:02 -0500 Received: from mail-ed1-x531.google.com (mail-ed1-x531.google.com [IPv6:2a00:1450:4864:20::531]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B33F4C061756; Thu, 23 Dec 2021 07:41:01 -0800 (PST) Received: by mail-ed1-x531.google.com with SMTP id x15so23036791edv.1; Thu, 23 Dec 2021 07:41:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=0JM3xGgJnvdcXaOjdlnR11Xdg76dzTUfR2d1jSQqiDw=; b=Rl5kcwCPakrhO+keHfzrTEmwkws9nF9RkORYUrD7mT2fW/YNr3a6rmW2PvhPIXkXEu bkcpzPGIXXqrWxX3F6l5Ck3oeqKluBObdQGEAI86jn+gKV5/vUJXRxexgKGgmoJnaulE wBETsQJxU/EkeDpotPI/n0aGkVg7Cz/PdbvWWsiX4CL0PZT8OvGX59OEvzbTbs188VJZ DazERDrmb7QcVjN8RfX5KzZu3iVlhdSxV7IajaRlv1kELT1qFItyHzYMdZFMVmqB/nic 5fz/u7ygWK9J8Jcw9jzzquwgqsPB4eru2ccVL2WjMrXjswBXB659WXIyL+yNPp0ahg2S ECjg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=0JM3xGgJnvdcXaOjdlnR11Xdg76dzTUfR2d1jSQqiDw=; b=i2XnzTum/U8VC4zz+4xAazQ7UFzzUyXIvEQQFC3i0FSs50YiOcOLKMA+kHPEPWVgnS I6O+y+BTMMQOmgSc9zVDXU3gzGP/zgbVi3+UzKSxrEMCh9toxDsIaml752IeVeacYBdK 6Y7+2wxjavGC94XIv6JPKjt6vafx2wP2pQxOAGGM260FH4+aMOeMGFmOg9THGnsBXCke B5JfWK0szAKjkFHYPOL81pB8f9q8quUYQ2ltmZD4pWbYykonA+51S5ZJAr3cBRKXDqPY t8Enurv3hx2x/dZxTWqZhVFFX189brGAtu9iLFARhq8OonwiPU/TU39dadiLQ4LvTox/ krLw== X-Gm-Message-State: AOAM530gDk/tWE1YswTBOJgMKr5MjUpH7EFZ7xTvNYX/W+WLm7Y9rfB+ 4ni95KXajgIlwxWLflp4a5c= X-Google-Smtp-Source: ABdhPJw567yoqg01N6hJ488QE1PLAoMasKI4FCok2Hu/RMuDe20JO8jlllZUS9+XCGyMSe3jQff3Pw== X-Received: by 2002:a05:6402:2808:: with SMTP id h8mr2554159ede.58.1640274060166; Thu, 23 Dec 2021 07:41:00 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.40.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:40:59 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 12/19] tcp: authopt: Add key selection controls Date: Thu, 23 Dec 2021 17:40:07 +0200 Message-Id: <03ccc03384afdd7c3ec1e20bff8325d127469faa.1640273966.git.cdleonard@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org The RFC requires that TCP can report the keyid and rnextkeyid values being sent or received, implement this via getsockopt values. The RFC also requires that user can select the sending key and that the sending key is automatically switched based on rnextkeyid. These requirements can conflict so we implement both and add a flag which specifies if user or peer request takes priority. Also add an option to control rnextkeyid explicitly from userspace. Signed-off-by: Leonard Crestez --- Documentation/networking/tcp_authopt.rst | 25 ++++++ include/net/tcp_authopt.h | 40 ++++++++- include/uapi/linux/tcp.h | 31 +++++++ net/ipv4/tcp_authopt.c | 107 ++++++++++++++++++++++- net/ipv4/tcp_ipv4.c | 2 +- net/ipv6/tcp_ipv6.c | 2 +- 6 files changed, 200 insertions(+), 7 deletions(-) diff --git a/Documentation/networking/tcp_authopt.rst b/Documentation/networking/tcp_authopt.rst index 72adb7a891ce..f29fdea7769f 100644 --- a/Documentation/networking/tcp_authopt.rst +++ b/Documentation/networking/tcp_authopt.rst @@ -42,10 +42,35 @@ new flags. RFC5925 requires that key ids do not overlap when tcp identifiers (addr/port) overlap. This is not enforced by linux, configuring ambiguous keys will result in packet drops and lost connections. +Key selection +------------- + +On getsockopt(TCP_AUTHOPT) information is provided about keyid/rnextkeyid in +the last send packet and about the keyid/rnextkeyd in the last valid received +packet. + +By default the sending keyid is selected to match the rnextkeyid value sent by +the remote side. If that keyid is not available (or for new connections) a +random matching key is selected. + +If the ``TCP_AUTHOPT_LOCK_KEYID`` flag is set then the sending key is selected +by the `tcp_authopt.send_local_id` field and recv_rnextkeyid is ignored. If no +key with local_id == send_local_id is configured then a random matching key is +selected. + +The current sending key is cached in the socket and will not change unless +requested by remote rnextkeyid or by setsockopt. + +The rnextkeyid value sent on the wire is usually the recv_id of the current +key used for sending. If the TCP_AUTHOPT_LOCK_RNEXTKEY flag is set in +`tcp_authopt.flags` the value of `tcp_authopt.send_rnextkeyid` is send +instead. This can be used to implement smooth rollover: the peer will switch +its keyid to the received rnextkeyid when it is available. + ABI Reference ============= .. kernel-doc:: include/uapi/linux/tcp.h :identifiers: tcp_authopt tcp_authopt_flag tcp_authopt_key tcp_authopt_key_flag tcp_authopt_alg diff --git a/include/net/tcp_authopt.h b/include/net/tcp_authopt.h index 9ee5165388b1..3d03fbb186ef 100644 --- a/include/net/tcp_authopt.h +++ b/include/net/tcp_authopt.h @@ -70,10 +70,45 @@ struct tcp_authopt_info { u32 dst_isn; /** @rcv_sne: Recv-side Sequence Number Extension tracking tcp_sock.rcv_nxt */ u32 rcv_sne; /** @snd_sne: Send-side Sequence Number Extension tracking tcp_sock.snd_nxt */ u32 snd_sne; + + /** + * @send_keyid: keyid currently being sent + * + * This is controlled by userspace by userspace if + * TCP_AUTHOPT_FLAG_LOCK_KEYID, otherwise we try to match recv_rnextkeyid + */ + u8 send_keyid; + /** + * @send_rnextkeyid: rnextkeyid currently being sent + * + * This is controlled by userspace if TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID is set + */ + u8 send_rnextkeyid; + /** + * @recv_keyid: last keyid received from remote + * + * This is reported to userspace but has no other special behavior attached. + */ + u8 recv_keyid; + /** + * @recv_rnextkeyid: last rnextkeyid received from remote + * + * Linux tries to honor this unless TCP_AUTHOPT_FLAG_LOCK_KEYID is set + */ + u8 recv_rnextkeyid; + + /** + * @send_key: Current key used for sending, cached. + * + * Once a key is found it only changes by user or remote request. + * + * Field is protected by the socket lock and holds a kref to the key. + */ + struct tcp_authopt_key_info __rcu *send_key; }; /* TCP authopt as found in header */ struct tcphdr_authopt { u8 num; @@ -95,22 +130,23 @@ int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *key); int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen); struct tcp_authopt_key_info *__tcp_authopt_select_key( const struct sock *sk, struct tcp_authopt_info *info, const struct sock *addr_sk, - u8 *rnextkeyid); + u8 *rnextkeyid, + bool locked); static inline struct tcp_authopt_key_info *tcp_authopt_select_key( const struct sock *sk, const struct sock *addr_sk, struct tcp_authopt_info **info, u8 *rnextkeyid) { if (tcp_authopt_needed) { *info = rcu_dereference(tcp_sk(sk)->authopt_info); if (*info) - return __tcp_authopt_select_key(sk, *info, addr_sk, rnextkeyid); + return __tcp_authopt_select_key(sk, *info, addr_sk, rnextkeyid, true); } return NULL; } int tcp_authopt_hash( char *hash_location, diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 76d7be6b27f4..e02176390519 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -346,10 +346,24 @@ struct tcp_diag_md5sig { /** * enum tcp_authopt_flag - flags for `tcp_authopt.flags` */ enum tcp_authopt_flag { + /** + * @TCP_AUTHOPT_FLAG_LOCK_KEYID: keyid controlled by sockopt + * + * If this is set `tcp_authopt.send_keyid` is used to determined sending + * key. Otherwise a key with send_id == recv_rnextkeyid is preferred. + */ + TCP_AUTHOPT_FLAG_LOCK_KEYID = (1 << 0), + /** + * @TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID: Override rnextkeyid from userspace + * + * If this is set then `tcp_authopt.send_rnextkeyid` is sent on outbound + * packets. Other the recv_id of the current sending key is sent. + */ + TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID = (1 << 1), /** * @TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED: * Configure behavior of segments with TCP-AO coming from hosts for which no * key is configured. The default recommended by RFC is to silently accept * such connections. @@ -361,10 +375,27 @@ enum tcp_authopt_flag { * struct tcp_authopt - Per-socket options related to TCP Authentication Option */ struct tcp_authopt { /** @flags: Combination of &enum tcp_authopt_flag */ __u32 flags; + /** + * @send_keyid: `tcp_authopt_key.send_id` of preferred send key + * + * This is only used if `TCP_AUTHOPT_FLAG_LOCK_KEYID` is set. + */ + __u8 send_keyid; + /** + * @send_rnextkeyid: The rnextkeyid to send in packets + * + * This is controlled by the user iff TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID is + * set. Otherwise rnextkeyid is the recv_id of the current key. + */ + __u8 send_rnextkeyid; + /** @recv_keyid: A recently-received keyid value. Only for getsockopt. */ + __u8 recv_keyid; + /** @recv_rnextkeyid: A recently-received rnextkeyid value. Only for getsockopt. */ + __u8 recv_rnextkeyid; }; /** * enum tcp_authopt_key_flag - flags for `tcp_authopt.flags` * diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index 6803fc39f9b6..f0d7a7facfb9 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -356,22 +356,89 @@ static struct tcp_authopt_key_info *tcp_authopt_lookup_send(struct netns_tcp_aut * * @sk: socket * @info: socket's tcp_authopt_info * @addr_sk: socket used for address lookup. Same as sk except for synack case * @rnextkeyid: value of rnextkeyid caller should write in packet + * @locked: If we're holding the socket lock. This is false for some timewait and reset cases * * Result is protected by RCU and can't be stored, it may only be passed to * tcp_authopt_hash and only under a single rcu_read_lock. */ struct tcp_authopt_key_info *__tcp_authopt_select_key(const struct sock *sk, struct tcp_authopt_info *info, const struct sock *addr_sk, - u8 *rnextkeyid) + u8 *rnextkeyid, + bool locked) { + struct tcp_authopt_key_info *key, *new_key = NULL; struct netns_tcp_authopt *net = sock_net_tcp_authopt(sk); - return tcp_authopt_lookup_send(net, addr_sk, -1); + /* Listen sockets don't refer to any specific connection so we don't try + * to keep using the same key and ignore any received keyids. + */ + if (sk->sk_state == TCP_LISTEN) { + int send_keyid = -1; + + if (info->flags & TCP_AUTHOPT_FLAG_LOCK_KEYID) + send_keyid = info->send_keyid; + key = tcp_authopt_lookup_send(net, addr_sk, send_keyid); + if (key) + *rnextkeyid = key->recv_id; + + return key; + } + + if (locked) { + sock_owned_by_me(sk); + key = rcu_dereference_protected(info->send_key, lockdep_sock_is_held(sk)); + if (key && (key->flags | TCP_AUTHOPT_KEY_DEL) == TCP_AUTHOPT_KEY_DEL) { + info->send_key = NULL; + tcp_authopt_key_put(key); + key = NULL; + } + } else { + key = NULL; + } + + /* Try to keep the same sending key unless user or peer requires a different key + * User request (via TCP_AUTHOPT_FLAG_LOCK_KEYID) always overrides peer request. + */ + if (info->flags & TCP_AUTHOPT_FLAG_LOCK_KEYID) { + int send_keyid = info->send_keyid; + + if (!key || key->send_id != send_keyid) + new_key = tcp_authopt_lookup_send(net, addr_sk, send_keyid); + } else { + if (!key || key->send_id != info->recv_rnextkeyid) + new_key = tcp_authopt_lookup_send(net, addr_sk, info->recv_rnextkeyid); + } + /* If no key found with specific send_id try anything else. */ + if (!key && !new_key) + new_key = tcp_authopt_lookup_send(net, addr_sk, -1); + + /* Update current key only if we hold the socket lock. */ + if (new_key && key != new_key) { + if (locked) { + if (kref_get_unless_zero(&new_key->ref)) { + rcu_assign_pointer(info->send_key, new_key); + tcp_authopt_key_put(key); + } + /* If key was deleted it's still valid until the end of + * the RCU grace period. + */ + } + key = new_key; + } + + if (key) { + if (info->flags & TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID) + *rnextkeyid = info->send_rnextkeyid; + else + *rnextkeyid = info->send_rnextkeyid = key->recv_id; + } + + return key; } EXPORT_SYMBOL(__tcp_authopt_select_key); static struct tcp_authopt_info *__tcp_authopt_info_get_or_create(struct sock *sk) { @@ -393,10 +460,12 @@ static struct tcp_authopt_info *__tcp_authopt_info_get_or_create(struct sock *sk return info; } #define TCP_AUTHOPT_KNOWN_FLAGS ( \ + TCP_AUTHOPT_FLAG_LOCK_KEYID | \ + TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID | \ TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED) /* Like copy_from_sockopt except tolerate different optlen for compatibility reasons * * If the src is shorter then it's from an old userspace and the rest of dst is @@ -464,18 +533,23 @@ int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen) info = __tcp_authopt_info_get_or_create(sk); if (IS_ERR(info)) return PTR_ERR(info); info->flags = opt.flags & TCP_AUTHOPT_KNOWN_FLAGS; + if (opt.flags & TCP_AUTHOPT_FLAG_LOCK_KEYID) + info->send_keyid = opt.send_keyid; + if (opt.flags & TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID) + info->send_rnextkeyid = opt.send_rnextkeyid; return 0; } int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *opt) { struct tcp_sock *tp = tcp_sk(sk); struct tcp_authopt_info *info; + struct tcp_authopt_key_info *send_key; int err; memset(opt, 0, sizeof(*opt)); sock_owned_by_me(sk); err = check_sysctl_tcp_authopt(); @@ -485,10 +559,22 @@ int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *opt) info = rcu_dereference_check(tp->authopt_info, lockdep_sock_is_held(sk)); if (!info) return -ENOENT; opt->flags = info->flags & TCP_AUTHOPT_KNOWN_FLAGS; + /* These keyids might be undefined, for example before connect. + * Reporting zero is not strictly correct because there are no reserved + * values. + */ + send_key = rcu_dereference_check(info->send_key, lockdep_sock_is_held(sk)); + if (send_key) + opt->send_keyid = send_key->send_id; + else + opt->send_keyid = 0; + opt->send_rnextkeyid = info->send_rnextkeyid; + opt->recv_keyid = info->recv_keyid; + opt->recv_rnextkeyid = info->recv_rnextkeyid; return 0; } #define TCP_AUTHOPT_KEY_KNOWN_FLAGS ( \ @@ -1410,11 +1496,11 @@ int __tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb, NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTHOPTFAILURE); print_tcpao_notice("TCP Authentication Unexpected: Rejected", skb); return -EINVAL; } print_tcpao_notice("TCP Authentication Unexpected: Accepted", skb); - return 0; + goto accept; } if (opt && !key) { /* Keys are configured for peer but with different keyid than packet */ NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTHOPTFAILURE); print_tcpao_notice("TCP Authentication Failed", skb); @@ -1433,10 +1519,25 @@ int __tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb, NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTHOPTFAILURE); print_tcpao_notice("TCP Authentication Failed", skb); return -EINVAL; } +accept: + /* Doing this for all valid packets will results in keyids temporarily + * flipping back and forth if packets are reordered or retransmitted + * but keys should eventually stabilize. + * + * This is connection-specific so don't store for listen sockets. + * + * We could store rnextkeyid from SYN in a request sock and use it for + * the SYNACK but we don't. + */ + if (sk->sk_state != TCP_LISTEN) { + info->recv_keyid = opt->keyid; + info->recv_rnextkeyid = opt->rnextkeyid; + } + return 1; } EXPORT_SYMBOL(__tcp_authopt_inbound_check); static int tcp_authopt_init_net(struct net *full_net) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 6b63175d0221..4ce0580edf98 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -664,11 +664,11 @@ static int tcp_v4_authopt_handle_reply(const struct sock *sk, info = tcp_twsk(sk)->tw_authopt_info; else info = rcu_dereference_check(tcp_sk(sk)->authopt_info, lockdep_sock_is_held(sk)); if (!info) return 0; - key_info = __tcp_authopt_select_key(sk, info, sk, &rnextkeyid); + key_info = __tcp_authopt_select_key(sk, info, sk, &rnextkeyid, false); if (!key_info) return 0; *optptr = htonl((TCPOPT_AUTHOPT << 24) | (TCPOLEN_AUTHOPT_OUTPUT << 16) | (key_info->send_id << 8) | diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index a51fb759ecf2..13048d74dc93 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -901,11 +901,11 @@ static int tcp_v6_send_response_init_authopt(const struct sock *sk, *info = tcp_twsk(sk)->tw_authopt_info; else *info = rcu_dereference(tcp_sk(sk)->authopt_info); if (!*info) return 0; - *key = __tcp_authopt_select_key(sk, *info, sk, rnextkeyid); + *key = __tcp_authopt_select_key(sk, *info, sk, rnextkeyid, false); if (*key) return TCPOLEN_AUTHOPT_OUTPUT; return 0; } From patchwork Thu Dec 23 15:40:08 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698421 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CC87DC433EF for ; Thu, 23 Dec 2021 15:41:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349210AbhLWPlQ (ORCPT ); Thu, 23 Dec 2021 10:41:16 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33864 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349136AbhLWPlD (ORCPT ); Thu, 23 Dec 2021 10:41:03 -0500 Received: from mail-ed1-x52e.google.com (mail-ed1-x52e.google.com [IPv6:2a00:1450:4864:20::52e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 694A0C061401; Thu, 23 Dec 2021 07:41:03 -0800 (PST) Received: by mail-ed1-x52e.google.com with SMTP id q14so15337711edi.3; Thu, 23 Dec 2021 07:41:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=lTEj+LxvEzuxVm46pzDOwbgchyNN/wJR68u77BkTKxU=; b=hvBxfk9AqiiR9zaf0irYbvpCvk9Fe/2mYHo+nKwcQ9zwv5YpoJqp7NJLLK2QG8hH4+ lO0wJHCaZeArGCH2Yv4XC6LQmlbxXiye4VTqzu+g0ulDwgW3SfT2LHP/z9pnSaZeky6G ZkKyzw6xEB9RoZFD6ttVN4pozXWg0GzqC15vMxyT8Lu9eJ5kP4qNN03HHy6F3kIlD90y 4gWXjcdEDWotrocF36TmhtFVa2hH53QLWX3IdBEENmOCeV4zGnDUrxhJTDkBhlPmlB9t ZYNDIXZ+4E4/JFdmYufRkpqyb0Qi0Kbnv0a90OwmLqL6/tObBIs5zetKZJ1nRUzSNw3Q Io1A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=lTEj+LxvEzuxVm46pzDOwbgchyNN/wJR68u77BkTKxU=; b=EaFM7lbJojAcRcJ8wcV0BbwbTJvTC5kdwtZ9WHzl5F0UEWRKXkct+1f/lB+GJtjB5+ tALQ98I9kwY9z1M36nkBzLNwLiF2rQUUtG7vbzDGL1mR2Yi4w+5tZKSxmBGv29/DMJtw LrLGUCydDCTxyebLTlAZjJUFYAQ3g/EiyrljZbhOUENNHGchcp+mOzJpo8OSaaBE2fHl uLe+JzDFTjnLTVCotRNiW/xHl3oGUedmXxe674oc0/BTB55+t4e76oLZUh6Qkuv2bExF W1+y5YcBhbRlIYuFYbZ68Ub6tVjsmUsYPx1oHJ3LZX4UYQGGq0s2tlIH7apdK4VmZ/lt Lz/Q== X-Gm-Message-State: AOAM5324GdqcEkNrWX7tZeS6kJTEx7ewkySkBZ0Jyv0AMrHXhDVzM1Q7 NfAdz4BN7yrCR2Z5SlfTa0TfPTPxeqQ89g== X-Google-Smtp-Source: ABdhPJxjwBY91zTIBAkwhEI4m3gAMJYZRhI8UebtZdIrvU+HElcQcS5QeZnHafLwj3Apd/eI68sm4Q== X-Received: by 2002:a05:6402:50c7:: with SMTP id h7mr2492477edb.277.1640274061846; Thu, 23 Dec 2021 07:41:01 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.41.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:41:01 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 13/19] tcp: authopt: Add initial l3index support Date: Thu, 23 Dec 2021 17:40:08 +0200 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org This is a parallel feature to tcp_md5sig.tcpm_ifindex support and allows applications to server multiple VRFs with a single socket. The ifindex argument must be the ifindex of a VRF device and must match exactly, keys with ifindex == 0 (outside of VRF) will not match for connections inside a VRF. Keys without the TCP_AUTHOPT_KEY_IFINDEX will ignore ifindex and match both inside and outside VRF. Signed-off-by: Leonard Crestez --- Documentation/networking/tcp_authopt.rst | 1 + include/net/tcp_authopt.h | 2 + include/uapi/linux/tcp.h | 11 ++++ net/ipv4/tcp_authopt.c | 71 ++++++++++++++++++++++-- 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/Documentation/networking/tcp_authopt.rst b/Documentation/networking/tcp_authopt.rst index f29fdea7769f..f681d2221ce3 100644 --- a/Documentation/networking/tcp_authopt.rst +++ b/Documentation/networking/tcp_authopt.rst @@ -37,10 +37,11 @@ expand over time by increasing the size of `struct tcp_authopt_key` and adding new flags. * Address binding is optional, by default keys match all addresses * Local address is ignored, matching is done by remote address * Ports are ignored + * It is possible to match a specific VRF by l3index (default is to ignore) RFC5925 requires that key ids do not overlap when tcp identifiers (addr/port) overlap. This is not enforced by linux, configuring ambiguous keys will result in packet drops and lost connections. diff --git a/include/net/tcp_authopt.h b/include/net/tcp_authopt.h index 3d03fbb186ef..800fde277239 100644 --- a/include/net/tcp_authopt.h +++ b/include/net/tcp_authopt.h @@ -45,10 +45,12 @@ struct tcp_authopt_key_info { u8 alg_id; /** @keylen: Same as &tcp_authopt_key.keylen */ u8 keylen; /** @key: Same as &tcp_authopt_key.key */ u8 key[TCP_AUTHOPT_MAXKEYLEN]; + /** @l3index: Same as &tcp_authopt_key.ifindex */ + int l3index; /** @addr: Same as &tcp_authopt_key.addr */ struct sockaddr_storage addr; /** @alg: Algorithm implementation matching alg_id */ struct tcp_authopt_alg_imp *alg; }; diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index e02176390519..a7f5f918ed5a 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -400,15 +400,17 @@ struct tcp_authopt { * enum tcp_authopt_key_flag - flags for `tcp_authopt.flags` * * @TCP_AUTHOPT_KEY_DEL: Delete the key and ignore non-id fields * @TCP_AUTHOPT_KEY_EXCLUDE_OPTS: Exclude TCP options from signature * @TCP_AUTHOPT_KEY_ADDR_BIND: Key only valid for `tcp_authopt.addr` + * @TCP_AUTHOPT_KEY_IFINDEX: Key only valid for `tcp_authopt.ifindex` */ enum tcp_authopt_key_flag { TCP_AUTHOPT_KEY_DEL = (1 << 0), TCP_AUTHOPT_KEY_EXCLUDE_OPTS = (1 << 1), TCP_AUTHOPT_KEY_ADDR_BIND = (1 << 2), + TCP_AUTHOPT_KEY_IFINDEX = (1 << 3), }; /** * enum tcp_authopt_alg - Algorithms for TCP Authentication Option */ @@ -450,10 +452,19 @@ struct tcp_authopt_key { * @addr: Key is only valid for this address * * Ignored unless TCP_AUTHOPT_KEY_ADDR_BIND flag is set */ struct __kernel_sockaddr_storage addr; + /** + * @ifindex: ifindex of vrf (l3mdev_master) interface + * + * If the TCP_AUTHOPT_KEY_IFINDEX flag is set then key only applies for + * connections through this interface. Interface must be an vrf master. + * + * This is similar to `tcp_msg5sig.tcpm_ifindex` + */ + int ifindex; }; /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index f0d7a7facfb9..f3e244d036c3 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include #include @@ -261,10 +262,14 @@ static bool tcp_authopt_key_match_exact(struct tcp_authopt_key_info *info, { if (info->send_id != key->send_id) return false; if (info->recv_id != key->recv_id) return false; + if ((info->flags & TCP_AUTHOPT_KEY_IFINDEX) != (key->flags & TCP_AUTHOPT_KEY_IFINDEX)) + return false; + if ((info->flags & TCP_AUTHOPT_KEY_IFINDEX) && info->l3index != key->ifindex) + return false; if ((info->flags & TCP_AUTHOPT_KEY_ADDR_BIND) != (key->flags & TCP_AUTHOPT_KEY_ADDR_BIND)) return false; if (info->flags & TCP_AUTHOPT_KEY_ADDR_BIND) if (!ipvx_addr_match(&info->addr, &key->addr)) return false; @@ -328,26 +333,49 @@ static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct so return key_info; return NULL; } +static bool better_key_match(struct tcp_authopt_key_info *old, struct tcp_authopt_key_info *new) +{ + if (!old) + return true; + + /* l3index always overrides non-l3index */ + if (old->l3index && new->l3index == 0) + return false; + if (old->l3index == 0 && new->l3index) + return true; + + return false; +} + static struct tcp_authopt_key_info *tcp_authopt_lookup_send(struct netns_tcp_authopt *net, const struct sock *addr_sk, int send_id) { struct tcp_authopt_key_info *result = NULL; struct tcp_authopt_key_info *key; + int l3index = -1; hlist_for_each_entry_rcu(key, &net->head, node, 0) { if (send_id >= 0 && key->send_id != send_id) continue; if (key->flags & TCP_AUTHOPT_KEY_ADDR_BIND) if (!tcp_authopt_key_match_sk_addr(key, addr_sk)) continue; - if (result && net_ratelimit()) - pr_warn("ambiguous tcp authentication keys configured for send\n"); - result = key; + if (key->flags & TCP_AUTHOPT_KEY_IFINDEX) { + if (l3index < 0) + l3index = l3mdev_master_ifindex_by_index(sock_net(addr_sk), + addr_sk->sk_bound_dev_if); + if (l3index != key->l3index) + continue; + } + if (better_key_match(result, key)) + result = key; + else if (result) + net_warn_ratelimited("ambiguous tcp authentication keys configured for send\n"); } return result; } @@ -578,19 +606,21 @@ int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *opt) } #define TCP_AUTHOPT_KEY_KNOWN_FLAGS ( \ TCP_AUTHOPT_KEY_DEL | \ TCP_AUTHOPT_KEY_EXCLUDE_OPTS | \ - TCP_AUTHOPT_KEY_ADDR_BIND) + TCP_AUTHOPT_KEY_ADDR_BIND | \ + TCP_AUTHOPT_KEY_IFINDEX) int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) { struct tcp_authopt_key opt; struct tcp_authopt_info *info; struct tcp_authopt_key_info *key_info, *old_key_info; struct netns_tcp_authopt *net = sock_net_tcp_authopt(sk); struct tcp_authopt_alg_imp *alg; + int l3index = 0; int err; sock_owned_by_me(sk); err = check_sysctl_tcp_authopt(); if (err) @@ -641,10 +671,24 @@ int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) return -EINVAL; err = tcp_authopt_alg_require(alg); if (err) return err; + /* check ifindex is valid (zero is always valid) */ + if (opt.flags & TCP_AUTHOPT_KEY_IFINDEX && opt.ifindex) { + struct net_device *dev; + + rcu_read_lock(); + dev = dev_get_by_index_rcu(sock_net(sk), opt.ifindex); + if (dev && netif_is_l3_master(dev)) + l3index = dev->ifindex; + rcu_read_unlock(); + + if (!l3index) + return -EINVAL; + } + key_info = sock_kmalloc(sk, sizeof(*key_info), GFP_KERNEL | __GFP_ZERO); if (!key_info) return -ENOMEM; mutex_lock(&net->mutex); kref_init(&key_info->ref); @@ -660,10 +704,11 @@ int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) key_info->alg_id = opt.alg; key_info->alg = alg; key_info->keylen = opt.keylen; memcpy(key_info->key, opt.key, opt.keylen); memcpy(&key_info->addr, &opt.addr, sizeof(key_info->addr)); + key_info->l3index = l3index; hlist_add_head_rcu(&key_info->node, &net->head); mutex_unlock(&net->mutex); return 0; } @@ -1423,21 +1468,37 @@ static struct tcp_authopt_key_info *tcp_authopt_lookup_recv(struct sock *sk, int recv_id, bool *anykey) { struct tcp_authopt_key_info *result = NULL; struct tcp_authopt_key_info *key; + int l3index = -1; *anykey = false; /* multiple matches will cause occasional failures */ hlist_for_each_entry_rcu(key, &net->head, node, 0) { if (key->flags & TCP_AUTHOPT_KEY_ADDR_BIND && !tcp_authopt_key_match_skb_addr(key, skb)) continue; + if (key->flags & TCP_AUTHOPT_KEY_IFINDEX) { + if (l3index < 0) { + if (skb->protocol == htons(ETH_P_IP)) { + l3index = inet_sdif(skb) ? inet_iif(skb) : 0; + } else if (skb->protocol == htons(ETH_P_IPV6)) { + l3index = inet6_sdif(skb) ? inet6_iif(skb) : 0; + } else { + WARN_ONCE(1, "unexpected skb->protocol=%x", skb->protocol); + continue; + } + } + + if (l3index != key->l3index) + continue; + } *anykey = true; if (recv_id >= 0 && key->recv_id != recv_id) continue; - if (!result) + if (better_key_match(result, key)) result = key; else if (result) net_warn_ratelimited("ambiguous tcp authentication keys configured for recv\n"); } From patchwork Thu Dec 23 15:40:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698425 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9DDCBC4167B for ; Thu, 23 Dec 2021 15:41:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349241AbhLWPlW (ORCPT ); Thu, 23 Dec 2021 10:41:22 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33916 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349102AbhLWPlF (ORCPT ); Thu, 23 Dec 2021 10:41:05 -0500 Received: from mail-ed1-x535.google.com (mail-ed1-x535.google.com [IPv6:2a00:1450:4864:20::535]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E5CB1C06175C; Thu, 23 Dec 2021 07:41:04 -0800 (PST) Received: by mail-ed1-x535.google.com with SMTP id bm14so22946861edb.5; Thu, 23 Dec 2021 07:41:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=TeRKucehsWGRQRB4QdxreoInCb5o1eayi99lDN4kmfs=; b=ShoE4C3BJ2bZ1XkL3fDxB+GL2U8hd0mO0ooTgg3Hkdpb0HcHc3m7a88VcLOsaknqTH KExbihYLxc37V09/DGE6ZlhwzSFFPrQOPlB+qDWfrJahudS9tLR9luxxeEzZTqVx2m0q /vkX5S/A2HaEAlfLg6Fi7OKOgvH1AZzTh1EXH/4skVewkxi+r25+PSwqqItTIYaK+moL 14+qCEQpp1brPQ2BneuZ7KzGt9hnO84W2KgDjnAglQsFW2JvaDvVBDgi7piRq07UKUgE jE70/w3M9BukYfA3v/TtV+R0IU/yD7tEYHLF2hV1b1Ecvnh5+5HHhv8ibObIKzKBYmbM ZZCQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=TeRKucehsWGRQRB4QdxreoInCb5o1eayi99lDN4kmfs=; b=tj4dzwB8rZTKByzkbC9x9IBd8h2U0qKqp9gaFz5rFtZ0su9Tb7s0XIHXg8nS7tHSRg sWWPCk7InGVUuzptxsS3NJiSxS25Qr8dyM6rAHA0futjZJF8Gy1ItZIV/1bcqqJmW+F6 SKeTOzaqjLEmT6Xlfe/hulW+ZwqZTnso4TtihasihaC/6mx+j9HgBmWuuas1jmw58p5p f+Ba4G13GlSLd3st4z5Y1lmk/DUs/NttO6xj4/kf6wiCgvcQtGMJLIx0HhqNc4IajI+b l09XTilmWaqR+jqbuuVfs/S+nkuuIv/zeKhqgET76joUKHH3g/hmLxblVpLFDhX6NJIE mcbw== X-Gm-Message-State: AOAM5336ZUrkrI/nSSpoF69CaZp0dtMCscUGbTFjGcntfbyTj4S7JrLj FZ7AYGWSWm6406L7OBHMoTM= X-Google-Smtp-Source: ABdhPJxthFan6y+KWhGlhqflHHV2+HpQOtwMFkGMq2yw6m+ZqatCst3ceYdpwSNFB/STsWgeG0N/6Q== X-Received: by 2002:a17:907:a406:: with SMTP id sg6mr2352872ejc.171.1640274063520; Thu, 23 Dec 2021 07:41:03 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.41.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:41:03 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 14/19] tcp: authopt: Add NOSEND/NORECV flags Date: Thu, 23 Dec 2021 17:40:09 +0200 Message-Id: <4e3efe908b6c56bbb80f931333c3c32f6a68733a.1640273966.git.cdleonard@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Add flags to allow marking individual keys and invalid for send or recv. Making keys assymetric this way is not mentioned in RFC5925 but RFC8177 requires that keys inside a keychain have independent "accept" and "send" lifetimes. Flag names are negative so that the default behavior is for keys to be valid for both send and recv. Setting both NOSEND and NORECV for a certain peer address can be used on a listen socket can be used to mean "TCP-AO is required from this peer but no keys are currently valid". Signed-off-by: Leonard Crestez --- include/uapi/linux/tcp.h | 4 ++++ net/ipv4/tcp_authopt.c | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index a7f5f918ed5a..ed27feb93b0e 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -401,16 +401,20 @@ struct tcp_authopt { * * @TCP_AUTHOPT_KEY_DEL: Delete the key and ignore non-id fields * @TCP_AUTHOPT_KEY_EXCLUDE_OPTS: Exclude TCP options from signature * @TCP_AUTHOPT_KEY_ADDR_BIND: Key only valid for `tcp_authopt.addr` * @TCP_AUTHOPT_KEY_IFINDEX: Key only valid for `tcp_authopt.ifindex` + * @TCP_AUTHOPT_KEY_NOSEND: Key invalid for send (expired) + * @TCP_AUTHOPT_KEY_NORECV: Key invalid for recv (expired) */ enum tcp_authopt_key_flag { TCP_AUTHOPT_KEY_DEL = (1 << 0), TCP_AUTHOPT_KEY_EXCLUDE_OPTS = (1 << 1), TCP_AUTHOPT_KEY_ADDR_BIND = (1 << 2), TCP_AUTHOPT_KEY_IFINDEX = (1 << 3), + TCP_AUTHOPT_KEY_NOSEND = (1 << 4), + TCP_AUTHOPT_KEY_NORECV = (1 << 5), }; /** * enum tcp_authopt_alg - Algorithms for TCP Authentication Option */ diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index f3e244d036c3..c598f3cf72d5 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -358,10 +358,12 @@ static struct tcp_authopt_key_info *tcp_authopt_lookup_send(struct netns_tcp_aut int l3index = -1; hlist_for_each_entry_rcu(key, &net->head, node, 0) { if (send_id >= 0 && key->send_id != send_id) continue; + if (key->flags & TCP_AUTHOPT_KEY_NOSEND) + continue; if (key->flags & TCP_AUTHOPT_KEY_ADDR_BIND) if (!tcp_authopt_key_match_sk_addr(key, addr_sk)) continue; if (key->flags & TCP_AUTHOPT_KEY_IFINDEX) { if (l3index < 0) @@ -607,11 +609,13 @@ int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *opt) #define TCP_AUTHOPT_KEY_KNOWN_FLAGS ( \ TCP_AUTHOPT_KEY_DEL | \ TCP_AUTHOPT_KEY_EXCLUDE_OPTS | \ TCP_AUTHOPT_KEY_ADDR_BIND | \ - TCP_AUTHOPT_KEY_IFINDEX) + TCP_AUTHOPT_KEY_IFINDEX | \ + TCP_AUTHOPT_KEY_NOSEND | \ + TCP_AUTHOPT_KEY_NORECV) int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) { struct tcp_authopt_key opt; struct tcp_authopt_info *info; @@ -1492,10 +1496,13 @@ static struct tcp_authopt_key_info *tcp_authopt_lookup_recv(struct sock *sk, if (l3index != key->l3index) continue; } *anykey = true; + // If only keys with norecv flag are present still consider that + if (key->flags & TCP_AUTHOPT_KEY_NORECV) + continue; if (recv_id >= 0 && key->recv_id != recv_id) continue; if (better_key_match(result, key)) result = key; else if (result) From patchwork Thu Dec 23 15:40:10 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698428 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5DA8AC433FE for ; Thu, 23 Dec 2021 15:41:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349306AbhLWPlc (ORCPT ); Thu, 23 Dec 2021 10:41:32 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33938 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349103AbhLWPlH (ORCPT ); Thu, 23 Dec 2021 10:41:07 -0500 Received: from mail-ed1-x534.google.com (mail-ed1-x534.google.com [IPv6:2a00:1450:4864:20::534]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EEDD4C061792; Thu, 23 Dec 2021 07:41:06 -0800 (PST) Received: by mail-ed1-x534.google.com with SMTP id m21so24214737edc.0; Thu, 23 Dec 2021 07:41:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=rEhPZkA/AwOo/pza/2HZJSP1cTNQTQZNl+J27uwUZ7g=; b=WsIW1hysqf398eRsPZ+fFAL7c2qABZUk5IrbaaLan7cwW15pw18ouzu7uqnBan4axr BW6BT2AIvwnHGYoJ2rrjYdOHuYQ6NmHuQLu7iOXzrC6mrLgd8aYBuBW7bPEV682oxa+n /BOBR3rORac6KTEGDlROhrO4yWoKWCtJsMif4965W+xm5Rt2DLiYb6NawPs+4p2ALBX1 ChuIhrhNz4oYsP5EKUJtYVmocoty5mA+f1d/3HefI3acOCBlaRhy10Z+fZnJhHMD+hYL gth1QQ/nQCygcDqfoG/kPj45anp0EEj62K4YG9/B6QFIEfa4Y6cPw0mwQkGl1bSXGxSx pFrw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=rEhPZkA/AwOo/pza/2HZJSP1cTNQTQZNl+J27uwUZ7g=; b=sI7YQ4N6OiEdw5258m8zWQJrWPPDeGE+zWXhFwYs9bmuGA55VyEGzADE2I7/tVx9I1 WeRnWzsw1b/BnuXuZRl4mBiooYxmeUgqSnfB6ALlpUAC72+WxV7lLmhHWyw7dvAl8423 ZQZDKQbCxnAH6H1undlODwZSyVYkCd6kb7S62B+CY2cLzghLkNUmQqUIpixgpPODOHee V0hchPnDPvqZWuAW6hcayvhgSdowKg5KVetN3SOAnQMdkjUDDURSJVavTRkbpxmJnbiw FNgloUlammKjSk5vVOSd92vMl+L07mlrJacD1Z+Fy+qJHXK2hFpEwlqDdZEu6tl7F2Hz ZNVA== X-Gm-Message-State: AOAM532BOs1oP2GfUhI4U1dffD9qQpUViKSr+5sS5TZ1JxYzyqKjBJjI OmGzktL/m9IwkZS12B3Z9XA= X-Google-Smtp-Source: ABdhPJysF1sO2gThsWcUfiaskTEUc/nRXerkqu/ktvs+bqvLrAbTBDZ6aQDpX2yWVuH9b2/FNr7S9g== X-Received: by 2002:a17:906:4790:: with SMTP id cw16mr2299544ejc.493.1640274065280; Thu, 23 Dec 2021 07:41:05 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.41.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:41:04 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 15/19] tcp: authopt: Add prefixlen support Date: Thu, 23 Dec 2021 17:40:10 +0200 Message-Id: <1d918bde7fdb13a05c899e519e833671cb5fbe5a.1640273966.git.cdleonard@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org This allows making a key apply to an addr/prefix instead of just the full addr. This is enabled through a custom flag, default behavior is still full address match. This is equivalent to TCP_MD5SIG_FLAG_PREFIX from TCP_MD5SIG and has the same use-cases. Signed-off-by: Leonard Crestez --- Documentation/networking/tcp_authopt.rst | 1 + include/net/tcp_authopt.h | 2 + include/uapi/linux/tcp.h | 10 ++++ net/ipv4/tcp_authopt.c | 63 ++++++++++++++++++++++-- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/Documentation/networking/tcp_authopt.rst b/Documentation/networking/tcp_authopt.rst index f681d2221ce3..6520c6d02755 100644 --- a/Documentation/networking/tcp_authopt.rst +++ b/Documentation/networking/tcp_authopt.rst @@ -38,10 +38,11 @@ new flags. * Address binding is optional, by default keys match all addresses * Local address is ignored, matching is done by remote address * Ports are ignored * It is possible to match a specific VRF by l3index (default is to ignore) + * It is possible to match with a fixed prefixlen (default is full address) RFC5925 requires that key ids do not overlap when tcp identifiers (addr/port) overlap. This is not enforced by linux, configuring ambiguous keys will result in packet drops and lost connections. diff --git a/include/net/tcp_authopt.h b/include/net/tcp_authopt.h index 800fde277239..743248904552 100644 --- a/include/net/tcp_authopt.h +++ b/include/net/tcp_authopt.h @@ -47,10 +47,12 @@ struct tcp_authopt_key_info { u8 keylen; /** @key: Same as &tcp_authopt_key.key */ u8 key[TCP_AUTHOPT_MAXKEYLEN]; /** @l3index: Same as &tcp_authopt_key.ifindex */ int l3index; + /** @prefix: Length of addr match (default full) */ + int prefixlen; /** @addr: Same as &tcp_authopt_key.addr */ struct sockaddr_storage addr; /** @alg: Algorithm implementation matching alg_id */ struct tcp_authopt_alg_imp *alg; }; diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index ed27feb93b0e..b1063e1e1b9f 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -403,18 +403,21 @@ struct tcp_authopt { * @TCP_AUTHOPT_KEY_EXCLUDE_OPTS: Exclude TCP options from signature * @TCP_AUTHOPT_KEY_ADDR_BIND: Key only valid for `tcp_authopt.addr` * @TCP_AUTHOPT_KEY_IFINDEX: Key only valid for `tcp_authopt.ifindex` * @TCP_AUTHOPT_KEY_NOSEND: Key invalid for send (expired) * @TCP_AUTHOPT_KEY_NORECV: Key invalid for recv (expired) + * @TCP_AUTHOPT_KEY_PREFIXLEN: Valid value in `tcp_authopt.prefixlen`, otherwise + * match full address length */ enum tcp_authopt_key_flag { TCP_AUTHOPT_KEY_DEL = (1 << 0), TCP_AUTHOPT_KEY_EXCLUDE_OPTS = (1 << 1), TCP_AUTHOPT_KEY_ADDR_BIND = (1 << 2), TCP_AUTHOPT_KEY_IFINDEX = (1 << 3), TCP_AUTHOPT_KEY_NOSEND = (1 << 4), TCP_AUTHOPT_KEY_NORECV = (1 << 5), + TCP_AUTHOPT_KEY_PREFIXLEN = (1 << 6), }; /** * enum tcp_authopt_alg - Algorithms for TCP Authentication Option */ @@ -465,10 +468,17 @@ struct tcp_authopt_key { * connections through this interface. Interface must be an vrf master. * * This is similar to `tcp_msg5sig.tcpm_ifindex` */ int ifindex; + /** + * @prefixlen: length of prefix to match + * + * Without the TCP_AUTHOPT_KEY_PREFIXLEN flag this is ignored and a full + * address match is performed. + */ + int prefixlen; }; /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index c598f3cf72d5..64bcb2a38472 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -4,10 +4,11 @@ #include #include #include #include #include +#include /* This is mainly intended to protect against local privilege escalations through * a rarely used feature so it is deliberately not namespaced. */ int sysctl_tcp_authopt; @@ -266,10 +267,14 @@ static bool tcp_authopt_key_match_exact(struct tcp_authopt_key_info *info, return false; if ((info->flags & TCP_AUTHOPT_KEY_IFINDEX) != (key->flags & TCP_AUTHOPT_KEY_IFINDEX)) return false; if ((info->flags & TCP_AUTHOPT_KEY_IFINDEX) && info->l3index != key->ifindex) return false; + if ((info->flags & TCP_AUTHOPT_KEY_PREFIXLEN) != (key->flags & TCP_AUTHOPT_KEY_PREFIXLEN)) + return false; + if ((info->flags & TCP_AUTHOPT_KEY_PREFIXLEN) && info->prefixlen != key->prefixlen) + return false; if ((info->flags & TCP_AUTHOPT_KEY_ADDR_BIND) != (key->flags & TCP_AUTHOPT_KEY_ADDR_BIND)) return false; if (info->flags & TCP_AUTHOPT_KEY_ADDR_BIND) if (!ipvx_addr_match(&info->addr, &key->addr)) return false; @@ -283,17 +288,20 @@ static bool tcp_authopt_key_match_skb_addr(struct tcp_authopt_key_info *key, u16 keyaf = key->addr.ss_family; struct iphdr *iph = (struct iphdr *)skb_network_header(skb); if (keyaf == AF_INET && iph->version == 4) { struct sockaddr_in *key_addr = (struct sockaddr_in *)&key->addr; + __be32 mask = inet_make_mask(key->prefixlen); - return iph->saddr == key_addr->sin_addr.s_addr; + return (iph->saddr & mask) == key_addr->sin_addr.s_addr; } else if (keyaf == AF_INET6 && iph->version == 6) { struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb); struct sockaddr_in6 *key_addr = (struct sockaddr_in6 *)&key->addr; - return ipv6_addr_equal(&ip6h->saddr, &key_addr->sin6_addr); + return ipv6_prefix_equal(&ip6h->saddr, + &key_addr->sin6_addr, + key->prefixlen); } /* This actually happens with ipv6-mapped-ipv4-addresses * IPv6 listen sockets will be asked to validate ipv4 packets. */ @@ -309,16 +317,19 @@ static bool tcp_authopt_key_match_sk_addr(struct tcp_authopt_key_info *key, if (keyaf != addr_sk->sk_family) return false; if (keyaf == AF_INET) { struct sockaddr_in *key_addr = (struct sockaddr_in *)&key->addr; + __be32 mask = inet_make_mask(key->prefixlen); - return addr_sk->sk_daddr == key_addr->sin_addr.s_addr; + return (addr_sk->sk_daddr & mask) == key_addr->sin_addr.s_addr; } else if (keyaf == AF_INET6) { struct sockaddr_in6 *key_addr = (struct sockaddr_in6 *)&key->addr; - return ipv6_addr_equal(&addr_sk->sk_v6_daddr, &key_addr->sin6_addr); + return ipv6_prefix_equal(&addr_sk->sk_v6_daddr, + &key_addr->sin6_addr, + key->prefixlen); } return false; } @@ -343,10 +354,16 @@ static bool better_key_match(struct tcp_authopt_key_info *old, struct tcp_authop /* l3index always overrides non-l3index */ if (old->l3index && new->l3index == 0) return false; if (old->l3index == 0 && new->l3index) return true; + /* Full address match overrides match by prefixlen */ + if (!(new->flags & TCP_AUTHOPT_KEY_PREFIXLEN) && (old->flags & TCP_AUTHOPT_KEY_PREFIXLEN)) + return false; + /* Longer prefixes are better matches */ + if (new->prefixlen > old->prefixlen) + return true; return false; } static struct tcp_authopt_key_info *tcp_authopt_lookup_send(struct netns_tcp_authopt *net, @@ -610,21 +627,32 @@ int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *opt) #define TCP_AUTHOPT_KEY_KNOWN_FLAGS ( \ TCP_AUTHOPT_KEY_DEL | \ TCP_AUTHOPT_KEY_EXCLUDE_OPTS | \ TCP_AUTHOPT_KEY_ADDR_BIND | \ TCP_AUTHOPT_KEY_IFINDEX | \ + TCP_AUTHOPT_KEY_PREFIXLEN | \ TCP_AUTHOPT_KEY_NOSEND | \ TCP_AUTHOPT_KEY_NORECV) +static bool ipv6_addr_is_prefix(struct in6_addr *addr, int plen) +{ + struct in6_addr copy; + + ipv6_addr_prefix_copy(©, addr, plen); + + return !!memcmp(©, addr, sizeof(*addr)); +} + int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) { struct tcp_authopt_key opt; struct tcp_authopt_info *info; struct tcp_authopt_key_info *key_info, *old_key_info; struct netns_tcp_authopt *net = sock_net_tcp_authopt(sk); struct tcp_authopt_alg_imp *alg; int l3index = 0; + int prefixlen; int err; sock_owned_by_me(sk); err = check_sysctl_tcp_authopt(); if (err) @@ -660,10 +688,36 @@ int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) if (opt.flags & TCP_AUTHOPT_KEY_ADDR_BIND) { if (sk->sk_family != opt.addr.ss_family) return -EINVAL; } + /* check prefixlen */ + if (opt.flags & TCP_AUTHOPT_KEY_PREFIXLEN) { + prefixlen = opt.prefixlen; + if (sk->sk_family == AF_INET) { + if (prefixlen < 0 || prefixlen > 32) + return -EINVAL; + if (((struct sockaddr_in *)&opt.addr)->sin_addr.s_addr & + ~inet_make_mask(prefixlen)) + return -EINVAL; + } + if (sk->sk_family == AF_INET6) { + if (prefixlen < 0 || prefixlen > 128) + return -EINVAL; + if (!ipv6_addr_is_prefix(&((struct sockaddr_in6 *)&opt.addr)->sin6_addr, + prefixlen)) + return -EINVAL; + } + } else { + if (sk->sk_family == AF_INET) + prefixlen = 32; + else if (sk->sk_family == AF_INET6) + prefixlen = 128; + else + return -EINVAL; + } + /* Initialize tcp_authopt_info if not already set */ info = __tcp_authopt_info_get_or_create(sk); if (IS_ERR(info)) return PTR_ERR(info); @@ -709,10 +763,11 @@ int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) key_info->alg = alg; key_info->keylen = opt.keylen; memcpy(key_info->key, opt.key, opt.keylen); memcpy(&key_info->addr, &opt.addr, sizeof(key_info->addr)); key_info->l3index = l3index; + key_info->prefixlen = prefixlen; hlist_add_head_rcu(&key_info->node, &net->head); mutex_unlock(&net->mutex); return 0; } From patchwork Thu Dec 23 15:40:11 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698427 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D3E14C433FE for ; Thu, 23 Dec 2021 15:41:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349176AbhLWPlb (ORCPT ); Thu, 23 Dec 2021 10:41:31 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33950 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349168AbhLWPlJ (ORCPT ); Thu, 23 Dec 2021 10:41:09 -0500 Received: from mail-ed1-x533.google.com (mail-ed1-x533.google.com [IPv6:2a00:1450:4864:20::533]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 81BF9C06179C; Thu, 23 Dec 2021 07:41:08 -0800 (PST) Received: by mail-ed1-x533.google.com with SMTP id bm14so22947590edb.5; Thu, 23 Dec 2021 07:41:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=4iUq/OcSfjWYCqr9JUkzg33/o8TN8G5Po1f+UBWAkdc=; b=BVffC+NmjubKsmKg0fVG3XWKoVupntAcAMgFs4YXFGPNto8HftLmDl6RGM/JrhD4XC EuckqX+ZQ8u2K3mUbrj23lqN0dQd05gRfeSIHruGfRpGwCO44qU/dO3fNHLZWSOCsrC/ SbsBHxdZeUn4uORnXRYICAVqghtcTAnGke0gpmdn7EhH56pjTt+RbzM7YUjOGn0MG7uu fBdSrgzI++xciR+SpEp68eFz8NhgwYimeMEDHfi9kwWQlBQqkrMOhTYIffkIRCG2/hUw Pd4qXBRJAxa9xS/kzTh2s8bcaV3kkI4Hz7lTCeZb8dqGqnH4wgWmI/FNFvMu3cYo0+NP pHFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=4iUq/OcSfjWYCqr9JUkzg33/o8TN8G5Po1f+UBWAkdc=; b=hlMahct7Ma+Gx6QtLFHNb7HzCgxG58Ttilt01656EdlOjaYRAkE73pRsMMWCj1d4xn kc8OWAl8pmUnK9QBE51JYlucm7opZbCpaR8ma5U6e2BlrGn9+UFW9vj4M9AwpnlrOlLs jruRGHeg46AxVG333/1t1lv/FP1tGXgoCTs2O5HmKI859V4adetjWOw+4OzJPGkw8YyI vhLppx5mQ65PIOR7a908yBd91RJnmzvarGPGAGoyGQyWFLSMiQI9Dr267BYvVPo1LPac /9ESvtRAX7G0GTDBz4rw3ww7+HqSfea3g5MpB66Wq+2SHF7TizqNPu87zfHdov5EbycD 2tJQ== X-Gm-Message-State: AOAM530+xhOvSqwNiKviBn6QjoGbjmJ9Uv0/4rtiEnGa1WHb0WLITKQY eRk4lP3+MVpIvjJYa1BhsDU= X-Google-Smtp-Source: ABdhPJxJHU3KajZgoUKDODBZwe3SOa5UFbtWtH4URSYNBLyZY4qEWR4/5XOTzkzxShhv08g3xH2tdQ== X-Received: by 2002:a17:906:9b91:: with SMTP id dd17mr2332545ejc.371.1640274067040; Thu, 23 Dec 2021 07:41:07 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.41.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:41:06 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 16/19] tcp: authopt: Add /proc/net/tcp_authopt listing all keys Date: Thu, 23 Dec 2021 17:40:11 +0200 Message-Id: <80510f065b3b17d36626962cd7c7719036a6f9e1.1640273966.git.cdleonard@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Signed-off-by: Leonard Crestez --- Documentation/networking/tcp_authopt.rst | 10 +++ net/ipv4/tcp_authopt.c | 100 ++++++++++++++++++++++- 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/tcp_authopt.rst b/Documentation/networking/tcp_authopt.rst index 6520c6d02755..eaf389f99139 100644 --- a/Documentation/networking/tcp_authopt.rst +++ b/Documentation/networking/tcp_authopt.rst @@ -69,10 +69,20 @@ The rnextkeyid value sent on the wire is usually the recv_id of the current key used for sending. If the TCP_AUTHOPT_LOCK_RNEXTKEY flag is set in `tcp_authopt.flags` the value of `tcp_authopt.send_rnextkeyid` is send instead. This can be used to implement smooth rollover: the peer will switch its keyid to the received rnextkeyid when it is available. +Proc interface +-------------- + +The ``/proc/net/tcp_authopt`` file contains a tab-separated table of keys. The +first line contains column names. The number of columns might increase in the +future if more matching criteria are added. Here is an example of the table:: + + flags send_id recv_id alg addr l3index + 0x44 0 0 1 10.10.2.2/31 0 + ABI Reference ============= .. kernel-doc:: include/uapi/linux/tcp.h :identifiers: tcp_authopt tcp_authopt_flag tcp_authopt_key tcp_authopt_key_flag tcp_authopt_alg diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c index 64bcb2a38472..22ce68c933a9 100644 --- a/net/ipv4/tcp_authopt.c +++ b/net/ipv4/tcp_authopt.c @@ -5,10 +5,11 @@ #include #include #include #include #include +#include /* This is mainly intended to protect against local privilege escalations through * a rarely used feature so it is deliberately not namespaced. */ int sysctl_tcp_authopt; @@ -1661,26 +1662,123 @@ int __tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb, return 1; } EXPORT_SYMBOL(__tcp_authopt_inbound_check); +struct tcp_authopt_iter_state { + struct seq_net_private p; +}; + +static struct tcp_authopt_key_info *tcp_authopt_get_key_index(struct netns_tcp_authopt *net, + int index) +{ + struct tcp_authopt_key_info *key; + + hlist_for_each_entry(key, &net->head, node) { + if (--index < 0) + return key; + } + + return NULL; +} + +static void *tcp_authopt_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(RCU) +{ + struct netns_tcp_authopt *net = &seq_file_net(seq)->tcp_authopt; + + rcu_read_lock(); + if (*pos == 0) + return SEQ_START_TOKEN; + else + return tcp_authopt_get_key_index(net, *pos - 1); +} + +static void tcp_authopt_seq_stop(struct seq_file *seq, void *v) + __releases(RCU) +{ + rcu_read_unlock(); +} + +static void *tcp_authopt_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct netns_tcp_authopt *net = &seq_file_net(seq)->tcp_authopt; + void *ret; + + ret = tcp_authopt_get_key_index(net, *pos); + ++*pos; + + return ret; +} + +static int tcp_authopt_seq_show(struct seq_file *seq, void *v) +{ + struct tcp_authopt_key_info *key = v; + + /* FIXME: Document somewhere */ + /* Key is deliberately inaccessible */ + if (v == SEQ_START_TOKEN) { + seq_puts(seq, "flags\tsend_id\trecv_id\talg\taddr\tl3index\n"); + return 0; + } + + seq_printf(seq, "0x%x\t%d\t%d\t%d", + key->flags, key->send_id, key->recv_id, (int)key->alg_id); + if (key->flags & TCP_AUTHOPT_KEY_ADDR_BIND) { + if (key->addr.ss_family == AF_INET6) + seq_printf(seq, "\t%pI6", &((struct sockaddr_in6 *)&key->addr)->sin6_addr); + else + seq_printf(seq, "\t%pI4", &((struct sockaddr_in *)&key->addr)->sin_addr); + if (key->flags & TCP_AUTHOPT_KEY_PREFIXLEN) + seq_printf(seq, "/%d", key->prefixlen); + } else { + seq_puts(seq, "\t*"); + } + seq_printf(seq, "\t%d", key->l3index); + seq_puts(seq, "\n"); + + return 0; +} + +static const struct seq_operations tcp_authopt_seq_ops = { + .start = tcp_authopt_seq_start, + .next = tcp_authopt_seq_next, + .stop = tcp_authopt_seq_stop, + .show = tcp_authopt_seq_show, +}; + +static int __net_init tcp_authopt_proc_init_net(struct net *net) +{ + if (!proc_create_net("tcp_authopt", 0400, net->proc_net, + &tcp_authopt_seq_ops, + sizeof(struct tcp_authopt_iter_state))) + return -ENOMEM; + return 0; +} + +static void __net_exit tcp_authopt_proc_exit_net(struct net *net) +{ + remove_proc_entry("tcp_authopt", net->proc_net); +} + static int tcp_authopt_init_net(struct net *full_net) { struct netns_tcp_authopt *net = &full_net->tcp_authopt; mutex_init(&net->mutex); INIT_HLIST_HEAD(&net->head); - return 0; + return tcp_authopt_proc_init_net(full_net); } static void tcp_authopt_exit_net(struct net *full_net) { struct netns_tcp_authopt *net = &full_net->tcp_authopt; struct tcp_authopt_key_info *key; struct hlist_node *n; + tcp_authopt_proc_exit_net(full_net); mutex_lock(&net->mutex); hlist_for_each_entry_safe(key, n, &net->head, node) { hlist_del_rcu(&key->node); tcp_authopt_key_put(key); From patchwork Thu Dec 23 15:40:12 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698430 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B6097C433EF for ; Thu, 23 Dec 2021 15:41:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349318AbhLWPle (ORCPT ); Thu, 23 Dec 2021 10:41:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33822 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349195AbhLWPlO (ORCPT ); Thu, 23 Dec 2021 10:41:14 -0500 Received: from mail-ed1-x52f.google.com (mail-ed1-x52f.google.com [IPv6:2a00:1450:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 37A93C06179E; Thu, 23 Dec 2021 07:41:10 -0800 (PST) Received: by mail-ed1-x52f.google.com with SMTP id b13so22893753edd.8; Thu, 23 Dec 2021 07:41:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=RVPT2+h1JMxni3JUffui8/RQgc3zJLoReDrrKPMolQw=; b=S/VYyffkEICDkIvrgYQXeTEvBKy6+EVEajZ/wtmGNdJGoiDCGeh8FVD4Bux5+UNprE BSHHlf4zzgm4aXSb1ZLdZXg/O08K0qUHHHW9QkvitWxEPz6+I05s1y9bXgaomRsUnOqp MNmH0oCkUgLdFTnEe+MKsDRsG2+1MHQIrObRZK9FEeEmIiaJxTmADxCTx0ZJnbij5rA6 0JSrV4F0O/gxx61ALYA4fBsV5K6jFpj/qn3rjkdASwUHEd9Nwq3ZJxt0q9E+ljUhz3L7 80tpfhBtBN9b3KuzJreKekLxSxFVl0SyVU2TZBGkiK1dITZxYFfmEyLwz45LH0qtLdLl eKYA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=RVPT2+h1JMxni3JUffui8/RQgc3zJLoReDrrKPMolQw=; b=3YsmGCnLu1LFIxIYKeqpCIYaqYw1ZZG8oThkzsEFkPrCChtTS7/6hmsvdj8yEQjEfF LcBsV8wR127DCF7Z4GhPSWKJLSlCWC5YCcyVXtkMevMjtcqh3EgJrcBqnXFxAByK2zMe nVCW7XCyukmBygBkpIgr24jTVdkeWM8wf6cnaZpJK9O3vJOm3LGGpxvn5IVA+k5zntf7 +HYfGqpRvqQp3kBBInsF86BoxUPykNnfzorX0q0P7pNB9vGqjkSuwgMPYtBKs6bEbiTy 61zfcrG8G9CL2PvknapaocVvmrZCt5cR4o1apIcDzVcqdD+ShtxIAjRI/py1MwvvVWQt nENA== X-Gm-Message-State: AOAM533uGdDjSfzeb/3YPDf8EZH+ui9oNbrW9jGXxG7S570b4cgDmW8T Z7gGp720V10jV628g6XV6sM= X-Google-Smtp-Source: ABdhPJyBtf3rAsrR1nabhqZCpYnBafRfHifIwQI8snd7AcFCCOBFh4xr+19LiycASdktgHkQQtUckA== X-Received: by 2002:a17:906:544f:: with SMTP id d15mr2374545ejp.373.1640274068826; Thu, 23 Dec 2021 07:41:08 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.41.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:41:08 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 17/19] selftests: nettest: Rename md5_prefix to key_addr_prefix Date: Thu, 23 Dec 2021 17:40:12 +0200 Message-Id: <5b91f7f8bed0e15ec3167ba58c8ee9019dc59231.1640273966.git.cdleonard@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org This is in preparation for reusing the same option for TCP-AO Reviewed-by: David Ahern Signed-off-by: Leonard Crestez --- tools/testing/selftests/net/nettest.c | 50 +++++++++++++-------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/tools/testing/selftests/net/nettest.c b/tools/testing/selftests/net/nettest.c index d9a6fd2cd9d3..3841e5fec7c7 100644 --- a/tools/testing/selftests/net/nettest.c +++ b/tools/testing/selftests/net/nettest.c @@ -94,17 +94,17 @@ struct sock_args { const char *clientns; const char *serverns; const char *password; const char *client_pw; - /* prefix for MD5 password */ - const char *md5_prefix_str; + /* prefix for MD5/AO*/ + const char *key_addr_prefix_str; union { struct sockaddr_in v4; struct sockaddr_in6 v6; - } md5_prefix; - unsigned int prefix_len; + } key_addr; + unsigned int key_addr_prefix_len; /* 0: default, -1: force off, +1: force on */ int bind_key_ifindex; /* expected addresses and device index for connection */ const char *expected_dev; @@ -264,16 +264,16 @@ static int tcp_md5sig(int sd, void *addr, socklen_t alen, struct sock_args *args int rc; md5sig.tcpm_keylen = keylen; memcpy(md5sig.tcpm_key, args->password, keylen); - if (args->prefix_len) { + if (args->key_addr_prefix_len) { opt = TCP_MD5SIG_EXT; md5sig.tcpm_flags |= TCP_MD5SIG_FLAG_PREFIX; - md5sig.tcpm_prefixlen = args->prefix_len; - addr = &args->md5_prefix; + md5sig.tcpm_prefixlen = args->key_addr_prefix_len; + addr = &args->key_addr; } memcpy(&md5sig.tcpm_addr, addr, alen); if ((args->ifindex && args->bind_key_ifindex >= 0) || args->bind_key_ifindex >= 1) { opt = TCP_MD5SIG_EXT; @@ -309,17 +309,17 @@ static int tcp_md5_remote(int sd, struct sock_args *args) int alen; switch (args->version) { case AF_INET: sin.sin_port = htons(args->port); - sin.sin_addr = args->md5_prefix.v4.sin_addr; + sin.sin_addr = args->key_addr.v4.sin_addr; addr = &sin; alen = sizeof(sin); break; case AF_INET6: sin6.sin6_port = htons(args->port); - sin6.sin6_addr = args->md5_prefix.v6.sin6_addr; + sin6.sin6_addr = args->key_addr.v6.sin6_addr; addr = &sin6; alen = sizeof(sin6); break; default: log_error("unknown address family\n"); @@ -705,11 +705,11 @@ enum addr_type { ADDR_TYPE_LOCAL, ADDR_TYPE_REMOTE, ADDR_TYPE_MCAST, ADDR_TYPE_EXPECTED_LOCAL, ADDR_TYPE_EXPECTED_REMOTE, - ADDR_TYPE_MD5_PREFIX, + ADDR_TYPE_KEY_PREFIX, }; static int convert_addr(struct sock_args *args, const char *_str, enum addr_type atype) { @@ -745,32 +745,32 @@ static int convert_addr(struct sock_args *args, const char *_str, break; case ADDR_TYPE_EXPECTED_REMOTE: desc = "expected remote"; addr = &args->expected_raddr; break; - case ADDR_TYPE_MD5_PREFIX: - desc = "md5 prefix"; + case ADDR_TYPE_KEY_PREFIX: + desc = "key addr prefix"; if (family == AF_INET) { - args->md5_prefix.v4.sin_family = AF_INET; - addr = &args->md5_prefix.v4.sin_addr; + args->key_addr.v4.sin_family = AF_INET; + addr = &args->key_addr.v4.sin_addr; } else if (family == AF_INET6) { - args->md5_prefix.v6.sin6_family = AF_INET6; - addr = &args->md5_prefix.v6.sin6_addr; + args->key_addr.v6.sin6_family = AF_INET6; + addr = &args->key_addr.v6.sin6_addr; } else return 1; sep = strchr(str, '/'); if (sep) { *sep = '\0'; sep++; if (str_to_uint(sep, 1, pfx_len_max, - &args->prefix_len) != 0) { - fprintf(stderr, "Invalid port\n"); + &args->key_addr_prefix_len) != 0) { + fprintf(stderr, "Invalid prefix\n"); return 1; } } else { - args->prefix_len = 0; + args->key_addr_prefix_len = 0; } break; default: log_error("unknown address type\n"); exit(1); @@ -835,13 +835,13 @@ static int validate_addresses(struct sock_args *args) if (args->remote_addr_str && convert_addr(args, args->remote_addr_str, ADDR_TYPE_REMOTE) < 0) return 1; - if (args->md5_prefix_str && - convert_addr(args, args->md5_prefix_str, - ADDR_TYPE_MD5_PREFIX) < 0) + if (args->key_addr_prefix_str && + convert_addr(args, args->key_addr_prefix_str, + ADDR_TYPE_KEY_PREFIX) < 0) return 1; if (args->expected_laddr_str && convert_addr(args, args->expected_laddr_str, ADDR_TYPE_EXPECTED_LOCAL)) @@ -2020,11 +2020,11 @@ int main(int argc, char *argv[]) break; case 'X': args.client_pw = optarg; break; case 'm': - args.md5_prefix_str = optarg; + args.key_addr_prefix_str = optarg; break; case 'S': args.use_setsockopt = 1; break; case 'f': @@ -2079,17 +2079,17 @@ int main(int argc, char *argv[]) return 1; } } if (args.password && - ((!args.has_remote_ip && !args.md5_prefix_str) || + ((!args.has_remote_ip && !args.key_addr_prefix_str) || args.type != SOCK_STREAM)) { log_error("MD5 passwords apply to TCP only and require a remote ip for the password\n"); return 1; } - if (args.md5_prefix_str && !args.password) { + if (args.key_addr_prefix_str && !args.password) { log_error("Prefix range for MD5 protection specified without a password\n"); return 1; } if (iter == 0) { From patchwork Thu Dec 23 15:40:13 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698429 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id F045AC4332F for ; Thu, 23 Dec 2021 15:41:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349342AbhLWPli (ORCPT ); Thu, 23 Dec 2021 10:41:38 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33970 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349196AbhLWPlO (ORCPT ); Thu, 23 Dec 2021 10:41:14 -0500 Received: from mail-ed1-x52f.google.com (mail-ed1-x52f.google.com [IPv6:2a00:1450:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0566DC0617A0; Thu, 23 Dec 2021 07:41:12 -0800 (PST) Received: by mail-ed1-x52f.google.com with SMTP id o6so22977903edc.4; Thu, 23 Dec 2021 07:41:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=UNC6gkaevkv7hTgw1A05+yNvh+7oeAOZgb9Vvub/3OM=; b=RbOX5jJgeSWVtpW64OXh52jw8hj4EszdNqmdKigCEgJ6npTDDynUzxCzd9h05Dh9zj mTQzI4jd4Y4ZIMzSTd2z01T75F28L4ZMDV8hiedUZDOCZ4qYfKSEpM3ZGcW4Ssh2aRs/ WquzcYK77Yu14Ofm3+CEq0267E8+K8svOL3ErBK6yQDXnoqWdZm5VLAb7nFl7T/ktAx4 oy6uxt/vgfS/UIiRnDPZ+twN1GuqprPT2JL2hN5IA1KmFcVwC3ZvshUkNPH1HKBeAMxE 1UWTrYIGmidF1GMJKDl6LkWdU7h/tbekEOmfUMLkeTG/QZw75O5KixkwOHce3ygdJOAh s+QA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=UNC6gkaevkv7hTgw1A05+yNvh+7oeAOZgb9Vvub/3OM=; b=Dj19ljdypmNO5RnqeT+FogZakHsMq+hp+UHbSehxo/e9gpqizKBOH942vgTwOVaZhJ FE12ctx0+1uzZVO2ywQXO73t+xTaB32dholGmydCGZv8N5j0fS9JQxaCFr1GOZ2BKonS LXf6Gr6V/AVnJ/mUtNsHLHrtCwxsJKz1s9wb8fz7YPtCmmepqDHU5bBVnjy9SMSJ2hbY u/trsIRfDDT6l9QnLbbH9S/5xmgAfs2bfyyevIMUPP8Awdp+7szacYW5lVKNXqtSD9lC qw7WAb09ZsfUbvgOByjxRSYIXznLIPTgiMHUSXpauPvLCFtBVAERZT4mQARnWs+v0Nvy dWdw== X-Gm-Message-State: AOAM532VMU1ToUhy1Fi4qbNvbJfsMJpDQMVSZlKslLcRMoVXhuTGFPkO Mt0a9C6Pc69GzvaBphakwAo= X-Google-Smtp-Source: ABdhPJy97s5/TIk7WxRqoK350Ay+Q1mdjUt2YP4EYnbA5ehSd2qrpuIkKfWzThfCaxYsDZC01fINVw== X-Received: by 2002:a17:907:da3:: with SMTP id go35mr2385095ejc.637.1640274070575; Thu, 23 Dec 2021 07:41:10 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.41.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:41:10 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 18/19] selftests: nettest: Initial tcp_authopt support Date: Thu, 23 Dec 2021 17:40:13 +0200 Message-Id: <3d18a48e88800cfdc0e61438ebdee636b512a7fc.1640273966.git.cdleonard@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Add support for configuring TCP Authentication Option. Only a single key is supported with default options. Reviewed-by: David Ahern Signed-off-by: Leonard Crestez --- tools/testing/selftests/net/nettest.c | 156 ++++++++++++++++++++++++-- 1 file changed, 145 insertions(+), 11 deletions(-) diff --git a/tools/testing/selftests/net/nettest.c b/tools/testing/selftests/net/nettest.c index 3841e5fec7c7..9615489230f8 100644 --- a/tools/testing/selftests/net/nettest.c +++ b/tools/testing/selftests/net/nettest.c @@ -27,10 +27,11 @@ #include #include #include #include #include +#include #include #include #include @@ -104,10 +105,12 @@ struct sock_args { } key_addr; unsigned int key_addr_prefix_len; /* 0: default, -1: force off, +1: force on */ int bind_key_ifindex; + const char *authopt_password; + /* expected addresses and device index for connection */ const char *expected_dev; const char *expected_server_dev; int expected_ifindex; @@ -254,10 +257,75 @@ static int switch_ns(const char *ns) close(fd); return ret; } +/* Fill key identification fields: address and ifindex */ +static void tcp_authopt_key_fill_id(struct tcp_authopt_key *key, struct sock_args *args) +{ + if (args->key_addr_prefix_str) { + key->flags |= TCP_AUTHOPT_KEY_ADDR_BIND; + switch (args->version) { + case AF_INET: + memcpy(&key->addr, &args->key_addr.v4, sizeof(args->key_addr.v4)); + break; + case AF_INET6: + memcpy(&key->addr, &args->key_addr.v6, sizeof(args->key_addr.v6)); + break; + default: + log_error("unknown address family\n"); + exit(1); + } + if (args->key_addr_prefix_len) { + key->flags |= TCP_AUTHOPT_KEY_PREFIXLEN; + key->prefixlen = args->key_addr_prefix_len; + } + } + + if ((args->ifindex && args->bind_key_ifindex >= 0) || args->bind_key_ifindex >= 1) { + key->flags |= TCP_AUTHOPT_KEY_IFINDEX; + key->ifindex = args->ifindex; + log_msg("TCP_AUTHOPT_KEY_IFINDEX set ifindex=%d\n", key->ifindex); + } else { + log_msg("TCP_AUTHOPT_KEY_IFINDEX off\n", key->ifindex); + } +} + +static int tcp_del_authopt(int sd, struct sock_args *args) +{ + struct tcp_authopt_key key; + int rc; + + memset(&key, 0, sizeof(key)); + key.flags |= TCP_AUTHOPT_KEY_DEL; + tcp_authopt_key_fill_id(&key, args); + + rc = setsockopt(sd, IPPROTO_TCP, TCP_AUTHOPT_KEY, &key, sizeof(key)); + if (rc < 0) + log_err_errno("setsockopt(TCP_AUTHOPT_KEY) del fail"); + + return rc; +} + +static int tcp_set_authopt(int sd, struct sock_args *args) +{ + struct tcp_authopt_key key; + int rc; + + memset(&key, 0, sizeof(key)); + strcpy((char *)key.key, args->authopt_password); + key.keylen = strlen(args->authopt_password); + key.alg = TCP_AUTHOPT_ALG_HMAC_SHA_1_96; + tcp_authopt_key_fill_id(&key, args); + + rc = setsockopt(sd, IPPROTO_TCP, TCP_AUTHOPT_KEY, &key, sizeof(key)); + if (rc < 0) + log_err_errno("setsockopt(TCP_AUTHOPT_KEY) add fail"); + + return rc; +} + static int tcp_md5sig(int sd, void *addr, socklen_t alen, struct sock_args *args) { int keylen = strlen(args->password); struct tcp_md5sig md5sig = {}; int opt = TCP_MD5SIG; @@ -1541,10 +1609,15 @@ static int do_server(struct sock_args *args, int ipc_fd) if (args->password && tcp_md5_remote(lsd, args)) { close(lsd); goto err_exit; } + if (args->authopt_password && tcp_set_authopt(lsd, args)) { + close(lsd); + goto err_exit; + } + ipc_write(ipc_fd, 1); while (1) { log_msg("waiting for client connection.\n"); FD_ZERO(&rfds); FD_SET(lsd, &rfds); @@ -1663,10 +1736,13 @@ static int connectsock(void *addr, socklen_t alen, struct sock_args *args) goto out; if (args->password && tcp_md5sig(sd, addr, alen, args)) goto err; + if (args->authopt_password && tcp_set_authopt(sd, args)) + goto err; + if (args->bind_test_only) goto out; if (connect(sd, addr, alen) < 0) { if (errno != EINPROGRESS) { @@ -1852,11 +1928,11 @@ static int ipc_parent(int cpid, int fd, struct sock_args *args) wait(&status); return client_status; } -#define GETOPT_STR "sr:l:c:p:t:g:P:DRn:M:X:m:d:I:BN:O:SCi6xL:0:1:2:3:Fbqf" +#define GETOPT_STR "sr:l:c:p:t:g:P:DRn:M:X:m:A:d:I:BN:O:SCi6xL:0:1:2:3:Fbqf" #define OPT_FORCE_BIND_KEY_IFINDEX 1001 #define OPT_NO_BIND_KEY_IFINDEX 1002 static struct option long_opts[] = { {"force-bind-key-ifindex", 0, 0, OPT_FORCE_BIND_KEY_IFINDEX}, @@ -1897,14 +1973,15 @@ static void print_usage(char *prog) " -L len send random message of given length\n" " -n num number of times to send message\n" "\n" " -M password use MD5 sum protection\n" " -X password MD5 password for client mode\n" - " -m prefix/len prefix and length to use for MD5 key\n" - " --no-bind-key-ifindex: Force TCP_MD5SIG_FLAG_IFINDEX off\n" - " --force-bind-key-ifindex: Force TCP_MD5SIG_FLAG_IFINDEX on\n" + " -m prefix/len prefix and length to use for MD5/AO key\n" + " --no-bind-key-ifindex: Force disable binding key to ifindex\n" + " --force-bind-key-ifindex: Force enable binding key to ifindex\n" " (default: only if -I is passed)\n" + " -A password use RFC5925 TCP Authentication Option with password\n" "\n" " -g grp multicast group (e.g., 239.1.1.1)\n" " -i interactive mode (default is echo and terminate)\n" "\n" " -0 addr Expected local address\n" @@ -1915,17 +1992,64 @@ static void print_usage(char *prog) " -b Bind test only.\n" " -q Be quiet. Run test without printing anything.\n" , prog, DEFAULT_PORT); } -int main(int argc, char *argv[]) +/* Needs explicit cleanup because keys are global per-namespace */ +void cleanup_tcp_authopt(struct sock_args *args) +{ + int fd; + + if (!args->authopt_password) + return; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + log_err_errno("Failed to create socket"); + return; + } + tcp_del_authopt(fd, args); + close(fd); +} + +static bool cleanup_done; +static struct sock_args args = { + .version = AF_INET, + .type = SOCK_STREAM, + .port = DEFAULT_PORT, +}; + +void cleanup(void) +{ + if (cleanup_done) + return; + cleanup_done = true; + cleanup_tcp_authopt(&args); +} + +void signal_handler(int num) +{ + cleanup(); +} + +void atexit_handler(void) +{ + cleanup(); +} + +/* Explicit cleanup is required for TCP-AO because keys are global. */ +static void register_cleanup(void) { - struct sock_args args = { - .version = AF_INET, - .type = SOCK_STREAM, - .port = DEFAULT_PORT, + struct sigaction sa = { + .sa_handler = signal_handler, }; + sigaction(SIGINT, &sa, NULL); + atexit(atexit_handler); +} + +int main(int argc, char *argv[]) +{ struct protoent *pe; int both_mode = 0; unsigned int tmp; int forever = 0; int fd[2]; @@ -2022,10 +2146,13 @@ int main(int argc, char *argv[]) args.client_pw = optarg; break; case 'm': args.key_addr_prefix_str = optarg; break; + case 'A': + args.authopt_password = optarg; + break; case 'S': args.use_setsockopt = 1; break; case 'f': args.use_freebind = 1; @@ -2085,12 +2212,17 @@ int main(int argc, char *argv[]) args.type != SOCK_STREAM)) { log_error("MD5 passwords apply to TCP only and require a remote ip for the password\n"); return 1; } - if (args.key_addr_prefix_str && !args.password) { - log_error("Prefix range for MD5 protection specified without a password\n"); + if (args.key_addr_prefix_str && !args.password && !args.authopt_password) { + log_error("Prefix range for authentication requires -M or -A\n"); + return 1; + } + + if (args.key_addr_prefix_len && args.authopt_password) { + log_error("TCP-AO does not support prefix match, only full address\n"); return 1; } if (iter == 0) { fprintf(stderr, "Invalid number of messages to send\n"); @@ -2113,10 +2245,12 @@ int main(int argc, char *argv[]) fprintf(stderr, "Local (server mode) or remote IP (client IP) required\n"); return 1; } + register_cleanup(); + if (interactive) { prog_timeout = 0; msg = NULL; } From patchwork Thu Dec 23 15:40:14 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 12698431 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9792CC433EF for ; Thu, 23 Dec 2021 15:41:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349201AbhLWPlt (ORCPT ); Thu, 23 Dec 2021 10:41:49 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33974 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349204AbhLWPlP (ORCPT ); Thu, 23 Dec 2021 10:41:15 -0500 Received: from mail-ed1-x530.google.com (mail-ed1-x530.google.com [IPv6:2a00:1450:4864:20::530]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 035FBC0617A2; Thu, 23 Dec 2021 07:41:14 -0800 (PST) Received: by mail-ed1-x530.google.com with SMTP id j21so22923971edt.9; Thu, 23 Dec 2021 07:41:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=mLCtEWl6YZYNKGw7jvkpXmqL/AYDPWPJ84U9BUE0+Jk=; b=OUE7j5BfOgM7IGv04XZXHi4aUvjQn0J+fAS2L0mahVRtiWNHuSXtZRBeddaBT4AUye mQApKIuIC+WNAeeTAwurl6cZ/DmXHdYQAtgnOrbQRzNLR3WYF3nHtPI2UN+02P4Kcxkl QYqH/LxfFsodQy9gM15l3WjkxLd7gLbRq4w8btzRs4/q1hffHkdHY6AsZKmitxYEsQQu Km3Ohw+yyIqNLjAR8BE/TzrIrFQlY8Gi00oioHO84kt8FKRiqJuCtzJ7LuFKWuY0ktg2 fvtPvjnsYwAvjBav67ZEhJ47B7Wc/zptx0ZrLXlh4nuqmUKVcg1L0zusg+Kq1cb6V6tt V7mg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=mLCtEWl6YZYNKGw7jvkpXmqL/AYDPWPJ84U9BUE0+Jk=; b=JZ01tUf2XV01I+d1REjg0frEISNpAJOTNxaqMeu9gsSjcJFK9jlgYplSsiRyeUskiC A7pQBCP4EPDyfBsSs2vYlS26BqstuWQBoduEHWrkuF3JuTxrL8SbBQhS5cnCwQDy+bFw 7mWRjleg/PdxP5Wktvyd9jcR44S2Q00isXCeg/jk/OPtLG/nGuIMr7iXE0/819YBiYvm mrvuI30cEsuFb326cSByULIftvnpSM6XVMf0k0eJJB675LljjNcxMWQQGteArwHYvb1n VJ6OqZJ3hEfSdDpcfgRJcxgHfVEPbYYZkI7zCjL+qYUej+nhluLiFRJDnfomyCzp328n b/cQ== X-Gm-Message-State: AOAM533rmvT5UCbw2wbKpWh3Nt4VCgZ/5ZIzZchUmta9GC7JSRb8DMpr 4rvyJQ0w3CuGttHG8T+fFmE= X-Google-Smtp-Source: ABdhPJyR3bSroLQ/z/pnPUqi2YRiELeTEh2Pqt7qqEfwS1hY8zoOvuym3PCvGswI17JVwtvB6JhHDA== X-Received: by 2002:a17:906:f43:: with SMTP id h3mr2285167ejj.414.1640274072525; Thu, 23 Dec 2021 07:41:12 -0800 (PST) Received: from ponky.lan ([2a04:241e:501:3870:7c02:dfc6:b554:ab10]) by smtp.gmail.com with ESMTPSA id bx6sm2088617edb.78.2021.12.23.07.41.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Dec 2021 07:41:11 -0800 (PST) From: Leonard Crestez To: David Ahern , Eric Dumazet , Philip Paeps , Dmitry Safonov <0x7f454c46@gmail.com> Cc: Shuah Khan , "David S. Miller" , Herbert Xu , Kuniyuki Iwashima , Hideaki YOSHIFUJI , Jakub Kicinski , Yuchung Cheng , Francesco Ruggeri , Mat Martineau , Christoph Paasch , Ivan Delalande , Priyaranjan Jha , netdev@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 19/19] selftests: net/fcnal: Initial tcp_authopt support Date: Thu, 23 Dec 2021 17:40:14 +0200 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Tests are mostly copied from tcp_md5 with minor changes. It covers VRF support but only based on binding multiple servers: not multiple keys bound to different interfaces. Also add a specific -t tcp_authopt to run only these tests specifically. Signed-off-by: Leonard Crestez --- tools/testing/selftests/net/fcnal-test.sh | 329 +++++++++++++++++++++- 1 file changed, 327 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh index 412d85205546..610ae75df8b2 100755 --- a/tools/testing/selftests/net/fcnal-test.sh +++ b/tools/testing/selftests/net/fcnal-test.sh @@ -817,10 +817,330 @@ ipv4_ping() } ################################################################################ # IPv4 TCP +# +# TCP Authentication Option Tests +# + +# try to enable tcp_authopt sysctl +enable_tcp_authopt() +{ + if [[ -e /proc/sys/net/ipv4/tcp_authopt ]]; then + sysctl -w net.ipv4.tcp_authopt=1 + fi +} + +# check if tcp_authopt is compiled with a client-side bind test +has_tcp_authopt() +{ + run_cmd_nsb nettest -b -A ${MD5_PW} -r ${NSA_IP} +} + +# Verify /proc/net/tcp_authopt is empty in all namespaces +check_tcp_authopt_key_leak() +{ + local ns cnt + + for ns in $NSA $NSB $NSC; do + if ! ip netns list | grep -q $ns; then + continue + fi + cnt=$(ip netns exec "$ns" cat /proc/net/tcp_authopt | wc -l) + if [[ $cnt != 1 ]]; then + echo "FAIL: leaked tcp_authopt keys in netns $ns" + ip netns exec $ns cat /proc/net/tcp_authopt + return 1 + fi + done +} + +log_check_tcp_authopt_key_leak() +{ + check_tcp_authopt_key_leak + log_test $? 0 "TCP-AO: Key leak check" +} + +ipv4_tcp_authopt_novrf() +{ + enable_tcp_authopt + if ! has_tcp_authopt; then + echo "TCP-AO appears to be missing, skip" + return 0 + fi + + log_start + run_cmd nettest -s -A ${MD5_PW} -m ${NSB_IP} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 0 "AO: Single address config" + + log_start + run_cmd nettest -s & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 2 "AO: Server no config, client uses password" + + log_start + run_cmd nettest -s -A ${MD5_PW} -m ${NSB_IP} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_WRONG_PW} + log_test $? 2 "AO: Client uses wrong password" + log_check_tcp_authopt_key_leak + + log_start + run_cmd nettest -s -A ${MD5_PW} -m ${NSB_LO_IP} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 2 "AO: Client address does not match address configured on server" + log_check_tcp_authopt_key_leak + + # client in prefix + log_start + run_cmd nettest -s -A ${MD5_PW} -m ${NS_NET} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 0 "AO: Prefix config" + + # client in prefix, wrong password + log_start + show_hint "Should timeout since client uses wrong password" + run_cmd nettest -s -A ${MD5_PW} -m ${NS_NET} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_WRONG_PW} + log_test $? 2 "AO: Prefix config, client uses wrong password" + log_check_tcp_authopt_key_leak + + # client outside of prefix + log_start + show_hint "Should timeout due to MD5 mismatch" + run_cmd nettest -s -A ${MD5_PW} -m ${NS_NET} & + sleep 1 + run_cmd_nsb nettest -c ${NSB_LO_IP} -r ${NSA_IP} -A ${MD5_PW} + log_test $? 2 "AO: Prefix config, client address not in configured prefix" + log_check_tcp_authopt_key_leak +} + +ipv6_tcp_authopt_novrf() +{ + enable_tcp_authopt + if ! has_tcp_authopt; then + echo "TCP-AO appears to be missing, skip" + return 0 + fi + + log_start + run_cmd nettest -6 -s -A ${MD5_PW} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -A ${MD5_PW} + log_test $? 0 "AO: Simple correct config" + + log_start + run_cmd nettest -6 -s + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -A ${MD5_PW} + log_test $? 2 "AO: Server no config, client uses password" + + log_start + run_cmd nettest -6 -s -A ${MD5_PW} -m ${NSB_IP6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -A ${MD5_WRONG_PW} + log_test $? 2 "AO: Client uses wrong password" + + log_start + run_cmd nettest -6 -s -A ${MD5_PW} -m ${NSB_LO_IP6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -A ${MD5_PW} + log_test $? 2 "AO: Client address does not match address configured on server" +} + +ipv4_tcp_authopt_vrf() +{ + enable_tcp_authopt + if ! has_tcp_authopt; then + echo "TCP-AO appears to be missing, skip" + return 0 + fi + + log_start + run_cmd nettest -s -I ${VRF} -A ${MD5_PW} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 0 "AO: VRF: Simple config" + + # + # duplicate config between default VRF and a VRF + # + + log_start + run_cmd nettest -s -I ${VRF} -A ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -A ${MD5_WRONG_PW} -m ${NSB_IP} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 0 "AO: VRF: Servers in default VRF and VRF, client in VRF" + + log_start + run_cmd nettest -s -I ${VRF} -A ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -A ${MD5_WRONG_PW} -m ${NSB_IP} & + sleep 1 + run_cmd_nsc nettest -r ${NSA_IP} -A ${MD5_WRONG_PW} + log_test $? 0 "AO: VRF: Servers in default VRF and VRF, client in default VRF" + + log_start + show_hint "Should timeout since client in default VRF uses VRF password" + run_cmd nettest -s -I ${VRF} -A ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -A ${MD5_WRONG_PW} -m ${NSB_IP} & + sleep 1 + run_cmd_nsc nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 2 "AO: VRF: Servers in default VRF and VRF, conn in default VRF with VRF pw" + + log_start + show_hint "Should timeout since client in VRF uses default VRF password" + run_cmd nettest -s -I ${VRF} -A ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -A ${MD5_WRONG_PW} -m ${NSB_IP} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_WRONG_PW} + log_test $? 2 "AO: VRF: Servers in default VRF and VRF, conn in VRF with default VRF pw" + + test_ipv4_tcp_authopt_vrf__global_server__bind_ifindex0 +} + +test_ipv4_tcp_authopt_vrf__global_server__bind_ifindex0() +{ + # This particular test needs tcp_l3mdev_accept=1 for Global server to accept VRF connections + local old_tcp_l3mdev_accept + old_tcp_l3mdev_accept=$(get_sysctl net.ipv4.tcp_l3mdev_accept) + set_sysctl net.ipv4.tcp_l3mdev_accept=1 + + log_start + run_cmd nettest -s -A ${MD5_PW} --force-bind-key-ifindex & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 2 "AO: VRF: Global server, Key bound to ifindex=0 rejects VRF connection" + + log_start + run_cmd nettest -s -A ${MD5_PW} --force-bind-key-ifindex & + sleep 1 + run_cmd_nsc nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 0 "AO: VRF: Global server, key bound to ifindex=0 accepts non-VRF connection" + log_start + + run_cmd nettest -s -A ${MD5_PW} --no-bind-key-ifindex & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 0 "AO: VRF: Global server, key not bound to ifindex accepts VRF connection" + + log_start + run_cmd nettest -s -A ${MD5_PW} --no-bind-key-ifindex & + sleep 1 + run_cmd_nsc nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 0 "AO: VRF: Global server, key not bound to ifindex accepts non-VRF connection" + + # restore value + set_sysctl net.ipv4.tcp_l3mdev_accept="$old_tcp_l3mdev_accept" +} + +ipv6_tcp_authopt_vrf() +{ + enable_tcp_authopt + if ! has_tcp_authopt; then + echo "TCP-AO appears to be missing, skip" + return 0 + fi + + log_start + run_cmd nettest -6 -s -I ${VRF} -A ${MD5_PW} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -A ${MD5_PW} + log_test $? 0 "AO: VRF: Simple config" + + # + # duplicate config between default VRF and a VRF + # + + log_start + run_cmd nettest -6 -s -I ${VRF} -A ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -A ${MD5_WRONG_PW} -m ${NSB_IP6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -A ${MD5_PW} + log_test $? 0 "AO: VRF: Servers in default VRF and VRF, client in VRF" + + log_start + run_cmd nettest -6 -s -I ${VRF} -A ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -A ${MD5_WRONG_PW} -m ${NSB_IP6} & + sleep 1 + run_cmd_nsc nettest -6 -r ${NSA_IP6} -A ${MD5_WRONG_PW} + log_test $? 0 "AO: VRF: Servers in default VRF and VRF, client in default VRF" + + log_start + show_hint "Should timeout since client in default VRF uses VRF password" + run_cmd nettest -6 -s -I ${VRF} -A ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -A ${MD5_WRONG_PW} -m ${NSB_IP6} & + sleep 1 + run_cmd_nsc nettest -6 -r ${NSA_IP6} -A ${MD5_PW} + log_test $? 2 "AO: VRF: Servers in default VRF and VRF, conn in default VRF with VRF pw" + + log_start + show_hint "Should timeout since client in VRF uses default VRF password" + run_cmd nettest -6 -s -I ${VRF} -A ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -A ${MD5_WRONG_PW} -m ${NSB_IP6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -A ${MD5_WRONG_PW} + log_test $? 2 "AO: VRF: Servers in default VRF and VRF, conn in VRF with default VRF pw" + + log_start + run_cmd nettest -6 -s -I ${VRF} -A ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -A ${MD5_WRONG_PW} -m ${NS_NET6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -A ${MD5_PW} + log_test $? 0 "AO: VRF: Prefix config in default VRF and VRF, conn in VRF" + + log_start + run_cmd nettest -6 -s -I ${VRF} -A ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -A ${MD5_WRONG_PW} -m ${NS_NET6} & + sleep 1 + run_cmd_nsc nettest -6 -r ${NSA_IP6} -A ${MD5_WRONG_PW} + log_test $? 0 "AO: VRF: Prefix config in default VRF and VRF, conn in default VRF" + + log_start + show_hint "Should timeout since client in default VRF uses VRF password" + run_cmd nettest -6 -s -I ${VRF} -A ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -A ${MD5_WRONG_PW} -m ${NS_NET6} & + sleep 1 + run_cmd_nsc nettest -6 -r ${NSA_IP6} -A ${MD5_PW} + log_test $? 2 "AO: VRF: Prefix config in def VRF and VRF, conn in def VRF with VRF pw" + + log_start + show_hint "Should timeout since client in VRF uses default VRF password" + run_cmd nettest -6 -s -I ${VRF} -A ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -A ${MD5_WRONG_PW} -m ${NS_NET6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -A ${MD5_WRONG_PW} + log_test $? 2 "AO: VRF: Prefix config in dev VRF and VRF, conn in VRF with def VRF pw" +} + +only_tcp_authopt() +{ + log_section "TCP Authentication Option" + + setup + set_sysctl net.ipv4.tcp_l3mdev_accept=0 + log_subsection "TCP-AO IPv4 no VRF" + ipv4_tcp_authopt_novrf + log_subsection "TCP-AO IPv6 no VRF" + ipv6_tcp_authopt_novrf + + setup "yes" + setup_vrf_dup + set_sysctl net.ipv4.tcp_l3mdev_accept=0 + log_subsection "TCP-AO IPv4 VRF" + ipv4_tcp_authopt_vrf + log_subsection "TCP-AO IPv6 VRF" + ipv6_tcp_authopt_vrf +} + # # MD5 tests without VRF # ipv4_tcp_md5_novrf() { @@ -1202,10 +1522,11 @@ ipv4_tcp_novrf() show_hint "Should fail 'Connection refused'" run_cmd nettest -d ${NSA_DEV} -r ${a} log_test_addr ${a} $? 1 "No server, device client, local conn" ipv4_tcp_md5_novrf + ipv4_tcp_authopt_novrf } ipv4_tcp_vrf() { local a @@ -1254,13 +1575,14 @@ ipv4_tcp_vrf() run_cmd nettest -s & sleep 1 run_cmd nettest -r ${a} -d ${NSA_DEV} log_test_addr ${a} $? 1 "Global server, local connection" - # run MD5 tests + # run MD5+AO tests setup_vrf_dup ipv4_tcp_md5 + ipv6_tcp_md5_vrf cleanup_vrf_dup # # enable VRF global server # @@ -2685,10 +3007,11 @@ ipv6_tcp_novrf() run_cmd nettest -6 -d ${NSA_DEV} -r ${a} log_test_addr ${a} $? 1 "No server, device client, local conn" done ipv6_tcp_md5_novrf + ipv6_tcp_authopt_novrf } ipv6_tcp_vrf() { local a @@ -2753,13 +3076,14 @@ ipv6_tcp_vrf() run_cmd nettest -6 -s & sleep 1 run_cmd nettest -6 -r ${a} -d ${NSA_DEV} log_test_addr ${a} $? 1 "Global server, local connection" - # run MD5 tests + # run MD5+AO tests setup_vrf_dup ipv6_tcp_md5 + ipv6_tcp_authopt_vrf cleanup_vrf_dup # # enable VRF global server # @@ -4125,10 +4449,11 @@ do ipv6_bind|bind6) ipv6_addr_bind;; ipv6_runtime) ipv6_runtime;; ipv6_netfilter) ipv6_netfilter;; use_cases) use_cases;; + tcp_authopt) only_tcp_authopt;; # setup namespaces and config, but do not run any tests setup) setup; exit 0;; vrf_setup) setup "yes"; exit 0;; esac