Skip to content

Commit 0aadc73

Browse files
0x7f454c46davem330
authored andcommitted
net/tcp: Prevent TCP-MD5 with TCP-AO being set
Be as conservative as possible: if there is TCP-MD5 key for a given peer regardless of L3 interface - don't allow setting TCP-AO key for the same peer. According to RFC5925, TCP-AO is supposed to replace TCP-MD5 and there can't be any switch between both on any connected tuple. Later it can be relaxed, if there's a use, but in the beginning restrict any intersection. Note: it's still should be possible to set both TCP-MD5 and TCP-AO keys on a listening socket for *different* peers. Co-developed-by: Francesco Ruggeri <[email protected]> Signed-off-by: Francesco Ruggeri <[email protected]> Co-developed-by: Salam Noureddine <[email protected]> Signed-off-by: Salam Noureddine <[email protected]> Signed-off-by: Dmitry Safonov <[email protected]> Acked-by: David Ahern <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 4954f17 commit 0aadc73

File tree

7 files changed

+198
-9
lines changed

7 files changed

+198
-9
lines changed

include/net/tcp.h

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1778,6 +1778,7 @@ int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr,
17781778

17791779
int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr,
17801780
int family, u8 prefixlen, int l3index, u8 flags);
1781+
void tcp_clear_md5_list(struct sock *sk);
17811782
struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
17821783
const struct sock *addr_sk);
17831784

@@ -1786,14 +1787,23 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
17861787
extern struct static_key_false_deferred tcp_md5_needed;
17871788
struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,
17881789
const union tcp_md5_addr *addr,
1789-
int family);
1790+
int family, bool any_l3index);
17901791
static inline struct tcp_md5sig_key *
17911792
tcp_md5_do_lookup(const struct sock *sk, int l3index,
17921793
const union tcp_md5_addr *addr, int family)
17931794
{
17941795
if (!static_branch_unlikely(&tcp_md5_needed.key))
17951796
return NULL;
1796-
return __tcp_md5_do_lookup(sk, l3index, addr, family);
1797+
return __tcp_md5_do_lookup(sk, l3index, addr, family, false);
1798+
}
1799+
1800+
static inline struct tcp_md5sig_key *
1801+
tcp_md5_do_lookup_any_l3index(const struct sock *sk,
1802+
const union tcp_md5_addr *addr, int family)
1803+
{
1804+
if (!static_branch_unlikely(&tcp_md5_needed.key))
1805+
return NULL;
1806+
return __tcp_md5_do_lookup(sk, 0, addr, family, true);
17971807
}
17981808

17991809
enum skb_drop_reason
@@ -1811,6 +1821,13 @@ tcp_md5_do_lookup(const struct sock *sk, int l3index,
18111821
return NULL;
18121822
}
18131823

1824+
static inline struct tcp_md5sig_key *
1825+
tcp_md5_do_lookup_any_l3index(const struct sock *sk,
1826+
const union tcp_md5_addr *addr, int family)
1827+
{
1828+
return NULL;
1829+
}
1830+
18141831
static inline enum skb_drop_reason
18151832
tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
18161833
const void *saddr, const void *daddr,
@@ -2177,6 +2194,9 @@ struct tcp_sock_af_ops {
21772194
#endif
21782195
#ifdef CONFIG_TCP_AO
21792196
int (*ao_parse)(struct sock *sk, int optname, sockptr_t optval, int optlen);
2197+
struct tcp_ao_key *(*ao_lookup)(const struct sock *sk,
2198+
struct sock *addr_sk,
2199+
int sndid, int rcvid);
21802200
#endif
21812201
};
21822202

@@ -2588,4 +2608,23 @@ static inline u64 tcp_transmit_time(const struct sock *sk)
25882608
return 0;
25892609
}
25902610

