Skip to content

Commit f7dca36

Browse files
0x7f454c46davem330
authored andcommitted
net/tcp: Add tcp_parse_auth_options()
Introduce a helper that: (1) shares the common code with TCP-MD5 header options parsing (2) looks for hash signature only once for both TCP-MD5 and TCP-AO (3) fails with -EEXIST if any TCP sign option is present twice, see RFC5925 (2.2): ">> A single TCP segment MUST NOT have more than one TCP-AO in its options sequence. When multiple TCP-AOs appear, TCP MUST discard the segment." 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 1e03d32 commit f7dca36

File tree

7 files changed

+93
-22
lines changed

7 files changed

+93
-22
lines changed

include/net/dropreason-core.h

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
FN(IP_NOPROTO) \
2121
FN(SOCKET_RCVBUFF) \
2222
FN(PROTO_MEM) \
23+
FN(TCP_AUTH_HDR) \
2324
FN(TCP_MD5NOTFOUND) \
2425
FN(TCP_MD5UNEXPECTED) \
2526
FN(TCP_MD5FAILURE) \
@@ -142,6 +143,11 @@ enum skb_drop_reason {
142143
* drop out of udp_memory_allocated.
143144
*/
144145
SKB_DROP_REASON_PROTO_MEM,
146+
/**
147+
* @SKB_DROP_REASON_TCP_AUTH_HDR: TCP-MD5 or TCP-AO hashes are met
148+
* twice or set incorrectly.
149+
*/
150+
SKB_DROP_REASON_TCP_AUTH_HDR,
145151
/**
146152
* @SKB_DROP_REASON_TCP_MD5NOTFOUND: no MD5 hash and one expected,
147153
* corresponding to LINUX_MIB_TCPMD5NOTFOUND

include/net/tcp.h

+23-1
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,6 @@ int tcp_mmap(struct file *file, struct socket *sock,
438438
void tcp_parse_options(const struct net *net, const struct sk_buff *skb,
439439
struct tcp_options_received *opt_rx,
440440
int estab, struct tcp_fastopen_cookie *foc);
441-
const u8 *tcp_parse_md5sig_option(const struct tcphdr *th);
442441

443442
/*
444443
* BPF SKB-less helpers
@@ -2675,6 +2674,29 @@ static inline u64 tcp_transmit_time(const struct sock *sk)
26752674
return 0;
26762675
}
26772676

2677+
static inline int tcp_parse_auth_options(const struct tcphdr *th,
2678+
const u8 **md5_hash, const struct tcp_ao_hdr **aoh)
2679+
{
2680+
const u8 *md5_tmp, *ao_tmp;
2681+
int ret;
2682+
2683+
ret = tcp_do_parse_auth_options(th, &md5_tmp, &ao_tmp);
2684+
if (ret)
2685+
return ret;
2686+
2687+
if (md5_hash)
2688+
*md5_hash = md5_tmp;
2689+
2690+
if (aoh) {
2691+
if (!ao_tmp)
2692+
*aoh = NULL;
2693+
else
2694+
*aoh = (struct tcp_ao_hdr *)(ao_tmp - 2);
2695+
}
2696+
2697+
return 0;
2698+
}
2699+
26782700
static inline bool tcp_ao_required(struct sock *sk, const void *saddr,
26792701
int family)
26802702
{

include/net/tcp_ao.h

+16-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen);
152152
void tcp_ao_established(struct sock *sk);
153153
void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb);
154154
void tcp_ao_connect_init(struct sock *sk);
155-
155+
void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
156+
struct tcp_request_sock *treq,
157+
unsigned short int family);
156158
#else /* CONFIG_TCP_AO */
157159

158160
static inline int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
@@ -185,4 +187,17 @@ static inline void tcp_ao_connect_init(struct sock *sk)
185187
}
186188
#endif
187189

