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