2611+
static inline bool tcp_ao_required(struct sock *sk, const void *saddr,
2612+
int family)
2613+
{
2614+
#ifdef CONFIG_TCP_AO
2615+
struct tcp_ao_info *ao_info;
2616+
struct tcp_ao_key *ao_key;
2617+
2618+
ao_info = rcu_dereference_check(tcp_sk(sk)->ao_info,
2619+
lockdep_sock_is_held(sk));
2620+
if (!ao_info)
2621+
return false;
2622+
2623+
ao_key = tcp_ao_do_lookup(sk, saddr, family, -1, -1);
2624+
if (ao_info->ao_required || ao_key)
2625+
return true;
2626+
#endif
2627+
return false;
2628+
}
2629+
25912630
#endif /* _TCP_H */

include/net/tcp_ao.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,24 @@ struct tcp_ao_info {
9292
int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
9393
sockptr_t optval, int optlen);
9494
void tcp_ao_destroy_sock(struct sock *sk);
95+
struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
96+
const union tcp_ao_addr *addr,
97+
int family, int sndid, int rcvid);
9598
/* ipv4 specific functions */
9699
int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen);
100+
struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
101+
int sndid, int rcvid);
97102
/* ipv6 specific functions */
98103
int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen);
104+
struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
105+
struct sock *addr_sk, int sndid, int rcvid);
99106
#else
107+
static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
108+
const union tcp_ao_addr *addr, int family, int sndid, int rcvid)
109+
{
110+
return NULL;
111+
}
112+
100113
static inline void tcp_ao_destroy_sock(struct sock *sk)
101114
{
102115
}

net/ipv4/tcp_ao.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk,
116116
return NULL;
117117
}
118118

119+
struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
120+
const union tcp_ao_addr *addr,
121+
int family, int sndid, int rcvid)
122+
{
123+
return __tcp_ao_do_lookup(sk, addr, family, U8_MAX, sndid, rcvid);
124+
}
125+
119126
static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags)
120127
{
121128
struct tcp_ao_info *ao;
@@ -162,6 +169,14 @@ void tcp_ao_destroy_sock(struct sock *sk)
162169
kfree_rcu(ao, rcu);
163170
}
164171

172+
struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
173+
int sndid, int rcvid)
174+
{
175+
union tcp_ao_addr *addr = (union tcp_ao_addr *)&addr_sk->sk_daddr;
176+
177+
return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid);
178+
}
179+
165180
static bool tcp_ao_can_set_current_rnext(struct sock *sk)
166181
{
167182
/* There aren't current/rnext keys on TCP_LISTEN sockets */
@@ -497,6 +512,10 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
497512
return -EINVAL;
498513
}
499514

515+
/* Don't allow keys for peers that have a matching TCP-MD5 key */
516+
if (tcp_md5_do_lookup_any_l3index(sk, addr, family))
517+
return -EKEYREJECTED;
518+
500519
ao_info = setsockopt_ao_info(sk);
501520
if (IS_ERR(ao_info))
502521
return PTR_ERR(ao_info);
@@ -698,6 +717,31 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family,
698717
return -ENOENT;
699718
}
700719

720+
/* cmd.ao_required makes a socket TCP-AO only.
721+
* Don't allow any md5 keys for any l3intf on the socket together with it.
722+
* Restricting it early in setsockopt() removes a check for
723+
* ao_info->ao_required on inbound tcp segment fast-path.
724+
*/
725+
static int tcp_ao_required_verify(struct sock *sk)
726+
{
727+
#ifdef CONFIG_TCP_MD5SIG
728+
const struct tcp_md5sig_info *md5sig;
729+
730+
if (!static_branch_unlikely(&tcp_md5_needed.key))
731+
return 0;
732+
733+
md5sig = rcu_dereference_check(tcp_sk(sk)->md5sig_info,
734+
lockdep_sock_is_held(sk));
735+
if (!md5sig)
736+
return 0;
737+
738+
if (rcu_dereference_check(hlist_first_rcu(&md5sig->head),
739+
lockdep_sock_is_held(sk)))
740+
return 1;
741+
#endif
742+
return 0;
743+
}
744+
701745
static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family,
702746
sockptr_t optval, int optlen)
703747
{
@@ -732,6 +776,9 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family,
732776
first = true;
733777
}
734778