190+
#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
191+
int tcp_do_parse_auth_options(const struct tcphdr *th,
192+
const u8 **md5_hash, const u8 **ao_hash);
193+
#else
194+
static inline int tcp_do_parse_auth_options(const struct tcphdr *th,
195+
const u8 **md5_hash, const u8 **ao_hash)
196+
{
197+
*md5_hash = NULL;
198+
*ao_hash = NULL;
199+
return 0;
200+
}
201+
#endif
202+
188203
#endif /* _TCP_AO_H */

net/ipv4/tcp.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -4398,7 +4398,8 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
43984398
l3index = sdif ? dif : 0;
43994399

44004400
hash_expected = tcp_md5_do_lookup(sk, l3index, saddr, family);
4401-
hash_location = tcp_parse_md5sig_option(th);
4401+
if (tcp_parse_auth_options(th, &hash_location, NULL))
4402+
return SKB_DROP_REASON_TCP_AUTH_HDR;
44024403

44034404
/* We've parsed the options - do we have a hash? */
44044405
if (!hash_expected && !hash_location)

net/ipv4/tcp_input.c

+29-10
Original file line numberDiff line numberDiff line change
@@ -4255,39 +4255,58 @@ static bool tcp_fast_parse_options(const struct net *net,
42554255
return true;
42564256
}
42574257

4258-
#ifdef CONFIG_TCP_MD5SIG
4258+
#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
42594259
/*
4260-
* Parse MD5 Signature option
4260+
* Parse Signature options
42614261
*/
4262-
const u8 *tcp_parse_md5sig_option(const struct tcphdr *th)
4262+
int tcp_do_parse_auth_options(const struct tcphdr *th,
4263+
const u8 **md5_hash, const u8 **ao_hash)
42634264
{
42644265
int length = (th->doff << 2) - sizeof(*th);
42654266
const u8 *ptr = (const u8 *)(th + 1);
4267+
unsigned int minlen = TCPOLEN_MD5SIG;
4268+
4269+
if (IS_ENABLED(CONFIG_TCP_AO))
4270+
minlen = sizeof(struct tcp_ao_hdr) + 1;
4271+
4272+
*md5_hash = NULL;
4273+
*ao_hash = NULL;
42664274

42674275
/* If not enough data remaining, we can short cut */
4268-
while (length >= TCPOLEN_MD5SIG) {
4276+
while (length >= minlen) {
42694277
int opcode = *ptr++;
42704278
int opsize;
42714279

42724280
switch (opcode) {
42734281
case TCPOPT_EOL:
4274-
return NULL;
4282+
return 0;
42754283
case TCPOPT_NOP:
42764284
length--;
42774285
continue;
42784286
default:
42794287
opsize = *ptr++;
42804288
if (opsize < 2 || opsize > length)
4281-
return NULL;
4282-
if (opcode == TCPOPT_MD5SIG)
4283-
return opsize == TCPOLEN_MD5SIG ? ptr : NULL;
4289+
return -EINVAL;
4290+
if (opcode == TCPOPT_MD5SIG) {
4291+
if (opsize != TCPOLEN_MD5SIG)
4292+
return -EINVAL;
4293+
if (unlikely(*md5_hash || *ao_hash))
4294+
return -EEXIST;
4295+
*md5_hash = ptr;
4296+
} else if (opcode == TCPOPT_AO) {
4297+
if (opsize <= sizeof(struct tcp_ao_hdr))
4298+
return -EINVAL;
4299+
if (unlikely(*md5_hash || *ao_hash))
4300+
return -EEXIST;
4301+
*ao_hash = ptr;
4302+
}
42844303
}
42854304
ptr += opsize - 2;
42864305
length -= opsize;
42874306
}
4288-
return NULL;
4307+
return 0;
42894308
}
4290-
EXPORT_SYMBOL(tcp_parse_md5sig_option);
4309+
EXPORT_SYMBOL(tcp_do_parse_auth_options);
42914310
#endif
42924311

42934312
/* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM

net/ipv4/tcp_ipv4.c

+10-5
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,9 @@ EXPORT_SYMBOL(tcp_v4_send_check);
670670
* Exception: precedence violation. We do not implement it in any case.
671671
*/
672672

673-
#ifdef CONFIG_TCP_MD5SIG
673+
#ifdef CONFIG_TCP_AO
674+
#define OPTION_BYTES MAX_TCP_OPTION_SPACE
675+
#elif defined(CONFIG_TCP_MD5SIG)
674676
#define OPTION_BYTES TCPOLEN_MD5SIG_ALIGNED
675677
#else
676678
#define OPTION_BYTES sizeof(__be32)
@@ -685,8 +687,8 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
685687
} rep;
686688
struct ip_reply_arg arg;
687689
#ifdef CONFIG_TCP_MD5SIG
690+
const __u8 *md5_hash_location = NULL;
688691
struct tcp_md5sig_key *key = NULL;
689-
const __u8 *hash_location = NULL;
690692
unsigned char newhash[16];
691693
int genhash;
692694
struct sock *sk1 = NULL;
@@ -727,8 +729,11 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
727729

