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

Configure Feed

Select the types of activity you want to include in your feed.

net/tcp: Verify inbound TCP-AO signed segments

Now there is a common function to verify signature on TCP segments:
tcp_inbound_hash(). It has checks for all possible cross-interactions
with MD5 signs as well as with unsigned segments.

The rules from RFC5925 are:
(1) Any TCP segment can have at max only one signature.
(2) TCP connections can't switch between using TCP-MD5 and TCP-AO.
(3) TCP-AO connections can't stop using AO, as well as unsigned
connections can't suddenly start using AO.

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

authored by

Dmitry Safonov and committed by
David S. Miller
0a3a8090 9427c6aa

+248 -47
+17
include/net/dropreason-core.h
··· 24 24 FN(TCP_MD5NOTFOUND) \ 25 25 FN(TCP_MD5UNEXPECTED) \ 26 26 FN(TCP_MD5FAILURE) \ 27 + FN(TCP_AONOTFOUND) \ 28 + FN(TCP_AOUNEXPECTED) \ 29 + FN(TCP_AOKEYNOTFOUND) \ 30 + FN(TCP_AOFAILURE) \ 27 31 FN(SOCKET_BACKLOG) \ 28 32 FN(TCP_FLAGS) \ 29 33 FN(TCP_ZEROWINDOW) \ ··· 167 163 * to LINUX_MIB_TCPMD5FAILURE 168 164 */ 169 165 SKB_DROP_REASON_TCP_MD5FAILURE, 166 + /** 167 + * @SKB_DROP_REASON_TCP_AONOTFOUND: no TCP-AO hash and one was expected 168 + */ 169 + SKB_DROP_REASON_TCP_AONOTFOUND, 170 + /** 171 + * @SKB_DROP_REASON_TCP_AOUNEXPECTED: TCP-AO hash is present and it 172 + * was not expected. 173 + */ 174 + SKB_DROP_REASON_TCP_AOUNEXPECTED, 175 + /** @SKB_DROP_REASON_TCP_AOKEYNOTFOUND: TCP-AO key is unknown */ 176 + SKB_DROP_REASON_TCP_AOKEYNOTFOUND, 177 + /** @SKB_DROP_REASON_TCP_AOFAILURE: TCP-AO hash is wrong */ 178 + SKB_DROP_REASON_TCP_AOFAILURE, 170 179 /** 171 180 * @SKB_DROP_REASON_SOCKET_BACKLOG: failed to add skb to socket backlog ( 172 181 * see LINUX_MIB_TCPBACKLOGDROP)
+51 -2
include/net/tcp.h
··· 1809 1809 enum skb_drop_reason 1810 1810 tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, 1811 1811 const void *saddr, const void *daddr, 1812 - int family, int dif, int sdif); 1812 + int family, int l3index, const __u8 *hash_location); 1813 1813 1814 1814 1815 1815 #define tcp_twsk_md5_key(twsk) ((twsk)->tw_md5_key) ··· 1831 1831 static inline enum skb_drop_reason 1832 1832 tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, 1833 1833 const void *saddr, const void *daddr, 1834 - int family, int dif, int sdif) 1834 + int family, int l3index, const __u8 *hash_location) 1835 1835 { 1836 1836 return SKB_NOT_DROPPED_YET; 1837 1837 } ··· 2728 2728 return true; 2729 2729 #endif 2730 2730 return false; 2731 + } 2732 + 2733 + /* Called with rcu_read_lock() */ 2734 + static inline enum skb_drop_reason 2735 + tcp_inbound_hash(struct sock *sk, const struct request_sock *req, 2736 + const struct sk_buff *skb, 2737 + const void *saddr, const void *daddr, 2738 + int family, int dif, int sdif) 2739 + { 2740 + const struct tcphdr *th = tcp_hdr(skb); 2741 + const struct tcp_ao_hdr *aoh; 2742 + const __u8 *md5_location; 2743 + int l3index; 2744 + 2745 + /* Invalid option or two times meet any of auth options */ 2746 + if (tcp_parse_auth_options(th, &md5_location, &aoh)) 2747 + return SKB_DROP_REASON_TCP_AUTH_HDR; 2748 + 2749 + if (req) { 2750 + if (tcp_rsk_used_ao(req) != !!aoh) 2751 + return SKB_DROP_REASON_TCP_AOFAILURE; 2752 + } 2753 + 2754 + /* sdif set, means packet ingressed via a device 2755 + * in an L3 domain and dif is set to the l3mdev 2756 + */ 2757 + l3index = sdif ? dif : 0; 2758 + 2759 + /* Fast path: unsigned segments */ 2760 + if (likely(!md5_location && !aoh)) { 2761 + /* Drop if there's TCP-MD5 or TCP-AO key with any rcvid/sndid 2762 + * for the remote peer. On TCP-AO established connection 2763 + * the last key is impossible to remove, so there's 2764 + * always at least one current_key. 2765 + */ 2766 + if (tcp_ao_required(sk, saddr, family)) 2767 + return SKB_DROP_REASON_TCP_AONOTFOUND; 2768 + if (unlikely(tcp_md5_do_lookup(sk, l3index, saddr, family))) { 2769 + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); 2770 + return SKB_DROP_REASON_TCP_MD5NOTFOUND; 2771 + } 2772 + return SKB_NOT_DROPPED_YET; 2773 + } 2774 + 2775 + if (aoh) 2776 + return tcp_inbound_ao_hash(sk, skb, family, req, aoh); 2777 + 2778 + return tcp_inbound_md5_hash(sk, skb, saddr, daddr, family, 2779 + l3index, md5_location); 2731 2780 } 2732 2781 2733 2782 #endif /* _TCP_H */
+14
include/net/tcp_ao.h
··· 111 111 }; 112 112 113 113 struct tcp_sigpool; 114 + #define TCP_AO_ESTABLISHED (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2 | \ 115 + TCPF_CLOSE | TCPF_CLOSE_WAIT | \ 116 + TCPF_LAST_ACK | TCPF_CLOSING) 114 117 115 118 int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, 116 119 struct tcp_ao_key *key, struct tcphdr *th, ··· 133 130 unsigned int len, struct tcp_sigpool *hp); 134 131 void tcp_ao_destroy_sock(struct sock *sk, bool twsk); 135 132 void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); 133 + enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, 134 + const struct sk_buff *skb, unsigned short int family, 135 + const struct request_sock *req, 136 + const struct tcp_ao_hdr *aoh); 136 137 struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, 137 138 const union tcp_ao_addr *addr, 138 139 int family, int sndid, int rcvid); ··· 213 206 struct tcp_request_sock *treq, 214 207 unsigned short int family) 215 208 { 209 + } 210 + 211 + static inline enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, 212 + const struct sk_buff *skb, unsigned short int family, 213 + const struct request_sock *req, const struct tcp_ao_hdr *aoh) 214 + { 215 + return SKB_NOT_DROPPED_YET; 216 216 } 217 217 218 218 static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
+8 -31
net/ipv4/tcp.c
··· 4375 4375 enum skb_drop_reason 4376 4376 tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, 4377 4377 const void *saddr, const void *daddr, 4378 - int family, int dif, int sdif) 4378 + int family, int l3index, const __u8 *hash_location) 4379 4379 { 4380 - /* 4381 - * This gets called for each TCP segment that arrives 4382 - * so we want to be efficient. 4380 + /* This gets called for each TCP segment that has TCP-MD5 option. 4383 4381 * We have 3 drop cases: 4384 4382 * o No MD5 hash and one expected. 4385 4383 * o MD5 hash and we're not expecting one. 4386 4384 * o MD5 hash and its wrong. 4387 4385 */ 4388 - const __u8 *hash_location = NULL; 4389 - struct tcp_md5sig_key *hash_expected; 4390 4386 const struct tcphdr *th = tcp_hdr(skb); 4391 4387 const struct tcp_sock *tp = tcp_sk(sk); 4392 - int genhash, l3index; 4388 + struct tcp_md5sig_key *key; 4393 4389 u8 newhash[16]; 4390 + int genhash; 4394 4391 4395 - /* sdif set, means packet ingressed via a device 4396 - * in an L3 domain and dif is set to the l3mdev 4397 - */ 4398 - l3index = sdif ? dif : 0; 4392 + key = tcp_md5_do_lookup(sk, l3index, saddr, family); 4399 4393 4400 - hash_expected = tcp_md5_do_lookup(sk, l3index, saddr, family); 4401 - if (tcp_parse_auth_options(th, &hash_location, NULL)) 4402 - return SKB_DROP_REASON_TCP_AUTH_HDR; 4403 - 4404 - /* We've parsed the options - do we have a hash? */ 4405 - if (!hash_expected && !hash_location) 4406 - return SKB_NOT_DROPPED_YET; 4407 - 4408 - if (hash_expected && !hash_location) { 4409 - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); 4410 - return SKB_DROP_REASON_TCP_MD5NOTFOUND; 4411 - } 4412 - 4413 - if (!hash_expected && hash_location) { 4394 + if (!key && hash_location) { 4414 4395 NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED); 4415 4396 return SKB_DROP_REASON_TCP_MD5UNEXPECTED; 4416 4397 } ··· 4401 4420 * IPv4-mapped case. 4402 4421 */ 4403 4422 if (family == AF_INET) 4404 - genhash = tcp_v4_md5_hash_skb(newhash, 4405 - hash_expected, 4406 - NULL, skb); 4423 + genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb); 4407 4424 else 4408 - genhash = tp->af_specific->calc_md5_hash(newhash, 4409 - hash_expected, 4425 + genhash = tp->af_specific->calc_md5_hash(newhash, key, 4410 4426 NULL, skb); 4411 - 4412 4427 if (genhash || memcmp(hash_location, newhash, 16) != 0) { 4413 4428 NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE); 4414 4429 if (family == AF_INET) {
+142
net/ipv4/tcp_ao.c
··· 761 761 treq->maclen = tcp_ao_maclen(key); 762 762 } 763 763 764 + static enum skb_drop_reason 765 + tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, 766 + unsigned short int family, struct tcp_ao_info *info, 767 + const struct tcp_ao_hdr *aoh, struct tcp_ao_key *key, 768 + u8 *traffic_key, u8 *phash, u32 sne) 769 + { 770 + u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr); 771 + const struct tcphdr *th = tcp_hdr(skb); 772 + void *hash_buf = NULL; 773 + 774 + if (maclen != tcp_ao_maclen(key)) 775 + return SKB_DROP_REASON_TCP_AOFAILURE; 776 + 777 + hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 778 + if (!hash_buf) 779 + return SKB_DROP_REASON_NOT_SPECIFIED; 780 + 781 + /* XXX: make it per-AF callback? */ 782 + tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key, 783 + (phash - (u8 *)th), sne); 784 + if (memcmp(phash, hash_buf, maclen)) { 785 + kfree(hash_buf); 786 + return SKB_DROP_REASON_TCP_AOFAILURE; 787 + } 788 + kfree(hash_buf); 789 + return SKB_NOT_DROPPED_YET; 790 + } 791 + 792 + enum skb_drop_reason 793 + tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, 794 + unsigned short int family, const struct request_sock *req, 795 + const struct tcp_ao_hdr *aoh) 796 + { 797 + const struct tcphdr *th = tcp_hdr(skb); 798 + u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */ 799 + struct tcp_ao_info *info; 800 + enum skb_drop_reason ret; 801 + struct tcp_ao_key *key; 802 + __be32 sisn, disn; 803 + u8 *traffic_key; 804 + u32 sne = 0; 805 + 806 + info = rcu_dereference(tcp_sk(sk)->ao_info); 807 + if (!info) 808 + return SKB_DROP_REASON_TCP_AOUNEXPECTED; 809 + 810 + if (unlikely(th->syn)) { 811 + sisn = th->seq; 812 + disn = 0; 813 + } 814 + 815 + /* Fast-path */ 816 + if (likely((1 << sk->sk_state) & TCP_AO_ESTABLISHED)) { 817 + enum skb_drop_reason err; 818 + struct tcp_ao_key *current_key; 819 + 820 + /* Check if this socket's rnext_key matches the keyid in the 821 + * packet. If not we lookup the key based on the keyid 822 + * matching the rcvid in the mkt. 823 + */ 824 + key = READ_ONCE(info->rnext_key); 825 + if (key->rcvid != aoh->keyid) { 826 + key = tcp_ao_established_key(info, -1, aoh->keyid); 827 + if (!key) 828 + goto key_not_found; 829 + } 830 + 831 + /* Delayed retransmitted SYN */ 832 + if (unlikely(th->syn && !th->ack)) 833 + goto verify_hash; 834 + 835 + sne = 0; 836 + /* Established socket, traffic key are cached */ 837 + traffic_key = rcv_other_key(key); 838 + err = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, 839 + traffic_key, phash, sne); 840 + if (err) 841 + return err; 842 + current_key = READ_ONCE(info->current_key); 843 + /* Key rotation: the peer asks us to use new key (RNext) */ 844 + if (unlikely(aoh->rnext_keyid != current_key->sndid)) { 845 + /* If the key is not found we do nothing. */ 846 + key = tcp_ao_established_key(info, aoh->rnext_keyid, -1); 847 + if (key) 848 + /* pairs with tcp_ao_del_cmd */ 849 + WRITE_ONCE(info->current_key, key); 850 + } 851 + return SKB_NOT_DROPPED_YET; 852 + } 853 + 854 + /* Lookup key based on peer address and keyid. 855 + * current_key and rnext_key must not be used on tcp listen 856 + * sockets as otherwise: 857 + * - request sockets would race on those key pointers 858 + * - tcp_ao_del_cmd() allows async key removal 859 + */ 860 + key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid); 861 + if (!key) 862 + goto key_not_found; 863 + 864 + if (th->syn && !th->ack) 865 + goto verify_hash; 866 + 867 + if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { 868 + /* Make the initial syn the likely case here */ 869 + if (unlikely(req)) { 870 + sne = 0; 871 + sisn = htonl(tcp_rsk(req)->rcv_isn); 872 + disn = htonl(tcp_rsk(req)->snt_isn); 873 + } else if (unlikely(th->ack && !th->syn)) { 874 + /* Possible syncookie packet */ 875 + sisn = htonl(ntohl(th->seq) - 1); 876 + disn = htonl(ntohl(th->ack_seq) - 1); 877 + sne = 0; 878 + } else if (unlikely(!th->syn)) { 879 + /* no way to figure out initial sisn/disn - drop */ 880 + return SKB_DROP_REASON_TCP_FLAGS; 881 + } 882 + } else if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { 883 + disn = info->lisn; 884 + if (th->syn || th->rst) 885 + sisn = th->seq; 886 + else 887 + sisn = info->risn; 888 + } else { 889 + WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", sk->sk_state); 890 + return SKB_DROP_REASON_TCP_AOFAILURE; 891 + } 892 + verify_hash: 893 + traffic_key = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 894 + if (!traffic_key) 895 + return SKB_DROP_REASON_NOT_SPECIFIED; 896 + tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family); 897 + ret = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, 898 + traffic_key, phash, sne); 899 + kfree(traffic_key); 900 + return ret; 901 + 902 + key_not_found: 903 + return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; 904 + } 905 + 764 906 static int tcp_ao_cache_traffic_keys(const struct sock *sk, 765 907 struct tcp_ao_info *ao, 766 908 struct tcp_ao_key *ao_key)
+5 -5
net/ipv4/tcp_ipv4.c
··· 2204 2204 if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) 2205 2205 drop_reason = SKB_DROP_REASON_XFRM_POLICY; 2206 2206 else 2207 - drop_reason = tcp_inbound_md5_hash(sk, skb, 2208 - &iph->saddr, &iph->daddr, 2209 - AF_INET, dif, sdif); 2207 + drop_reason = tcp_inbound_hash(sk, req, skb, 2208 + &iph->saddr, &iph->daddr, 2209 + AF_INET, dif, sdif); 2210 2210 if (unlikely(drop_reason)) { 2211 2211 sk_drops_add(sk, skb); 2212 2212 reqsk_put(req); ··· 2283 2283 goto discard_and_relse; 2284 2284 } 2285 2285 2286 - drop_reason = tcp_inbound_md5_hash(sk, skb, &iph->saddr, 2287 - &iph->daddr, AF_INET, dif, sdif); 2286 + drop_reason = tcp_inbound_hash(sk, NULL, skb, &iph->saddr, &iph->daddr, 2287 + AF_INET, dif, sdif); 2288 2288 if (drop_reason) 2289 2289 goto discard_and_relse; 2290 2290
+5 -4
net/ipv6/tcp_ao.c
··· 53 53 const struct sk_buff *skb, 54 54 __be32 sisn, __be32 disn) 55 55 { 56 - const struct ipv6hdr *iph = ipv6_hdr(skb); 57 - const struct tcphdr *th = tcp_hdr(skb); 56 + const struct ipv6hdr *iph = ipv6_hdr(skb); 57 + const struct tcphdr *th = tcp_hdr(skb); 58 58 59 - return tcp_v6_ao_calc_key(mkt, key, &iph->saddr, &iph->daddr, 60 - th->source, th->dest, sisn, disn); 59 + return tcp_v6_ao_calc_key(mkt, key, &iph->saddr, 60 + &iph->daddr, th->source, 61 + th->dest, sisn, disn); 61 62 } 62 63 63 64 int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
+6 -5
net/ipv6/tcp_ipv6.c
··· 1785 1785 if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) 1786 1786 drop_reason = SKB_DROP_REASON_XFRM_POLICY; 1787 1787 else 1788 - drop_reason = tcp_inbound_md5_hash(sk, skb, 1789 - &hdr->saddr, &hdr->daddr, 1790 - AF_INET6, dif, sdif); 1788 + drop_reason = tcp_inbound_hash(sk, req, skb, 1789 + &hdr->saddr, &hdr->daddr, 1790 + AF_INET6, dif, sdif); 1791 1791 if (drop_reason) { 1792 1792 sk_drops_add(sk, skb); 1793 1793 reqsk_put(req); ··· 1861 1861 goto discard_and_relse; 1862 1862 } 1863 1863 1864 - drop_reason = tcp_inbound_md5_hash(sk, skb, &hdr->saddr, &hdr->daddr, 1865 - AF_INET6, dif, sdif); 1864 + drop_reason = tcp_inbound_hash(sk, NULL, skb, &hdr->saddr, &hdr->daddr, 1865 + AF_INET6, dif, sdif); 1866 1866 if (drop_reason) 1867 1867 goto discard_and_relse; 1868 1868 ··· 2089 2089 .ao_lookup = tcp_v6_ao_lookup, 2090 2090 .calc_ao_hash = tcp_v4_ao_hash_skb, 2091 2091 .ao_parse = tcp_v6_parse_ao, 2092 + .ao_calc_key_sk = tcp_v4_ao_calc_key_sk, 2092 2093 #endif 2093 2094 }; 2094 2095 #endif