xref: /dragonfly/sys/net/wg/wg_cookie.c (revision 03c3b87e)
1a364ee04SAaron LI /*-
2a364ee04SAaron LI  * SPDX-License-Identifier: ISC
3a6bca3d2SAaron LI  *
4a6bca3d2SAaron LI  * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
5a6bca3d2SAaron LI  * Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
6a364ee04SAaron LI  *
7a364ee04SAaron LI  * Permission to use, copy, modify, and distribute this software for any
8a364ee04SAaron LI  * purpose with or without fee is hereby granted, provided that the above
9a364ee04SAaron LI  * copyright notice and this permission notice appear in all copies.
10a364ee04SAaron LI  *
11a364ee04SAaron LI  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12a364ee04SAaron LI  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13a364ee04SAaron LI  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14a364ee04SAaron LI  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a364ee04SAaron LI  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a364ee04SAaron LI  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17a364ee04SAaron LI  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18a6bca3d2SAaron LI  */
19a6bca3d2SAaron LI 
20a6bca3d2SAaron LI #include "opt_inet6.h"
21a6bca3d2SAaron LI 
22a6bca3d2SAaron LI #include <sys/param.h>
23a6bca3d2SAaron LI #include <sys/systm.h>
2464544dd5SAaron LI #include <sys/callout.h>
25a6bca3d2SAaron LI #include <sys/kernel.h>
26a6bca3d2SAaron LI #include <sys/lock.h>
2764544dd5SAaron LI #include <sys/malloc.h>
28be332237SAaron LI #include <sys/objcache.h>
291fdfe5a9SAaron LI #include <sys/queue.h>
30a6bca3d2SAaron LI #include <sys/socket.h>
312bed72b3SAaron LI #include <sys/time.h>
32a6bca3d2SAaron LI #include <netinet/in.h>
33a6bca3d2SAaron LI 
34cfdd69bcSAaron LI #include <crypto/chachapoly.h>
35d94868ddSAaron LI #include <crypto/blake2/blake2s.h>
36d94868ddSAaron LI #include <crypto/siphash/siphash.h>
37d94868ddSAaron LI 
38a6bca3d2SAaron LI #include "wg_cookie.h"
39a6bca3d2SAaron LI 
40*03c3b87eSAaron LI /* Constants for cookies */
41*03c3b87eSAaron LI #define COOKIE_KEY_SIZE		BLAKE2S_KEY_SIZE
42*03c3b87eSAaron LI #define COOKIE_SECRET_SIZE	32
43a6bca3d2SAaron LI #define COOKIE_MAC1_KEY_LABEL	"mac1----"
44a6bca3d2SAaron LI #define COOKIE_COOKIE_KEY_LABEL	"cookie--"
45a6bca3d2SAaron LI #define COOKIE_SECRET_MAX_AGE	120
46a6bca3d2SAaron LI #define COOKIE_SECRET_LATENCY	5
47a6bca3d2SAaron LI 
48a6bca3d2SAaron LI /* Constants for initiation rate limiting */
49a6bca3d2SAaron LI #define RATELIMIT_SIZE		(1 << 13)
50a6bca3d2SAaron LI #define RATELIMIT_MASK		(RATELIMIT_SIZE - 1)
51a6bca3d2SAaron LI #define RATELIMIT_SIZE_MAX	(RATELIMIT_SIZE * 8)
522bed72b3SAaron LI #define NSEC_PER_SEC		1000000000LL
53a6bca3d2SAaron LI #define INITIATIONS_PER_SECOND	20
54a6bca3d2SAaron LI #define INITIATIONS_BURSTABLE	5
552bed72b3SAaron LI #define INITIATION_COST		(NSEC_PER_SEC / INITIATIONS_PER_SECOND)
56a6bca3d2SAaron LI #define TOKEN_MAX		(INITIATION_COST * INITIATIONS_BURSTABLE)
571ef0d803SAaron LI #define ELEMENT_TIMEOUT		1 /* second */
58a6bca3d2SAaron LI #define IPV4_MASK_SIZE		4 /* Use all 4 bytes of IPv4 address */
59a6bca3d2SAaron LI #define IPV6_MASK_SIZE		8 /* Use top 8 bytes (/64) of IPv6 address */
60a6bca3d2SAaron LI 
61*03c3b87eSAaron LI struct cookie_maker {
62*03c3b87eSAaron LI 	uint8_t		cm_mac1_key[COOKIE_KEY_SIZE];
63*03c3b87eSAaron LI 	uint8_t		cm_cookie_key[COOKIE_KEY_SIZE];
64*03c3b87eSAaron LI 
65*03c3b87eSAaron LI 	struct lock	cm_lock;
66*03c3b87eSAaron LI 	bool		cm_cookie_valid;
67*03c3b87eSAaron LI 	uint8_t		cm_cookie[COOKIE_COOKIE_SIZE];
68*03c3b87eSAaron LI 	struct timespec	cm_cookie_birthdate;	/* nanouptime */
69*03c3b87eSAaron LI 	bool		cm_mac1_sent;
70*03c3b87eSAaron LI 	uint8_t		cm_mac1_last[COOKIE_MAC_SIZE];
71*03c3b87eSAaron LI };
72*03c3b87eSAaron LI 
73*03c3b87eSAaron LI struct cookie_checker {
74*03c3b87eSAaron LI 	struct lock	cc_key_lock;
75*03c3b87eSAaron LI 	uint8_t		cc_mac1_key[COOKIE_KEY_SIZE];
76*03c3b87eSAaron LI 	uint8_t		cc_cookie_key[COOKIE_KEY_SIZE];
77*03c3b87eSAaron LI 
78*03c3b87eSAaron LI 	struct lock	cc_secret_mtx;
79*03c3b87eSAaron LI 	struct timespec	cc_secret_birthdate;	/* nanouptime */
80*03c3b87eSAaron LI 	uint8_t		cc_secret[COOKIE_SECRET_SIZE];
81*03c3b87eSAaron LI };
82*03c3b87eSAaron LI 
83a6bca3d2SAaron LI struct ratelimit_key {
84a6bca3d2SAaron LI 	uint8_t ip[IPV6_MASK_SIZE];
85a6bca3d2SAaron LI };
86a6bca3d2SAaron LI 
87a6bca3d2SAaron LI struct ratelimit_entry {
88a6bca3d2SAaron LI 	LIST_ENTRY(ratelimit_entry)	r_entry;
89a6bca3d2SAaron LI 	struct ratelimit_key		r_key;
902bed72b3SAaron LI 	struct timespec			r_last_time;	/* nanouptime */
91a6bca3d2SAaron LI 	uint64_t			r_tokens;
92a6bca3d2SAaron LI };
93a6bca3d2SAaron LI 
94a6bca3d2SAaron LI struct ratelimit {
95a6bca3d2SAaron LI 	uint8_t				rl_secret[SIPHASH_KEY_LENGTH];
96139109cdSAaron LI 	struct lock			rl_mtx;
97a6bca3d2SAaron LI 	struct callout			rl_gc;
98a6bca3d2SAaron LI 	LIST_HEAD(, ratelimit_entry)	rl_table[RATELIMIT_SIZE];
99a6bca3d2SAaron LI 	size_t				rl_table_num;
100a6bca3d2SAaron LI 	bool				rl_initialized;
101a6bca3d2SAaron LI };
102a6bca3d2SAaron LI 
1031ef0d803SAaron LI 
104a6bca3d2SAaron LI static void	macs_mac1(struct cookie_macs *, const void *, size_t,
105a6bca3d2SAaron LI 			  const uint8_t[COOKIE_KEY_SIZE]);
106a6bca3d2SAaron LI static void	macs_mac2(struct cookie_macs *, const void *, size_t,
107a6bca3d2SAaron LI 			  const uint8_t[COOKIE_COOKIE_SIZE]);
108a6bca3d2SAaron LI static void	make_cookie(struct cookie_checker *,
1091ef0d803SAaron LI 			    uint8_t[COOKIE_COOKIE_SIZE],
1101ef0d803SAaron LI 			    const struct sockaddr *);
1111ef0d803SAaron LI static void	precompute_key(uint8_t[COOKIE_KEY_SIZE],
112c3668ed7SAaron LI 			       const uint8_t[COOKIE_INPUT_SIZE],
113c3668ed7SAaron LI 			       const uint8_t *, size_t);
114a6bca3d2SAaron LI static void	ratelimit_init(struct ratelimit *);
115a6bca3d2SAaron LI static void	ratelimit_deinit(struct ratelimit *);
116a6bca3d2SAaron LI static void	ratelimit_gc_callout(void *);
117a6bca3d2SAaron LI static void	ratelimit_gc_schedule(struct ratelimit *);
118a6bca3d2SAaron LI static void	ratelimit_gc(struct ratelimit *, bool);
1191ef0d803SAaron LI static int	ratelimit_allow(struct ratelimit *, const struct sockaddr *);
1201ef0d803SAaron LI 
121a6bca3d2SAaron LI 
122a6bca3d2SAaron LI static struct ratelimit ratelimit_v4;
123a6bca3d2SAaron LI #ifdef INET6
124a6bca3d2SAaron LI static struct ratelimit ratelimit_v6;
125a6bca3d2SAaron LI #endif
126be332237SAaron LI 
127be332237SAaron LI static struct objcache *ratelimit_zone;
1281ef0d803SAaron LI static MALLOC_DEFINE(M_WG_RATELIMIT, "WG ratelimit", "wireguard ratelimit");
129*03c3b87eSAaron LI static MALLOC_DEFINE(M_WG_COOKIE, "WG cookie", "wireguard cookie");
130a6bca3d2SAaron LI 
1311ef0d803SAaron LI 
1321ef0d803SAaron LI static inline uint64_t
siphash13(const uint8_t key[SIPHASH_KEY_LENGTH],const void * src,size_t len)1331ef0d803SAaron LI siphash13(const uint8_t key[SIPHASH_KEY_LENGTH], const void *src, size_t len)
1341ef0d803SAaron LI {
1351ef0d803SAaron LI 	SIPHASH_CTX ctx;
1361ef0d803SAaron LI 	return SipHashX(&ctx, 1, 3, key, src, len);
1371ef0d803SAaron LI }
1381ef0d803SAaron LI 
139682f87c9SAaron LI static inline bool
timer_expired(const struct timespec * birthdate,time_t sec,long nsec)1401ef0d803SAaron LI timer_expired(const struct timespec *birthdate, time_t sec, long nsec)
1411ef0d803SAaron LI {
1421ef0d803SAaron LI 	struct timespec uptime;
1431ef0d803SAaron LI 	struct timespec expire = { .tv_sec = sec, .tv_nsec = nsec };
1441ef0d803SAaron LI 
1451ef0d803SAaron LI 	if (birthdate->tv_sec == 0 && birthdate->tv_nsec == 0)
146682f87c9SAaron LI 		return (true);
1471ef0d803SAaron LI 
1481ef0d803SAaron LI 	getnanouptime(&uptime);
1491ef0d803SAaron LI 	timespecadd(birthdate, &expire, &expire);
150682f87c9SAaron LI 	return timespeccmp(&uptime, &expire, >);
1511ef0d803SAaron LI }
1521ef0d803SAaron LI 
1531ef0d803SAaron LI /*----------------------------------------------------------------------------*/
154a6bca3d2SAaron LI /* Public Functions */
1551ef0d803SAaron LI 
156a6bca3d2SAaron LI int
cookie_init(void)157a6bca3d2SAaron LI cookie_init(void)
158a6bca3d2SAaron LI {
1591ef0d803SAaron LI 	ratelimit_zone = objcache_create_simple(
1601ef0d803SAaron LI 	    M_WG_RATELIMIT, sizeof(struct ratelimit_entry));
161be332237SAaron LI 	if (ratelimit_zone == NULL)
1621ef0d803SAaron LI 		return (ENOMEM);
163a6bca3d2SAaron LI 
164a6bca3d2SAaron LI 	ratelimit_init(&ratelimit_v4);
165a6bca3d2SAaron LI #ifdef INET6
166a6bca3d2SAaron LI 	ratelimit_init(&ratelimit_v6);
167a6bca3d2SAaron LI #endif
1681ef0d803SAaron LI 
169a6bca3d2SAaron LI 	return (0);
170a6bca3d2SAaron LI }
171a6bca3d2SAaron LI 
172a6bca3d2SAaron LI void
cookie_deinit(void)173a6bca3d2SAaron LI cookie_deinit(void)
174a6bca3d2SAaron LI {
175a6bca3d2SAaron LI 	ratelimit_deinit(&ratelimit_v4);
176a6bca3d2SAaron LI #ifdef INET6
177a6bca3d2SAaron LI 	ratelimit_deinit(&ratelimit_v6);
178a6bca3d2SAaron LI #endif
179a6bca3d2SAaron LI 	if (ratelimit_zone != NULL)
180be332237SAaron LI 		objcache_destroy(ratelimit_zone);
181a6bca3d2SAaron LI }
182a6bca3d2SAaron LI 
183*03c3b87eSAaron LI struct cookie_checker *
cookie_checker_alloc(void)184*03c3b87eSAaron LI cookie_checker_alloc(void)
185a6bca3d2SAaron LI {
186*03c3b87eSAaron LI 	struct cookie_checker *cc;
187*03c3b87eSAaron LI 
188*03c3b87eSAaron LI 	cc = kmalloc(sizeof(*cc), M_WG_COOKIE, M_WAITOK | M_ZERO);
1897ef217feSAaron LI 	lockinit(&cc->cc_key_lock, "cookie_checker_key", 0, 0);
190139109cdSAaron LI 	lockinit(&cc->cc_secret_mtx, "cookie_checker_secret", 0, 0);
191*03c3b87eSAaron LI 
192*03c3b87eSAaron LI 	return (cc);
193a6bca3d2SAaron LI }
194a6bca3d2SAaron LI 
195a6bca3d2SAaron LI void
cookie_checker_free(struct cookie_checker * cc)196a6bca3d2SAaron LI cookie_checker_free(struct cookie_checker *cc)
197a6bca3d2SAaron LI {
1987ef217feSAaron LI 	lockuninit(&cc->cc_key_lock);
199139109cdSAaron LI 	lockuninit(&cc->cc_secret_mtx);
200a6bca3d2SAaron LI 	explicit_bzero(cc, sizeof(*cc));
201*03c3b87eSAaron LI 	kfree(cc, M_WG_COOKIE);
202a6bca3d2SAaron LI }
203a6bca3d2SAaron LI 
204a6bca3d2SAaron LI void
cookie_checker_update(struct cookie_checker * cc,const uint8_t key[COOKIE_INPUT_SIZE])205a6bca3d2SAaron LI cookie_checker_update(struct cookie_checker *cc,
206a6bca3d2SAaron LI 		      const uint8_t key[COOKIE_INPUT_SIZE])
207a6bca3d2SAaron LI {
2087ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_EXCLUSIVE);
2091ef0d803SAaron LI 	if (key != NULL) {
210c3668ed7SAaron LI 		precompute_key(cc->cc_mac1_key, key, COOKIE_MAC1_KEY_LABEL,
211c3668ed7SAaron LI 			       sizeof(COOKIE_MAC1_KEY_LABEL) - 1);
212c3668ed7SAaron LI 		precompute_key(cc->cc_cookie_key, key, COOKIE_COOKIE_KEY_LABEL,
213c3668ed7SAaron LI 			       sizeof(COOKIE_COOKIE_KEY_LABEL) - 1);
214a6bca3d2SAaron LI 	} else {
215a6bca3d2SAaron LI 		bzero(cc->cc_mac1_key, sizeof(cc->cc_mac1_key));
216a6bca3d2SAaron LI 		bzero(cc->cc_cookie_key, sizeof(cc->cc_cookie_key));
217a6bca3d2SAaron LI 	}
2187ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_RELEASE);
219a6bca3d2SAaron LI }
220a6bca3d2SAaron LI 
221a6bca3d2SAaron LI void
cookie_checker_create_payload(struct cookie_checker * cc,const struct cookie_macs * macs,uint8_t nonce[COOKIE_NONCE_SIZE],uint8_t ecookie[COOKIE_ENCRYPTED_SIZE],const struct sockaddr * sa)222a6bca3d2SAaron LI cookie_checker_create_payload(struct cookie_checker *cc,
2231ef0d803SAaron LI 			      const struct cookie_macs *macs,
2241ef0d803SAaron LI 			      uint8_t nonce[COOKIE_NONCE_SIZE],
2251ef0d803SAaron LI 			      uint8_t ecookie[COOKIE_ENCRYPTED_SIZE],
2261ef0d803SAaron LI 			      const struct sockaddr *sa)
227a6bca3d2SAaron LI {
228a6bca3d2SAaron LI 	uint8_t cookie[COOKIE_COOKIE_SIZE];
229a6bca3d2SAaron LI 
230a6bca3d2SAaron LI 	make_cookie(cc, cookie, sa);
231a9b5f3fdSAaron LI 	karc4random_buf(nonce, COOKIE_NONCE_SIZE);
232a6bca3d2SAaron LI 
2337ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_SHARED);
234a6bca3d2SAaron LI 	xchacha20poly1305_encrypt(ecookie, cookie, COOKIE_COOKIE_SIZE,
2351ef0d803SAaron LI 				  macs->mac1, COOKIE_MAC_SIZE, nonce,
2361ef0d803SAaron LI 				  cc->cc_cookie_key);
2377ef217feSAaron LI 	lockmgr(&cc->cc_key_lock, LK_RELEASE);
238a6bca3d2SAaron LI 
239a6bca3d2SAaron LI 	explicit_bzero(cookie, sizeof(cookie));
240a6bca3d2SAaron LI }
241a6bca3d2SAaron LI 
2421ef0d803SAaron LI int
cookie_checker_validate_macs(struct cookie_checker * cc,const struct cookie_macs * macs,const void * buf,size_t len,bool check_cookie,const struct sockaddr * sa)2431ef0d803SAaron LI cookie_checker_validate_macs(struct cookie_checker *cc,
2441ef0d803SAaron LI 			     const struct cookie_macs *macs,
2451ef0d803SAaron LI 			     const void *buf, size_t len, bool check_cookie,
2461ef0d803SAaron LI 			     const struct sockaddr *sa)
2471ef0d803SAaron LI {
2481ef0d803SAaron LI 	struct cookie_macs our_macs;
2491ef0d803SAaron LI 	uint8_t cookie[COOKIE_COOKIE_SIZE];
2501ef0d803SAaron LI 
2511ef0d803SAaron LI 	/* Validate incoming MACs */
2521ef0d803SAaron LI 	lockmgr(&cc->cc_key_lock, LK_SHARED);
2531ef0d803SAaron LI 	macs_mac1(&our_macs, buf, len, cc->cc_mac1_key);
2541ef0d803SAaron LI 	lockmgr(&cc->cc_key_lock, LK_RELEASE);
2551ef0d803SAaron LI 
2561ef0d803SAaron LI 	/* If mac1 is invald, we want to drop the packet */
2571ef0d803SAaron LI 	if (timingsafe_bcmp(our_macs.mac1, macs->mac1, COOKIE_MAC_SIZE) != 0)
2581ef0d803SAaron LI 		return (EINVAL);
2591ef0d803SAaron LI 
2601ef0d803SAaron LI 	if (check_cookie) {
2611ef0d803SAaron LI 		make_cookie(cc, cookie, sa);
2621ef0d803SAaron LI 		macs_mac2(&our_macs, buf, len, cookie);
2631ef0d803SAaron LI 
2641ef0d803SAaron LI 		/* If mac2 is invalid, we want to send a cookie response. */
2651ef0d803SAaron LI 		if (timingsafe_bcmp(our_macs.mac2, macs->mac2, COOKIE_MAC_SIZE)
2661ef0d803SAaron LI 		    != 0)
2671ef0d803SAaron LI 			return (EAGAIN);
2681ef0d803SAaron LI 
2691ef0d803SAaron LI 		/*
2701ef0d803SAaron LI 		 * If the mac2 is valid, we may want to rate limit the peer.
2711ef0d803SAaron LI 		 * ratelimit_allow() will return either 0 or ECONNREFUSED,
2721ef0d803SAaron LI 		 * implying there is no ratelimiting, or we should ratelimit
2731ef0d803SAaron LI 		 * (refuse), respectively.
2741ef0d803SAaron LI 		 */
2751ef0d803SAaron LI 		if (sa->sa_family == AF_INET)
2761ef0d803SAaron LI 			return ratelimit_allow(&ratelimit_v4, sa);
2771ef0d803SAaron LI #ifdef INET6
2781ef0d803SAaron LI 		else if (sa->sa_family == AF_INET6)
2791ef0d803SAaron LI 			return ratelimit_allow(&ratelimit_v6, sa);
2801ef0d803SAaron LI #endif
2811ef0d803SAaron LI 		else
2821ef0d803SAaron LI 			return (EAFNOSUPPORT);
2831ef0d803SAaron LI 	}
2841ef0d803SAaron LI 
2851ef0d803SAaron LI 	return (0);
2861ef0d803SAaron LI }
2871ef0d803SAaron LI 
288*03c3b87eSAaron LI struct cookie_maker *
cookie_maker_alloc(const uint8_t key[COOKIE_INPUT_SIZE])289*03c3b87eSAaron LI cookie_maker_alloc(const uint8_t key[COOKIE_INPUT_SIZE])
290a6bca3d2SAaron LI {
291*03c3b87eSAaron LI 	struct cookie_maker *cm;
292*03c3b87eSAaron LI 
293*03c3b87eSAaron LI 	cm = kmalloc(sizeof(*cm), M_WG_COOKIE, M_WAITOK | M_ZERO);
294c3668ed7SAaron LI 	precompute_key(cm->cm_mac1_key, key, COOKIE_MAC1_KEY_LABEL,
295c3668ed7SAaron LI 		       sizeof(COOKIE_MAC1_KEY_LABEL) - 1);
296c3668ed7SAaron LI 	precompute_key(cm->cm_cookie_key, key, COOKIE_COOKIE_KEY_LABEL,
297c3668ed7SAaron LI 		       sizeof(COOKIE_COOKIE_KEY_LABEL) - 1);
2987ef217feSAaron LI 	lockinit(&cm->cm_lock, "cookie_maker", 0, 0);
299*03c3b87eSAaron LI 
300*03c3b87eSAaron LI 	return (cm);
301a6bca3d2SAaron LI }
302a6bca3d2SAaron LI 
303a6bca3d2SAaron LI void
cookie_maker_free(struct cookie_maker * cm)304a6bca3d2SAaron LI cookie_maker_free(struct cookie_maker *cm)
305a6bca3d2SAaron LI {
3067ef217feSAaron LI 	lockuninit(&cm->cm_lock);
307a6bca3d2SAaron LI 	explicit_bzero(cm, sizeof(*cm));
308*03c3b87eSAaron LI 	kfree(cm, M_WG_COOKIE);
309a6bca3d2SAaron LI }
310a6bca3d2SAaron LI 
311a6bca3d2SAaron LI int
cookie_maker_consume_payload(struct cookie_maker * cm,const uint8_t nonce[COOKIE_NONCE_SIZE],const uint8_t ecookie[COOKIE_ENCRYPTED_SIZE])312a6bca3d2SAaron LI cookie_maker_consume_payload(struct cookie_maker *cm,
3131ef0d803SAaron LI 			     const uint8_t nonce[COOKIE_NONCE_SIZE],
3141ef0d803SAaron LI 			     const uint8_t ecookie[COOKIE_ENCRYPTED_SIZE])
315a6bca3d2SAaron LI {
316a6bca3d2SAaron LI 	uint8_t cookie[COOKIE_COOKIE_SIZE];
3171ef0d803SAaron LI 	int ret = 0;
318a6bca3d2SAaron LI 
3197ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_SHARED);
3201ef0d803SAaron LI 
321a6bca3d2SAaron LI 	if (!cm->cm_mac1_sent) {
322a6bca3d2SAaron LI 		ret = ETIMEDOUT;
3231ef0d803SAaron LI 		goto out;
324a6bca3d2SAaron LI 	}
325a6bca3d2SAaron LI 
326a6bca3d2SAaron LI 	if (!xchacha20poly1305_decrypt(cookie, ecookie, COOKIE_ENCRYPTED_SIZE,
3271ef0d803SAaron LI 				       cm->cm_mac1_last, COOKIE_MAC_SIZE,
3281ef0d803SAaron LI 				       nonce, cm->cm_cookie_key)) {
329a6bca3d2SAaron LI 		ret = EINVAL;
3301ef0d803SAaron LI 		goto out;
331a6bca3d2SAaron LI 	}
332a6bca3d2SAaron LI 
3331ef0d803SAaron LI 	lockmgr(&cm->cm_lock, LK_RELEASE);
3347ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_EXCLUSIVE);
3351ef0d803SAaron LI 
336a6bca3d2SAaron LI 	memcpy(cm->cm_cookie, cookie, COOKIE_COOKIE_SIZE);
3372bed72b3SAaron LI 	getnanouptime(&cm->cm_cookie_birthdate);
338a6bca3d2SAaron LI 	cm->cm_cookie_valid = true;
339a6bca3d2SAaron LI 	cm->cm_mac1_sent = false;
340a6bca3d2SAaron LI 
3411ef0d803SAaron LI out:
3427ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_RELEASE);
3431ef0d803SAaron LI 	return (ret);
344a6bca3d2SAaron LI }
345a6bca3d2SAaron LI 
346a6bca3d2SAaron LI void
cookie_maker_mac(struct cookie_maker * cm,struct cookie_macs * macs,const void * buf,size_t len)3471ef0d803SAaron LI cookie_maker_mac(struct cookie_maker *cm, struct cookie_macs *macs,
3481ef0d803SAaron LI 		 const void *buf, size_t len)
349a6bca3d2SAaron LI {
3507ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_EXCLUSIVE);
3511ef0d803SAaron LI 
352a6bca3d2SAaron LI 	macs_mac1(macs, buf, len, cm->cm_mac1_key);
353a6bca3d2SAaron LI 	memcpy(cm->cm_mac1_last, macs->mac1, COOKIE_MAC_SIZE);
354a6bca3d2SAaron LI 	cm->cm_mac1_sent = true;
355a6bca3d2SAaron LI 
356a6bca3d2SAaron LI 	if (cm->cm_cookie_valid &&
3572bed72b3SAaron LI 	    !timer_expired(&cm->cm_cookie_birthdate,
358a6bca3d2SAaron LI 			   COOKIE_SECRET_MAX_AGE - COOKIE_SECRET_LATENCY, 0)) {
359a6bca3d2SAaron LI 		macs_mac2(macs, buf, len, cm->cm_cookie);
360a6bca3d2SAaron LI 	} else {
361a6bca3d2SAaron LI 		bzero(macs->mac2, COOKIE_MAC_SIZE);
362a6bca3d2SAaron LI 		cm->cm_cookie_valid = false;
363a6bca3d2SAaron LI 	}
3641ef0d803SAaron LI 
3657ef217feSAaron LI 	lockmgr(&cm->cm_lock, LK_RELEASE);
366a6bca3d2SAaron LI }
367a6bca3d2SAaron LI 
3681ef0d803SAaron LI /*----------------------------------------------------------------------------*/
369a6bca3d2SAaron LI /* Private functions */
3701ef0d803SAaron LI 
371a6bca3d2SAaron LI static void
precompute_key(uint8_t key[COOKIE_KEY_SIZE],const uint8_t input[COOKIE_INPUT_SIZE],const uint8_t * label,size_t label_len)3721ef0d803SAaron LI precompute_key(uint8_t key[COOKIE_KEY_SIZE],
373c3668ed7SAaron LI 	       const uint8_t input[COOKIE_INPUT_SIZE],
374c3668ed7SAaron LI 	       const uint8_t *label, size_t label_len)
375a6bca3d2SAaron LI {
376a6bca3d2SAaron LI 	struct blake2s_state blake;
3771ef0d803SAaron LI 
378a6bca3d2SAaron LI 	blake2s_init(&blake, COOKIE_KEY_SIZE);
379c3668ed7SAaron LI 	blake2s_update(&blake, label, label_len);
380a6bca3d2SAaron LI 	blake2s_update(&blake, input, COOKIE_INPUT_SIZE);
381a6bca3d2SAaron LI 	blake2s_final(&blake, key);
382a6bca3d2SAaron LI }
383a6bca3d2SAaron LI 
384a6bca3d2SAaron LI static void
macs_mac1(struct cookie_macs * macs,const void * buf,size_t len,const uint8_t key[COOKIE_KEY_SIZE])385a6bca3d2SAaron LI macs_mac1(struct cookie_macs *macs, const void *buf, size_t len,
386a6bca3d2SAaron LI 	  const uint8_t key[COOKIE_KEY_SIZE])
387a6bca3d2SAaron LI {
388a6bca3d2SAaron LI 	struct blake2s_state state;
3891ef0d803SAaron LI 
390a6bca3d2SAaron LI 	blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_KEY_SIZE);
391a6bca3d2SAaron LI 	blake2s_update(&state, buf, len);
392a6bca3d2SAaron LI 	blake2s_final(&state, macs->mac1);
393a6bca3d2SAaron LI }
394a6bca3d2SAaron LI 
395a6bca3d2SAaron LI static void
macs_mac2(struct cookie_macs * macs,const void * buf,size_t len,const uint8_t key[COOKIE_COOKIE_SIZE])396a6bca3d2SAaron LI macs_mac2(struct cookie_macs *macs, const void *buf, size_t len,
397a6bca3d2SAaron LI 	  const uint8_t key[COOKIE_COOKIE_SIZE])
398a6bca3d2SAaron LI {
399a6bca3d2SAaron LI 	struct blake2s_state state;
4001ef0d803SAaron LI 
401a6bca3d2SAaron LI 	blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_COOKIE_SIZE);
402a6bca3d2SAaron LI 	blake2s_update(&state, buf, len);
403a6bca3d2SAaron LI 	blake2s_update(&state, macs->mac1, COOKIE_MAC_SIZE);
404a6bca3d2SAaron LI 	blake2s_final(&state, macs->mac2);
405a6bca3d2SAaron LI }
406a6bca3d2SAaron LI 
407a6bca3d2SAaron LI static void
make_cookie(struct cookie_checker * cc,uint8_t cookie[COOKIE_COOKIE_SIZE],const struct sockaddr * sa)408a6bca3d2SAaron LI make_cookie(struct cookie_checker *cc, uint8_t cookie[COOKIE_COOKIE_SIZE],
4091ef0d803SAaron LI 	    const struct sockaddr *sa)
410a6bca3d2SAaron LI {
411a6bca3d2SAaron LI 	struct blake2s_state state;
412a6bca3d2SAaron LI 
413139109cdSAaron LI 	lockmgr(&cc->cc_secret_mtx, LK_EXCLUSIVE);
4142bed72b3SAaron LI 	if (timer_expired(&cc->cc_secret_birthdate,
415a6bca3d2SAaron LI 			  COOKIE_SECRET_MAX_AGE, 0)) {
416a9b5f3fdSAaron LI 		karc4random_buf(cc->cc_secret, COOKIE_SECRET_SIZE);
4172bed72b3SAaron LI 		getnanouptime(&cc->cc_secret_birthdate);
418a6bca3d2SAaron LI 	}
419a6bca3d2SAaron LI 	blake2s_init_key(&state, COOKIE_COOKIE_SIZE, cc->cc_secret,
420a6bca3d2SAaron LI 			 COOKIE_SECRET_SIZE);
421139109cdSAaron LI 	lockmgr(&cc->cc_secret_mtx, LK_RELEASE);
422a6bca3d2SAaron LI 
423a6bca3d2SAaron LI 	if (sa->sa_family == AF_INET) {
4241ef0d803SAaron LI 		const struct sockaddr_in *sin = (const void *)sa;
4251ef0d803SAaron LI 		blake2s_update(&state, (const uint8_t *)&sin->sin_addr,
4261ef0d803SAaron LI 			       sizeof(sin->sin_addr));
4271ef0d803SAaron LI 		blake2s_update(&state, (const uint8_t *)&sin->sin_port,
4281ef0d803SAaron LI 			       sizeof(sin->sin_port));
429a6bca3d2SAaron LI 		blake2s_final(&state, cookie);
430a6bca3d2SAaron LI #ifdef INET6
431a6bca3d2SAaron LI 	} else if (sa->sa_family == AF_INET6) {
4321ef0d803SAaron LI 		const struct sockaddr_in6 *sin6 = (const void *)sa;
4331ef0d803SAaron LI 		blake2s_update(&state, (const uint8_t *)&sin6->sin6_addr,
4341ef0d803SAaron LI 			       sizeof(sin6->sin6_addr));
4351ef0d803SAaron LI 		blake2s_update(&state, (const uint8_t *)&sin6->sin6_port,
4361ef0d803SAaron LI 			       sizeof(sin6->sin6_port));
437a6bca3d2SAaron LI 		blake2s_final(&state, cookie);
438a6bca3d2SAaron LI #endif
439a6bca3d2SAaron LI 	} else {
440a9b5f3fdSAaron LI 		karc4random_buf(cookie, COOKIE_COOKIE_SIZE);
441a6bca3d2SAaron LI 	}
442a6bca3d2SAaron LI }
443a6bca3d2SAaron LI 
4441ef0d803SAaron LI 
445a6bca3d2SAaron LI static void
ratelimit_init(struct ratelimit * rl)446a6bca3d2SAaron LI ratelimit_init(struct ratelimit *rl)
447a6bca3d2SAaron LI {
448a6bca3d2SAaron LI 	size_t i;
4491ef0d803SAaron LI 
4501ef0d803SAaron LI 	bzero(rl, sizeof(*rl));
451139109cdSAaron LI 	lockinit(&rl->rl_mtx, "ratelimit_lock", 0, 0);
452b1d93564SAaron LI 	callout_init_lk(&rl->rl_gc, &rl->rl_mtx);
453a9b5f3fdSAaron LI 	karc4random_buf(rl->rl_secret, sizeof(rl->rl_secret));
454a6bca3d2SAaron LI 	for (i = 0; i < RATELIMIT_SIZE; i++)
455a6bca3d2SAaron LI 		LIST_INIT(&rl->rl_table[i]);
456a6bca3d2SAaron LI 	rl->rl_table_num = 0;
4571ef0d803SAaron LI 
458a6bca3d2SAaron LI 	rl->rl_initialized = true;
459a6bca3d2SAaron LI }
460a6bca3d2SAaron LI 
461a6bca3d2SAaron LI static void
ratelimit_deinit(struct ratelimit * rl)462a6bca3d2SAaron LI ratelimit_deinit(struct ratelimit *rl)
463a6bca3d2SAaron LI {
464a6bca3d2SAaron LI 	if (!rl->rl_initialized)
465a6bca3d2SAaron LI 		return;
4661ef0d803SAaron LI 
467139109cdSAaron LI 	lockmgr(&rl->rl_mtx, LK_EXCLUSIVE);
468a6bca3d2SAaron LI 	callout_stop(&rl->rl_gc);
469a6bca3d2SAaron LI 	ratelimit_gc(rl, true);
470139109cdSAaron LI 	lockmgr(&rl->rl_mtx, LK_RELEASE);
471139109cdSAaron LI 	lockuninit(&rl->rl_mtx);
472a6bca3d2SAaron LI 
473a6bca3d2SAaron LI 	rl->rl_initialized = false;
474a6bca3d2SAaron LI }
475a6bca3d2SAaron LI 
476a6bca3d2SAaron LI static void
ratelimit_gc_callout(void * _rl)477a6bca3d2SAaron LI ratelimit_gc_callout(void *_rl)
478a6bca3d2SAaron LI {
4791ef0d803SAaron LI 	/* callout will lock for us */
480a6bca3d2SAaron LI 	ratelimit_gc(_rl, false);
481a6bca3d2SAaron LI }
482a6bca3d2SAaron LI 
483a6bca3d2SAaron LI static void
ratelimit_gc_schedule(struct ratelimit * rl)484a6bca3d2SAaron LI ratelimit_gc_schedule(struct ratelimit *rl)
485a6bca3d2SAaron LI {
4861ef0d803SAaron LI 	/*
4871ef0d803SAaron LI 	 * Trigger another GC if needed.  There is no point calling GC if
4881ef0d803SAaron LI 	 * there are no entries in the table.  We also want to ensure that
4891ef0d803SAaron LI 	 * GC occurs on a regular interval, so don't override a currently
4901ef0d803SAaron LI 	 * pending GC.
491a6bca3d2SAaron LI 	 *
4921ef0d803SAaron LI 	 * In the case of a forced ratelimit_gc(), there will be no entries
4931ef0d803SAaron LI 	 * left so we will not schedule another GC.
4941ef0d803SAaron LI 	 */
495a6bca3d2SAaron LI 	if (rl->rl_table_num > 0 && !callout_pending(&rl->rl_gc))
496a6bca3d2SAaron LI 		callout_reset(&rl->rl_gc, ELEMENT_TIMEOUT * hz,
497a6bca3d2SAaron LI 			      ratelimit_gc_callout, rl);
498a6bca3d2SAaron LI }
499a6bca3d2SAaron LI 
500a6bca3d2SAaron LI static void
ratelimit_gc(struct ratelimit * rl,bool force)501a6bca3d2SAaron LI ratelimit_gc(struct ratelimit *rl, bool force)
502a6bca3d2SAaron LI {
503a6bca3d2SAaron LI 	struct ratelimit_entry *r, *tr;
5042bed72b3SAaron LI 	struct timespec expiry;
5051ef0d803SAaron LI 	size_t i;
506a6bca3d2SAaron LI 
507139109cdSAaron LI 	KKASSERT(lockstatus(&rl->rl_mtx, curthread) == LK_EXCLUSIVE);
508a6bca3d2SAaron LI 
509a6bca3d2SAaron LI 	if (rl->rl_table_num == 0)
510a6bca3d2SAaron LI 		return;
511a6bca3d2SAaron LI 
5122bed72b3SAaron LI 	getnanouptime(&expiry);
5132bed72b3SAaron LI 	expiry.tv_sec -= ELEMENT_TIMEOUT;
514a6bca3d2SAaron LI 
515a6bca3d2SAaron LI 	for (i = 0; i < RATELIMIT_SIZE; i++) {
5161fdfe5a9SAaron LI 		LIST_FOREACH_MUTABLE(r, &rl->rl_table[i], r_entry, tr) {
5172bed72b3SAaron LI 			if (force ||
5182bed72b3SAaron LI 			    timespeccmp(&r->r_last_time, &expiry, <)) {
519a6bca3d2SAaron LI 				rl->rl_table_num--;
520a6bca3d2SAaron LI 				LIST_REMOVE(r, r_entry);
521be332237SAaron LI 				objcache_put(ratelimit_zone, r);
522a6bca3d2SAaron LI 			}
523a6bca3d2SAaron LI 		}
524a6bca3d2SAaron LI 	}
525a6bca3d2SAaron LI 
526a6bca3d2SAaron LI 	ratelimit_gc_schedule(rl);
527a6bca3d2SAaron LI }
528a6bca3d2SAaron LI 
529a6bca3d2SAaron LI static int
ratelimit_allow(struct ratelimit * rl,const struct sockaddr * sa)5301ef0d803SAaron LI ratelimit_allow(struct ratelimit *rl, const struct sockaddr *sa)
531a6bca3d2SAaron LI {
5322bed72b3SAaron LI 	struct timespec diff;
533a6bca3d2SAaron LI 	struct ratelimit_entry *r;
5343b7831f6SAaron LI 	struct ratelimit_key key = { 0 };
5351ef0d803SAaron LI 	uint64_t bucket, tokens;
5361ef0d803SAaron LI 	size_t len;
5371ef0d803SAaron LI 	int ret = ECONNREFUSED;
538a6bca3d2SAaron LI 
539a6bca3d2SAaron LI 	if (sa->sa_family == AF_INET) {
5401ef0d803SAaron LI 		len = IPV4_MASK_SIZE;
5411ef0d803SAaron LI 		memcpy(key.ip, &((const struct sockaddr_in *)sa)->sin_addr,
5421ef0d803SAaron LI 		       len);
543a6bca3d2SAaron LI 	}
544a6bca3d2SAaron LI #ifdef INET6
5451ef0d803SAaron LI 	else if (sa->sa_family == AF_INET6) {
5461ef0d803SAaron LI 		len = IPV6_MASK_SIZE;
5471ef0d803SAaron LI 		memcpy(key.ip, &((const struct sockaddr_in6 *)sa)->sin6_addr,
5481ef0d803SAaron LI 		       len);
5491ef0d803SAaron LI 	}
550a6bca3d2SAaron LI #endif
5511ef0d803SAaron LI 	else {
5521ef0d803SAaron LI 		return (ret);
5531ef0d803SAaron LI 	}
554a6bca3d2SAaron LI 
555a6bca3d2SAaron LI 	bucket = siphash13(rl->rl_secret, &key, len) & RATELIMIT_MASK;
556139109cdSAaron LI 	lockmgr(&rl->rl_mtx, LK_EXCLUSIVE);
557a6bca3d2SAaron LI 
558a6bca3d2SAaron LI 	LIST_FOREACH(r, &rl->rl_table[bucket], r_entry) {
5591ef0d803SAaron LI 		if (memcmp(&r->r_key, &key, len) != 0)
560a6bca3d2SAaron LI 			continue;
561a6bca3d2SAaron LI 
5621ef0d803SAaron LI 		/*
5631ef0d803SAaron LI 		 * Found an entry for the endpoint.  We apply standard token
5641ef0d803SAaron LI 		 * bucket, by calculating the time lapsed since last_time,
5651ef0d803SAaron LI 		 * adding that, ensuring that we cap the tokens at TOKEN_MAX.
5661ef0d803SAaron LI 		 * If the endpoint has no tokens left (i.e., tokens <
5671ef0d803SAaron LI 		 * INITIATION_COST) then we block the request.  Otherwise, we
5681ef0d803SAaron LI 		 * subtract the INITITIATION_COST and return OK.
5691ef0d803SAaron LI 		 */
5702bed72b3SAaron LI 		diff = r->r_last_time;
5712bed72b3SAaron LI 		getnanouptime(&r->r_last_time);
5722bed72b3SAaron LI 		timespecsub(&r->r_last_time, &diff, &diff);
573a6bca3d2SAaron LI 
5742bed72b3SAaron LI 		tokens = r->r_tokens;
5752bed72b3SAaron LI 		tokens += diff.tv_sec * NSEC_PER_SEC + diff.tv_nsec;
576a6bca3d2SAaron LI 		if (tokens > TOKEN_MAX)
577a6bca3d2SAaron LI 			tokens = TOKEN_MAX;
578a6bca3d2SAaron LI 
579a6bca3d2SAaron LI 		if (tokens >= INITIATION_COST) {
580a6bca3d2SAaron LI 			r->r_tokens = tokens - INITIATION_COST;
581a6bca3d2SAaron LI 			goto ok;
582a6bca3d2SAaron LI 		} else {
583a6bca3d2SAaron LI 			r->r_tokens = tokens;
584a6bca3d2SAaron LI 			goto error;
585a6bca3d2SAaron LI 		}
586a6bca3d2SAaron LI 	}
587a6bca3d2SAaron LI 
5881ef0d803SAaron LI 	/*
5891ef0d803SAaron LI 	 * Didn't have an entry for the endpoint, so let's add one if we
5901ef0d803SAaron LI 	 * have space.
5911ef0d803SAaron LI 	 */
592a6bca3d2SAaron LI 	if (rl->rl_table_num >= RATELIMIT_SIZE_MAX)
593a6bca3d2SAaron LI 		goto error;
594a6bca3d2SAaron LI 
595be332237SAaron LI 	if ((r = objcache_get(ratelimit_zone, M_NOWAIT)) == NULL)
596a6bca3d2SAaron LI 		goto error;
597be332237SAaron LI 	bzero(r, sizeof(*r)); /* objcache_get() doesn't ensure M_ZERO. */
598a6bca3d2SAaron LI 
599a6bca3d2SAaron LI 	rl->rl_table_num++;
600a6bca3d2SAaron LI 
6011ef0d803SAaron LI 	/* Insert the new entry and initialize it. */
602a6bca3d2SAaron LI 	LIST_INSERT_HEAD(&rl->rl_table[bucket], r, r_entry);
603a6bca3d2SAaron LI 	r->r_key = key;
604a6bca3d2SAaron LI 	r->r_tokens = TOKEN_MAX - INITIATION_COST;
6052bed72b3SAaron LI 	getnanouptime(&r->r_last_time);
606a6bca3d2SAaron LI 
6071ef0d803SAaron LI 	/* We've added a new entry; let's trigger GC. */
608a6bca3d2SAaron LI 	ratelimit_gc_schedule(rl);
6091ef0d803SAaron LI 
610a6bca3d2SAaron LI ok:
611a6bca3d2SAaron LI 	ret = 0;
612a6bca3d2SAaron LI error:
613139109cdSAaron LI 	lockmgr(&rl->rl_mtx, LK_RELEASE);
6141ef0d803SAaron LI 	return (ret);
615a6bca3d2SAaron LI }
616a6bca3d2SAaron LI 
617a6bca3d2SAaron LI 
618e6c44b2eSAaron LI #ifdef WG_SELFTESTS
619a6bca3d2SAaron LI #include "selftest/cookie.c"
620e6c44b2eSAaron LI #endif /* WG_SELFTESTS */
621