728730
net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
729731
#ifdef CONFIG_TCP_MD5SIG
732+
/* Invalid TCP option size or twice included auth */
733+
if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, NULL))
734+
return;
735+
730736
rcu_read_lock();
731-
hash_location = tcp_parse_md5sig_option(th);
732737
if (sk && sk_fullsock(sk)) {
733738
const union tcp_md5_addr *addr;
734739
int l3index;
@@ -739,7 +744,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
739744
l3index = tcp_v4_sdif(skb) ? inet_iif(skb) : 0;
740745
addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr;
741746
key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET);
742-
} else if (hash_location) {
747+
} else if (md5_hash_location) {
743748
const union tcp_md5_addr *addr;
744749
int sdif = tcp_v4_sdif(skb);
745750
int dif = inet_iif(skb);
@@ -771,7 +776,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
771776

772777

773778
genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
774-
if (genhash || memcmp(hash_location, newhash, 16) != 0)
779+
if (genhash || memcmp(md5_hash_location, newhash, 16) != 0)
775780
goto out;
776781

777782
}

net/ipv6/tcp_ipv6.c

+7-4
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
990990
u32 seq = 0, ack_seq = 0;
991991
struct tcp_md5sig_key *key = NULL;
992992
#ifdef CONFIG_TCP_MD5SIG
993-
const __u8 *hash_location = NULL;
993+
const __u8 *md5_hash_location = NULL;
994994
unsigned char newhash[16];
995995
int genhash;
996996
struct sock *sk1 = NULL;
@@ -1012,8 +1012,11 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
10121012

10131013
net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
10141014
#ifdef CONFIG_TCP_MD5SIG
1015+
/* Invalid TCP option size or twice included auth */
1016+
if (tcp_parse_auth_options(th, &md5_hash_location, NULL))
1017+
return;
1018+
10151019
rcu_read_lock();
1016-
hash_location = tcp_parse_md5sig_option(th);
10171020
if (sk && sk_fullsock(sk)) {
10181021
int l3index;
10191022

@@ -1022,7 +1025,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
10221025
*/
10231026
l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0;
10241027
key = tcp_v6_md5_do_lookup(sk, &ipv6h->saddr, l3index);
1025-
} else if (hash_location) {
1028+
} else if (md5_hash_location) {
10261029
int dif = tcp_v6_iif_l3_slave(skb);
10271030
int sdif = tcp_v6_sdif(skb);
10281031
int l3index;
@@ -1051,7 +1054,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
10511054
goto out;
10521055

10531056
genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb);
1054-
if (genhash || memcmp(hash_location, newhash, 16) != 0)
1057+
if (genhash || memcmp(md5_hash_location, newhash, 16) != 0)
10551058
goto out;
10561059
}
10571060
#endif

0 commit comments

Comments
 (0)