779+
if (cmd.ao_required && tcp_ao_required_verify(sk))
780+
return -EKEYREJECTED;
781+
735782
/* For sockets in TCP_CLOSED it's possible set keys that aren't
736783
* matching the future peer (address/port/VRF/etc),
737784
* tcp_ao_connect_init() will choose a correct matching MKT

net/ipv4/tcp_ipv4.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,7 @@ static bool better_md5_match(struct tcp_md5sig_key *old, struct tcp_md5sig_key *
10821082
/* Find the Key structure for an address. */
10831083
struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,
10841084
const union tcp_md5_addr *addr,
1085-
int family)
1085+
int family, bool any_l3index)
10861086
{
10871087
const struct tcp_sock *tp = tcp_sk(sk);
10881088
struct tcp_md5sig_key *key;
@@ -1101,7 +1101,8 @@ struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,
11011101
lockdep_sock_is_held(sk)) {
11021102
if (key->family != family)
11031103
continue;
1104-
if (key->flags & TCP_MD5SIG_FLAG_IFINDEX && key->l3index != l3index)
1104+
if (!any_l3index && key->flags & TCP_MD5SIG_FLAG_IFINDEX &&
1105+
key->l3index != l3index)
11051106
continue;
11061107
if (family == AF_INET) {
11071108
mask = inet_make_mask(key->prefixlen);
@@ -1313,7 +1314,7 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family,
13131314
}
13141315
EXPORT_SYMBOL(tcp_md5_do_del);
13151316

1316-
static void tcp_clear_md5_list(struct sock *sk)
1317+
void tcp_clear_md5_list(struct sock *sk)
13171318
{
13181319
struct tcp_sock *tp = tcp_sk(sk);
13191320
struct tcp_md5sig_key *key;
@@ -1383,6 +1384,12 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,
13831384
if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
13841385
return -EINVAL;
13851386

1387+
/* Don't allow keys for peers that have a matching TCP-AO key.
1388+
* See the comment in tcp_ao_add_cmd()
1389+
*/
1390+
if (tcp_ao_required(sk, addr, AF_INET))
1391+
return -EKEYREJECTED;
1392+
13861393
return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags,
13871394
cmd.tcpm_key, cmd.tcpm_keylen);
13881395
}
@@ -2279,6 +2286,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
22792286
.md5_parse = tcp_v4_parse_md5_keys,
22802287
#endif
22812288
#ifdef CONFIG_TCP_AO
2289+
.ao_lookup = tcp_v4_ao_lookup,
22822290
.ao_parse = tcp_v4_parse_ao,
22832291
#endif
22842292
};

net/ipv4/tcp_output.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3931,6 +3931,53 @@ int tcp_connect(struct sock *sk)
39313931

39323932
tcp_call_bpf(sk, BPF_SOCK_OPS_TCP_CONNECT_CB, 0, NULL);
39333933

3934+
#if defined(CONFIG_TCP_MD5SIG) && defined(CONFIG_TCP_AO)
3935+
/* Has to be checked late, after setting daddr/saddr/ops.
3936+
* Return error if the peer has both a md5 and a tcp-ao key
3937+
* configured as this is ambiguous.
3938+
*/
3939+
if (unlikely(rcu_dereference_protected(tp->md5sig_info,
3940+
lockdep_sock_is_held(sk)))) {
3941+
bool needs_ao = !!tp->af_specific->ao_lookup(sk, sk, -1, -1);
3942+
bool needs_md5 = !!tp->af_specific->md5_lookup(sk, sk);
3943+
struct tcp_ao_info *ao_info;
3944+
3945+
ao_info = rcu_dereference_check(tp->ao_info,
3946+
lockdep_sock_is_held(sk));
3947+
if (ao_info) {
3948+
/* This is an extra check: tcp_ao_required() in
3949+
* tcp_v{4,6}_parse_md5_keys() should prevent adding
3950+
* md5 keys on ao_required socket.
3951+
*/
3952+
needs_ao |= ao_info->ao_required;
3953+
WARN_ON_ONCE(ao_info->ao_required && needs_md5);
3954+
}
3955+
if (needs_md5 && needs_ao)
3956+
return -EKEYREJECTED;
3957+
3958+
/* If we have a matching md5 key and no matching tcp-ao key
3959+
* then free up ao_info if allocated.
3960+
*/
3961+
if (needs_md5) {
3962+
tcp_ao_destroy_sock(sk);
3963+
} else if (needs_ao) {
3964+
tcp_clear_md5_list(sk);
3965+
kfree(rcu_replace_pointer(tp->md5sig_info, NULL,
3966+
lockdep_sock_is_held(sk)));
3967+
}
3968+
}
3969+
#endif
3970+
#ifdef CONFIG_TCP_AO
3971+
if (unlikely(rcu_dereference_protected(tp->ao_info,
3972+
lockdep_sock_is_held(sk)))) {
3973+
/* Don't allow connecting if ao is configured but no
3974+
* matching key is found.
3975+
*/
3976+
if (!tp->af_specific->ao_lookup(sk, sk, -1, -1))
3977+
return -EKEYREJECTED;
3978+
}
3979+
#endif
3980+
39343981
if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk))
39353982
return -EHOSTUNREACH; /* Routing failure or similar. */
39363983

