1  // SPDX-License-Identifier: GPL-2.0-or-later
2  /*
3   * INET		An implementation of the TCP Authentication Option (TCP-AO).
4   *		See RFC5925.
5   *
6   * Authors:	Dmitry Safonov <dima@arista.com>
7   *		Francesco Ruggeri <fruggeri@arista.com>
8   *		Salam Noureddine <noureddine@arista.com>
9   */
10  #include <crypto/hash.h>
11  #include <linux/tcp.h>
12  
13  #include <net/tcp.h>
14  #include <net/ipv6.h>
15  
tcp_v6_ao_calc_key(struct tcp_ao_key * mkt,u8 * key,const struct in6_addr * saddr,const struct in6_addr * daddr,__be16 sport,__be16 dport,__be32 sisn,__be32 disn)16  static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
17  			      const struct in6_addr *saddr,
18  			      const struct in6_addr *daddr,
19  			      __be16 sport, __be16 dport,
20  			      __be32 sisn, __be32 disn)
21  {
22  	struct kdf_input_block {
23  		u8			counter;
24  		u8			label[6];
25  		struct tcp6_ao_context	ctx;
26  		__be16			outlen;
27  	} __packed * tmp;
28  	struct tcp_sigpool hp;
29  	int err;
30  
31  	err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp);
32  	if (err)
33  		return err;
34  
35  	tmp = hp.scratch;
36  	tmp->counter	= 1;
37  	memcpy(tmp->label, "TCP-AO", 6);
38  	tmp->ctx.saddr	= *saddr;
39  	tmp->ctx.daddr	= *daddr;
40  	tmp->ctx.sport	= sport;
41  	tmp->ctx.dport	= dport;
42  	tmp->ctx.sisn	= sisn;
43  	tmp->ctx.disn	= disn;
44  	tmp->outlen	= htons(tcp_ao_digest_size(mkt) * 8); /* in bits */
45  
46  	err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp);
47  	tcp_sigpool_end(&hp);
48  
49  	return err;
50  }
51  
tcp_v6_ao_calc_key_skb(struct tcp_ao_key * mkt,u8 * key,const struct sk_buff * skb,__be32 sisn,__be32 disn)52  int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
53  			   const struct sk_buff *skb,
54  			   __be32 sisn, __be32 disn)
55  {
56  	const struct ipv6hdr *iph = ipv6_hdr(skb);
57  	const struct tcphdr *th = tcp_hdr(skb);
58  
59  	return tcp_v6_ao_calc_key(mkt, key, &iph->saddr,
60  				  &iph->daddr, th->source,
61  				  th->dest, sisn, disn);
62  }
63  
tcp_v6_ao_calc_key_sk(struct tcp_ao_key * mkt,u8 * key,const struct sock * sk,__be32 sisn,__be32 disn,bool send)64  int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
65  			  const struct sock *sk, __be32 sisn,
66  			  __be32 disn, bool send)
67  {
68  	if (send)
69  		return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_rcv_saddr,
70  					  &sk->sk_v6_daddr, htons(sk->sk_num),
71  					  sk->sk_dport, sisn, disn);
72  	else
73  		return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_daddr,
74  					  &sk->sk_v6_rcv_saddr, sk->sk_dport,
75  					  htons(sk->sk_num), disn, sisn);
76  }
77  
tcp_v6_ao_calc_key_rsk(struct tcp_ao_key * mkt,u8 * key,struct request_sock * req)78  int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
79  			   struct request_sock *req)
80  {
81  	struct inet_request_sock *ireq = inet_rsk(req);
82  
83  	return tcp_v6_ao_calc_key(mkt, key,
84  			&ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr,
85  			htons(ireq->ir_num), ireq->ir_rmt_port,
86  			htonl(tcp_rsk(req)->snt_isn),
87  			htonl(tcp_rsk(req)->rcv_isn));
88  }
89  
tcp_v6_ao_lookup(const struct sock * sk,struct sock * addr_sk,int sndid,int rcvid)90  struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
91  				    struct sock *addr_sk,
92  				    int sndid, int rcvid)
93  {
94  	int l3index = l3mdev_master_ifindex_by_index(sock_net(sk),
95  						     addr_sk->sk_bound_dev_if);
96  	struct in6_addr *addr = &addr_sk->sk_v6_daddr;
97  
98  	return tcp_ao_do_lookup(sk, l3index, (union tcp_ao_addr *)addr,
99  				AF_INET6, sndid, rcvid);
100  }
101  
tcp_v6_ao_lookup_rsk(const struct sock * sk,struct request_sock * req,int sndid,int rcvid)102  struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk,
103  					struct request_sock *req,
104  					int sndid, int rcvid)
105  {
106  	struct inet_request_sock *ireq = inet_rsk(req);
107  	struct in6_addr *addr = &ireq->ir_v6_rmt_addr;
108  	int l3index;
109  
110  	l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif);
111  	return tcp_ao_do_lookup(sk, l3index, (union tcp_ao_addr *)addr,
112  				AF_INET6, sndid, rcvid);
113  }
114  
tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool * hp,const struct in6_addr * daddr,const struct in6_addr * saddr,int nbytes)115  int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp,
116  				const struct in6_addr *daddr,
117  				const struct in6_addr *saddr, int nbytes)
118  {
119  	struct tcp6_pseudohdr *bp;
120  	struct scatterlist sg;
121  
122  	bp = hp->scratch;
123  	/* 1. TCP pseudo-header (RFC2460) */
124  	bp->saddr = *saddr;
125  	bp->daddr = *daddr;
126  	bp->len = cpu_to_be32(nbytes);
127  	bp->protocol = cpu_to_be32(IPPROTO_TCP);
128  
129  	sg_init_one(&sg, bp, sizeof(*bp));
130  	ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp));
131  	return crypto_ahash_update(hp->req);
132  }
133  
tcp_v6_ao_hash_skb(char * ao_hash,struct tcp_ao_key * key,const struct sock * sk,const struct sk_buff * skb,const u8 * tkey,int hash_offset,u32 sne)134  int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
135  		       const struct sock *sk, const struct sk_buff *skb,
136  		       const u8 *tkey, int hash_offset, u32 sne)
137  {
138  	return tcp_ao_hash_skb(AF_INET6, ao_hash, key, sk, skb, tkey,
139  			hash_offset, sne);
140  }
141  
tcp_v6_parse_ao(struct sock * sk,int cmd,sockptr_t optval,int optlen)142  int tcp_v6_parse_ao(struct sock *sk, int cmd,
143  		    sockptr_t optval, int optlen)
144  {
145  	return tcp_parse_ao(sk, cmd, AF_INET6, optval, optlen);
146  }
147  
tcp_v6_ao_synack_hash(char * ao_hash,struct tcp_ao_key * ao_key,struct request_sock * req,const struct sk_buff * skb,int hash_offset,u32 sne)148  int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
149  			  struct request_sock *req, const struct sk_buff *skb,
150  			  int hash_offset, u32 sne)
151  {
152  	void *hash_buf = NULL;
153  	int err;
154  
155  	hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC);
156  	if (!hash_buf)
157  		return -ENOMEM;
158  
159  	err = tcp_v6_ao_calc_key_rsk(ao_key, hash_buf, req);
160  	if (err)
161  		goto out;
162  
163  	err = tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb,
164  			      hash_buf, hash_offset, sne);
165  out:
166  	kfree(hash_buf);
167  	return err;
168  }
169