The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/wg/wg_cookie.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /* SPDX-License-Identifier: ISC
    2  *
    3  * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
    4  * Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
    5  */
    6 
    7 #include "opt_inet.h"
    8 #include "opt_inet6.h"
    9 
   10 #include <sys/param.h>
   11 #include <sys/systm.h>
   12 #include <sys/kernel.h>
   13 #include <sys/lock.h>
   14 #include <sys/mutex.h>
   15 #include <sys/rwlock.h>
   16 #include <sys/socket.h>
   17 #include <crypto/siphash/siphash.h>
   18 #include <netinet/in.h>
   19 #include <vm/uma.h>
   20 
   21 #include "wg_cookie.h"
   22 
   23 #define COOKIE_MAC1_KEY_LABEL   "mac1----"
   24 #define COOKIE_COOKIE_KEY_LABEL "cookie--"
   25 #define COOKIE_SECRET_MAX_AGE   120
   26 #define COOKIE_SECRET_LATENCY   5
   27 
   28 /* Constants for initiation rate limiting */
   29 #define RATELIMIT_SIZE          (1 << 13)
   30 #define RATELIMIT_MASK          (RATELIMIT_SIZE - 1)
   31 #define RATELIMIT_SIZE_MAX      (RATELIMIT_SIZE * 8)
   32 #define INITIATIONS_PER_SECOND  20
   33 #define INITIATIONS_BURSTABLE   5
   34 #define INITIATION_COST         (SBT_1S / INITIATIONS_PER_SECOND)
   35 #define TOKEN_MAX               (INITIATION_COST * INITIATIONS_BURSTABLE)
   36 #define ELEMENT_TIMEOUT         1
   37 #define IPV4_MASK_SIZE          4 /* Use all 4 bytes of IPv4 address */
   38 #define IPV6_MASK_SIZE          8 /* Use top 8 bytes (/64) of IPv6 address */
   39 
   40 struct ratelimit_key {
   41         struct vnet *vnet;
   42         uint8_t ip[IPV6_MASK_SIZE];
   43 };
   44 
   45 struct ratelimit_entry {
   46         LIST_ENTRY(ratelimit_entry)     r_entry;
   47         struct ratelimit_key            r_key;
   48         sbintime_t                      r_last_time;    /* sbinuptime */
   49         uint64_t                        r_tokens;
   50 };
   51 
   52 struct ratelimit {
   53         uint8_t                         rl_secret[SIPHASH_KEY_LENGTH];
   54         struct mtx                      rl_mtx;
   55         struct callout                  rl_gc;
   56         LIST_HEAD(, ratelimit_entry)    rl_table[RATELIMIT_SIZE];
   57         size_t                          rl_table_num;
   58 };
   59 
   60 static void     precompute_key(uint8_t *,
   61                         const uint8_t[COOKIE_INPUT_SIZE], const char *);
   62 static void     macs_mac1(struct cookie_macs *, const void *, size_t,
   63                         const uint8_t[COOKIE_KEY_SIZE]);
   64 static void     macs_mac2(struct cookie_macs *, const void *, size_t,
   65                         const uint8_t[COOKIE_COOKIE_SIZE]);
   66 static int      timer_expired(sbintime_t, uint32_t, uint32_t);
   67 static void     make_cookie(struct cookie_checker *,
   68                         uint8_t[COOKIE_COOKIE_SIZE], struct sockaddr *);
   69 static void     ratelimit_init(struct ratelimit *);
   70 static void     ratelimit_deinit(struct ratelimit *);
   71 static void     ratelimit_gc_callout(void *);
   72 static void     ratelimit_gc_schedule(struct ratelimit *);
   73 static void     ratelimit_gc(struct ratelimit *, bool);
   74 static int      ratelimit_allow(struct ratelimit *, struct sockaddr *, struct vnet *);
   75 static uint64_t siphash13(const uint8_t [SIPHASH_KEY_LENGTH], const void *, size_t);
   76 
   77 static struct ratelimit ratelimit_v4;
   78 #ifdef INET6
   79 static struct ratelimit ratelimit_v6;
   80 #endif
   81 static uma_zone_t ratelimit_zone;
   82 
   83 /* Public Functions */
   84 int
   85 cookie_init(void)
   86 {
   87         if ((ratelimit_zone = uma_zcreate("wg ratelimit",
   88             sizeof(struct ratelimit_entry), NULL, NULL, NULL, NULL, 0, 0)) == NULL)
   89                 return ENOMEM;
   90 
   91         ratelimit_init(&ratelimit_v4);
   92 #ifdef INET6
   93         ratelimit_init(&ratelimit_v6);
   94 #endif
   95         return (0);
   96 }
   97 
   98 void
   99 cookie_deinit(void)
  100 {
  101         ratelimit_deinit(&ratelimit_v4);
  102 #ifdef INET6
  103         ratelimit_deinit(&ratelimit_v6);
  104 #endif
  105         uma_zdestroy(ratelimit_zone);
  106 }
  107 
  108 void
  109 cookie_checker_init(struct cookie_checker *cc)
  110 {
  111         bzero(cc, sizeof(*cc));
  112 
  113         rw_init(&cc->cc_key_lock, "cookie_checker_key");
  114         mtx_init(&cc->cc_secret_mtx, "cookie_checker_secret", NULL, MTX_DEF);
  115 }
  116 
  117 void
  118 cookie_checker_free(struct cookie_checker *cc)
  119 {
  120         rw_destroy(&cc->cc_key_lock);
  121         mtx_destroy(&cc->cc_secret_mtx);
  122         explicit_bzero(cc, sizeof(*cc));
  123 }
  124 
  125 void
  126 cookie_checker_update(struct cookie_checker *cc,
  127     const uint8_t key[COOKIE_INPUT_SIZE])
  128 {
  129         rw_wlock(&cc->cc_key_lock);
  130         if (key) {
  131                 precompute_key(cc->cc_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
  132                 precompute_key(cc->cc_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
  133         } else {
  134                 bzero(cc->cc_mac1_key, sizeof(cc->cc_mac1_key));
  135                 bzero(cc->cc_cookie_key, sizeof(cc->cc_cookie_key));
  136         }
  137         rw_wunlock(&cc->cc_key_lock);
  138 }
  139 
  140 void
  141 cookie_checker_create_payload(struct cookie_checker *cc,
  142     struct cookie_macs *macs, uint8_t nonce[COOKIE_NONCE_SIZE],
  143     uint8_t ecookie[COOKIE_ENCRYPTED_SIZE], struct sockaddr *sa)
  144 {
  145         uint8_t cookie[COOKIE_COOKIE_SIZE];
  146 
  147         make_cookie(cc, cookie, sa);
  148         arc4random_buf(nonce, COOKIE_NONCE_SIZE);
  149 
  150         rw_rlock(&cc->cc_key_lock);
  151         xchacha20poly1305_encrypt(ecookie, cookie, COOKIE_COOKIE_SIZE,
  152             macs->mac1, COOKIE_MAC_SIZE, nonce, cc->cc_cookie_key);
  153         rw_runlock(&cc->cc_key_lock);
  154 
  155         explicit_bzero(cookie, sizeof(cookie));
  156 }
  157 
  158 void
  159 cookie_maker_init(struct cookie_maker *cm, const uint8_t key[COOKIE_INPUT_SIZE])
  160 {
  161         bzero(cm, sizeof(*cm));
  162         precompute_key(cm->cm_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
  163         precompute_key(cm->cm_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
  164         rw_init(&cm->cm_lock, "cookie_maker");
  165 }
  166 
  167 void
  168 cookie_maker_free(struct cookie_maker *cm)
  169 {
  170         rw_destroy(&cm->cm_lock);
  171         explicit_bzero(cm, sizeof(*cm));
  172 }
  173 
  174 int
  175 cookie_maker_consume_payload(struct cookie_maker *cm,
  176     uint8_t nonce[COOKIE_NONCE_SIZE], uint8_t ecookie[COOKIE_ENCRYPTED_SIZE])
  177 {
  178         uint8_t cookie[COOKIE_COOKIE_SIZE];
  179         int ret;
  180 
  181         rw_rlock(&cm->cm_lock);
  182         if (!cm->cm_mac1_sent) {
  183                 ret = ETIMEDOUT;
  184                 goto error;
  185         }
  186 
  187         if (!xchacha20poly1305_decrypt(cookie, ecookie, COOKIE_ENCRYPTED_SIZE,
  188             cm->cm_mac1_last, COOKIE_MAC_SIZE, nonce, cm->cm_cookie_key)) {
  189                 ret = EINVAL;
  190                 goto error;
  191         }
  192         rw_runlock(&cm->cm_lock);
  193 
  194         rw_wlock(&cm->cm_lock);
  195         memcpy(cm->cm_cookie, cookie, COOKIE_COOKIE_SIZE);
  196         cm->cm_cookie_birthdate = getsbinuptime();
  197         cm->cm_cookie_valid = true;
  198         cm->cm_mac1_sent = false;
  199         rw_wunlock(&cm->cm_lock);
  200 
  201         return 0;
  202 error:
  203         rw_runlock(&cm->cm_lock);
  204         return ret;
  205 }
  206 
  207 void
  208 cookie_maker_mac(struct cookie_maker *cm, struct cookie_macs *macs, void *buf,
  209     size_t len)
  210 {
  211         rw_wlock(&cm->cm_lock);
  212         macs_mac1(macs, buf, len, cm->cm_mac1_key);
  213         memcpy(cm->cm_mac1_last, macs->mac1, COOKIE_MAC_SIZE);
  214         cm->cm_mac1_sent = true;
  215 
  216         if (cm->cm_cookie_valid &&
  217             !timer_expired(cm->cm_cookie_birthdate,
  218             COOKIE_SECRET_MAX_AGE - COOKIE_SECRET_LATENCY, 0)) {
  219                 macs_mac2(macs, buf, len, cm->cm_cookie);
  220         } else {
  221                 bzero(macs->mac2, COOKIE_MAC_SIZE);
  222                 cm->cm_cookie_valid = false;
  223         }
  224         rw_wunlock(&cm->cm_lock);
  225 }
  226 
  227 int
  228 cookie_checker_validate_macs(struct cookie_checker *cc, struct cookie_macs *macs,
  229     void *buf, size_t len, bool check_cookie, struct sockaddr *sa, struct vnet *vnet)
  230 {
  231         struct cookie_macs our_macs;
  232         uint8_t cookie[COOKIE_COOKIE_SIZE];
  233 
  234         /* Validate incoming MACs */
  235         rw_rlock(&cc->cc_key_lock);
  236         macs_mac1(&our_macs, buf, len, cc->cc_mac1_key);
  237         rw_runlock(&cc->cc_key_lock);
  238 
  239         /* If mac1 is invald, we want to drop the packet */
  240         if (timingsafe_bcmp(our_macs.mac1, macs->mac1, COOKIE_MAC_SIZE) != 0)
  241                 return EINVAL;
  242 
  243         if (check_cookie) {
  244                 make_cookie(cc, cookie, sa);
  245                 macs_mac2(&our_macs, buf, len, cookie);
  246 
  247                 /* If the mac2 is invalid, we want to send a cookie response */
  248                 if (timingsafe_bcmp(our_macs.mac2, macs->mac2, COOKIE_MAC_SIZE) != 0)
  249                         return EAGAIN;
  250 
  251                 /* If the mac2 is valid, we may want rate limit the peer.
  252                  * ratelimit_allow will return either 0 or ECONNREFUSED,
  253                  * implying there is no ratelimiting, or we should ratelimit
  254                  * (refuse) respectively. */
  255                 if (sa->sa_family == AF_INET)
  256                         return ratelimit_allow(&ratelimit_v4, sa, vnet);
  257 #ifdef INET6
  258                 else if (sa->sa_family == AF_INET6)
  259                         return ratelimit_allow(&ratelimit_v6, sa, vnet);
  260 #endif
  261                 else
  262                         return EAFNOSUPPORT;
  263         }
  264 
  265         return 0;
  266 }
  267 
  268 /* Private functions */
  269 static void
  270 precompute_key(uint8_t *key, const uint8_t input[COOKIE_INPUT_SIZE],
  271     const char *label)
  272 {
  273         struct blake2s_state blake;
  274         blake2s_init(&blake, COOKIE_KEY_SIZE);
  275         blake2s_update(&blake, label, strlen(label));
  276         blake2s_update(&blake, input, COOKIE_INPUT_SIZE);
  277         blake2s_final(&blake, key);
  278 }
  279 
  280 static void
  281 macs_mac1(struct cookie_macs *macs, const void *buf, size_t len,
  282     const uint8_t key[COOKIE_KEY_SIZE])
  283 {
  284         struct blake2s_state state;
  285         blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_KEY_SIZE);
  286         blake2s_update(&state, buf, len);
  287         blake2s_final(&state, macs->mac1);
  288 }
  289 
  290 static void
  291 macs_mac2(struct cookie_macs *macs, const void *buf, size_t len,
  292     const uint8_t key[COOKIE_COOKIE_SIZE])
  293 {
  294         struct blake2s_state state;
  295         blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_COOKIE_SIZE);
  296         blake2s_update(&state, buf, len);
  297         blake2s_update(&state, macs->mac1, COOKIE_MAC_SIZE);
  298         blake2s_final(&state, macs->mac2);
  299 }
  300 
  301 static __inline int
  302 timer_expired(sbintime_t timer, uint32_t sec, uint32_t nsec)
  303 {
  304         sbintime_t now = getsbinuptime();
  305         return (now > (timer + sec * SBT_1S + nstosbt(nsec))) ? ETIMEDOUT : 0;
  306 }
  307 
  308 static void
  309 make_cookie(struct cookie_checker *cc, uint8_t cookie[COOKIE_COOKIE_SIZE],
  310     struct sockaddr *sa)
  311 {
  312         struct blake2s_state state;
  313 
  314         mtx_lock(&cc->cc_secret_mtx);
  315         if (timer_expired(cc->cc_secret_birthdate,
  316             COOKIE_SECRET_MAX_AGE, 0)) {
  317                 arc4random_buf(cc->cc_secret, COOKIE_SECRET_SIZE);
  318                 cc->cc_secret_birthdate = getsbinuptime();
  319         }
  320         blake2s_init_key(&state, COOKIE_COOKIE_SIZE, cc->cc_secret,
  321             COOKIE_SECRET_SIZE);
  322         mtx_unlock(&cc->cc_secret_mtx);
  323 
  324         if (sa->sa_family == AF_INET) {
  325                 blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_addr,
  326                                 sizeof(struct in_addr));
  327                 blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_port,
  328                                 sizeof(in_port_t));
  329                 blake2s_final(&state, cookie);
  330 #ifdef INET6
  331         } else if (sa->sa_family == AF_INET6) {
  332                 blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_addr,
  333                                 sizeof(struct in6_addr));
  334                 blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_port,
  335                                 sizeof(in_port_t));
  336                 blake2s_final(&state, cookie);
  337 #endif
  338         } else {
  339                 arc4random_buf(cookie, COOKIE_COOKIE_SIZE);
  340         }
  341 }
  342 
  343 static void
  344 ratelimit_init(struct ratelimit *rl)
  345 {
  346         size_t i;
  347         mtx_init(&rl->rl_mtx, "ratelimit_lock", NULL, MTX_DEF);
  348         callout_init_mtx(&rl->rl_gc, &rl->rl_mtx, 0);
  349         arc4random_buf(rl->rl_secret, sizeof(rl->rl_secret));
  350         for (i = 0; i < RATELIMIT_SIZE; i++)
  351                 LIST_INIT(&rl->rl_table[i]);
  352         rl->rl_table_num = 0;
  353 }
  354 
  355 static void
  356 ratelimit_deinit(struct ratelimit *rl)
  357 {
  358         mtx_lock(&rl->rl_mtx);
  359         callout_stop(&rl->rl_gc);
  360         ratelimit_gc(rl, true);
  361         mtx_unlock(&rl->rl_mtx);
  362         mtx_destroy(&rl->rl_mtx);
  363 }
  364 
  365 static void
  366 ratelimit_gc_callout(void *_rl)
  367 {
  368         /* callout will lock rl_mtx for us */
  369         ratelimit_gc(_rl, false);
  370 }
  371 
  372 static void
  373 ratelimit_gc_schedule(struct ratelimit *rl)
  374 {
  375         /* Trigger another GC if needed. There is no point calling GC if there
  376          * are no entries in the table. We also want to ensure that GC occurs
  377          * on a regular interval, so don't override a currently pending GC.
  378          *
  379          * In the case of a forced ratelimit_gc, there will be no entries left
  380          * so we will will not schedule another GC. */
  381         if (rl->rl_table_num > 0 && !callout_pending(&rl->rl_gc))
  382                 callout_reset(&rl->rl_gc, ELEMENT_TIMEOUT * hz,
  383                     ratelimit_gc_callout, rl);
  384 }
  385 
  386 static void
  387 ratelimit_gc(struct ratelimit *rl, bool force)
  388 {
  389         size_t i;
  390         struct ratelimit_entry *r, *tr;
  391         sbintime_t expiry;
  392 
  393         mtx_assert(&rl->rl_mtx, MA_OWNED);
  394 
  395         if (rl->rl_table_num == 0)
  396                 return;
  397 
  398         expiry = getsbinuptime() - ELEMENT_TIMEOUT * SBT_1S;
  399 
  400         for (i = 0; i < RATELIMIT_SIZE; i++) {
  401                 LIST_FOREACH_SAFE(r, &rl->rl_table[i], r_entry, tr) {
  402                         if (r->r_last_time < expiry || force) {
  403                                 rl->rl_table_num--;
  404                                 LIST_REMOVE(r, r_entry);
  405                                 uma_zfree(ratelimit_zone, r);
  406                         }
  407                 }
  408         }
  409 
  410         ratelimit_gc_schedule(rl);
  411 }
  412 
  413 static int
  414 ratelimit_allow(struct ratelimit *rl, struct sockaddr *sa, struct vnet *vnet)
  415 {
  416         uint64_t bucket, tokens;
  417         sbintime_t diff, now;
  418         struct ratelimit_entry *r;
  419         int ret = ECONNREFUSED;
  420         struct ratelimit_key key = { .vnet = vnet };
  421         size_t len = sizeof(key);
  422 
  423         if (sa->sa_family == AF_INET) {
  424                 memcpy(key.ip, &satosin(sa)->sin_addr, IPV4_MASK_SIZE);
  425                 len -= IPV6_MASK_SIZE - IPV4_MASK_SIZE;
  426         }
  427 #ifdef INET6
  428         else if (sa->sa_family == AF_INET6)
  429                 memcpy(key.ip, &satosin6(sa)->sin6_addr, IPV6_MASK_SIZE);
  430 #endif
  431         else
  432                 return ret;
  433 
  434         bucket = siphash13(rl->rl_secret, &key, len) & RATELIMIT_MASK;
  435         mtx_lock(&rl->rl_mtx);
  436 
  437         LIST_FOREACH(r, &rl->rl_table[bucket], r_entry) {
  438                 if (bcmp(&r->r_key, &key, len) != 0)
  439                         continue;
  440 
  441                 /* If we get to here, we've found an entry for the endpoint.
  442                  * We apply standard token bucket, by calculating the time
  443                  * lapsed since our last_time, adding that, ensuring that we
  444                  * cap the tokens at TOKEN_MAX. If the endpoint has no tokens
  445                  * left (that is tokens <= INITIATION_COST) then we block the
  446                  * request, otherwise we subtract the INITITIATION_COST and
  447                  * return OK. */
  448                 now = getsbinuptime();
  449                 diff = now - r->r_last_time;
  450                 r->r_last_time = now;
  451 
  452                 tokens = r->r_tokens + diff;
  453 
  454                 if (tokens > TOKEN_MAX)
  455                         tokens = TOKEN_MAX;
  456 
  457                 if (tokens >= INITIATION_COST) {
  458                         r->r_tokens = tokens - INITIATION_COST;
  459                         goto ok;
  460                 } else {
  461                         r->r_tokens = tokens;
  462                         goto error;
  463                 }
  464         }
  465 
  466         /* If we get to here, we didn't have an entry for the endpoint, let's
  467          * add one if we have space. */
  468         if (rl->rl_table_num >= RATELIMIT_SIZE_MAX)
  469                 goto error;
  470 
  471         /* Goto error if out of memory */
  472         if ((r = uma_zalloc(ratelimit_zone, M_NOWAIT | M_ZERO)) == NULL)
  473                 goto error;
  474 
  475         rl->rl_table_num++;
  476 
  477         /* Insert entry into the hashtable and ensure it's initialised */
  478         LIST_INSERT_HEAD(&rl->rl_table[bucket], r, r_entry);
  479         r->r_key = key;
  480         r->r_last_time = getsbinuptime();
  481         r->r_tokens = TOKEN_MAX - INITIATION_COST;
  482 
  483         /* If we've added a new entry, let's trigger GC. */
  484         ratelimit_gc_schedule(rl);
  485 ok:
  486         ret = 0;
  487 error:
  488         mtx_unlock(&rl->rl_mtx);
  489         return ret;
  490 }
  491 
  492 static uint64_t siphash13(const uint8_t key[SIPHASH_KEY_LENGTH], const void *src, size_t len)
  493 {
  494         SIPHASH_CTX ctx;
  495         return (SipHashX(&ctx, 1, 3, key, src, len));
  496 }
  497 
  498 #ifdef SELFTESTS
  499 #include "selftest/cookie.c"
  500 #endif /* SELFTESTS */

Cache object: 064449cdbcf3064a84e5db3c97e39745


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.