net/ipv6/tcp_ao.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,23 @@
1212
#include <net/tcp.h>
1313
#include <net/ipv6.h>
1414

15+
static struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk,
16+
const struct in6_addr *addr,
17+
int sndid, int rcvid)
18+
{
19+
return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)addr, AF_INET6,
20+
sndid, rcvid);
21+
}
22+
23+
struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
24+
struct sock *addr_sk,
25+
int sndid, int rcvid)
26+
{
27+
struct in6_addr *addr = &addr_sk->sk_v6_daddr;
28+
29+
return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid);
30+
}
31+
1532
int tcp_v6_parse_ao(struct sock *sk, int cmd,
1633
sockptr_t optval, int optlen)
1734
{

net/ipv6/tcp_ipv6.c

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,
600600
{
601601
struct tcp_md5sig cmd;
602602
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr;
603+
union tcp_ao_addr *addr;
603604
int l3index = 0;
604605
u8 prefixlen;
605606
u8 flags;
@@ -654,13 +655,28 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,
654655
if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
655656
return -EINVAL;
656657

657-
if (ipv6_addr_v4mapped(&sin6->sin6_addr))
658-
return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
658+
if (ipv6_addr_v4mapped(&sin6->sin6_addr)) {
659+
addr = (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3];
660+
661+
/* Don't allow keys for peers that have a matching TCP-AO key.
662+
* See the comment in tcp_ao_add_cmd()
663+
*/
664+
if (tcp_ao_required(sk, addr, AF_INET))
665+
return -EKEYREJECTED;
666+
return tcp_md5_do_add(sk, addr,
659667
AF_INET, prefixlen, l3index, flags,
660668
cmd.tcpm_key, cmd.tcpm_keylen);
669+
}
670+
671+
addr = (union tcp_md5_addr *)&sin6->sin6_addr;
672+
673+
/* Don't allow keys for peers that have a matching TCP-AO key.
674+
* See the comment in tcp_ao_add_cmd()
675+
*/
676+
if (tcp_ao_required(sk, addr, AF_INET6))
677+
return -EKEYREJECTED;
661678

662-
return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr,
663-
AF_INET6, prefixlen, l3index, flags,
679+
return tcp_md5_do_add(sk, addr, AF_INET6, prefixlen, l3index, flags,
664680
cmd.tcpm_key, cmd.tcpm_keylen);
665681
}
666682

@@ -1903,6 +1919,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
19031919
.md5_parse = tcp_v6_parse_md5_keys,
19041920
#endif
19051921
#ifdef CONFIG_TCP_AO
1922+
.ao_lookup = tcp_v6_ao_lookup,
19061923
.ao_parse = tcp_v6_parse_ao,
19071924
#endif
19081925
};
@@ -1934,6 +1951,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
19341951
.md5_parse = tcp_v6_parse_md5_keys,
19351952
#endif
19361953
#ifdef CONFIG_TCP_AO
1954+
.ao_lookup = tcp_v6_ao_lookup,
19371955
.ao_parse = tcp_v6_parse_ao,
19381956
#endif
19391957
};

0 commit comments

Comments
 (0)