mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-07 00:50:50 +00:00
Import the WireGuard driver from zx2c4.com.
This commit brings back the driver from FreeBSD commit
f187d6dfbf
plus subsequent fixes from
upstream.
Relative to upstream this commit includes a few other small fixes such
as additional INET and INET6 #ifdef's, #include cleanups, and updates
for recent API changes in main.
Reviewed by: pauamma, gbe, kevans, emaste
Obtained from: git@git.zx2c4.com:wireguard-freebsd @ 3cc22b2
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D36909
This commit is contained in:
parent
9e0aaedd70
commit
744bfb2131
|
@ -136,6 +136,8 @@
|
|||
..
|
||||
vkbd
|
||||
..
|
||||
wg
|
||||
..
|
||||
wi
|
||||
..
|
||||
..
|
||||
|
|
|
@ -49,7 +49,7 @@ LSUBDIRS= dev/acpica dev/agp dev/ciss dev/filemon dev/firewire \
|
|||
dev/hwpmc dev/hyperv \
|
||||
dev/ic dev/iicbus dev/io dev/mfi dev/mmc dev/nvme \
|
||||
dev/ofw dev/pbio dev/pci ${_dev_powermac_nvram} dev/ppbus dev/pwm \
|
||||
dev/smbus dev/speaker dev/tcp_log dev/veriexec dev/vkbd \
|
||||
dev/smbus dev/speaker dev/tcp_log dev/veriexec dev/vkbd dev/wg \
|
||||
fs/devfs fs/fdescfs fs/msdosfs fs/nfs fs/nullfs \
|
||||
fs/procfs fs/smbfs fs/udf fs/unionfs \
|
||||
geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \
|
||||
|
@ -225,6 +225,10 @@ NVPAIRDIR= ${INCLUDEDIR}/sys
|
|||
MLX5= mlx5io.h
|
||||
MLX5DIR= ${INCLUDEDIR}/dev/mlx5
|
||||
|
||||
.PATH: ${SRCTOP}/sys/dev/wg
|
||||
WG= if_wg.h
|
||||
WGDIR= ${INCLUDEDIR}/dev/wg
|
||||
|
||||
INCSGROUPS= INCS \
|
||||
ACPICA \
|
||||
AGP \
|
||||
|
@ -244,7 +248,8 @@ INCSGROUPS= INCS \
|
|||
RPC \
|
||||
SECAUDIT \
|
||||
TEKEN \
|
||||
VERIEXEC
|
||||
VERIEXEC \
|
||||
WG
|
||||
|
||||
.if ${MK_IPFILTER} != "no"
|
||||
INCSGROUPS+= IPFILTER
|
||||
|
|
|
@ -584,6 +584,7 @@ MAN= aac.4 \
|
|||
vtnet.4 \
|
||||
watchdog.4 \
|
||||
${_wbwd.4} \
|
||||
wg.4 \
|
||||
witness.4 \
|
||||
wlan.4 \
|
||||
wlan_acl.4 \
|
||||
|
@ -761,6 +762,7 @@ MLINKS+=vr.4 if_vr.4
|
|||
MLINKS+=vte.4 if_vte.4
|
||||
MLINKS+=vtnet.4 if_vtnet.4
|
||||
MLINKS+=watchdog.4 SW_WATCHDOG.4
|
||||
MLINKS+=wg.4 if_wg.4
|
||||
MLINKS+=${_wpi.4} ${_if_wpi.4}
|
||||
MLINKS+=xl.4 if_xl.4
|
||||
|
||||
|
|
213
share/man/man4/wg.4
Normal file
213
share/man/man4/wg.4
Normal file
|
@ -0,0 +1,213 @@
|
|||
.\" Copyright (c) 2020 Gordon Bergling <gbe@FreeBSD.org>
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd October 28, 2022
|
||||
.Dt WG 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm wg
|
||||
.Nd "WireGuard - pseudo-device"
|
||||
.Sh SYNOPSIS
|
||||
To load the driver as a module at boot time, place the following line in
|
||||
.Xr loader.conf 5 :
|
||||
.Bd -literal -offset indent
|
||||
if_wg_load="YES"
|
||||
.Ed
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
driver provides Virtual Private Network (VPN) interfaces for the secure
|
||||
exchange of layer 3 traffic with other WireGuard peers using the WireGuard
|
||||
protocol.
|
||||
.Pp
|
||||
A
|
||||
.Nm
|
||||
interface recognises one or more peers, establishes a secure tunnel with
|
||||
each on demand, and tracks each peer's UDP endpoint for exchanging encrypted
|
||||
traffic with.
|
||||
.Pp
|
||||
The interfaces can be created at runtime using the
|
||||
.Ic ifconfig Cm wg Ns Ar N Cm create
|
||||
command.
|
||||
The interface itself can be configured with
|
||||
.Xr wg 8 .
|
||||
.Pp
|
||||
The following glossary provides a brief overview of WireGuard
|
||||
terminology:
|
||||
.Bl -tag -width indent -offset 3n
|
||||
.It Peer
|
||||
Peers exchange IPv4 or IPv6 traffic over secure tunnels.
|
||||
Each
|
||||
.Nm
|
||||
interface may be configured to recognise one or more peers.
|
||||
.It Key
|
||||
Each peer uses its private key and corresponding public key to
|
||||
identify itself to others.
|
||||
A peer configures a
|
||||
.Nm
|
||||
interface with its own private key and with the public keys of its peers.
|
||||
.It Pre-shared key
|
||||
In addition to the public keys, each peer pair may be configured with a
|
||||
unique pre-shared symmetric key.
|
||||
This is used in their handshake to guard against future compromise of the
|
||||
peers' encrypted tunnel if a quantum-computational attack on their
|
||||
Diffie-Hellman exchange becomes feasible.
|
||||
It is optional, but recommended.
|
||||
.It Allowed IPs
|
||||
A single
|
||||
.Nm
|
||||
interface may maintain concurrent tunnels connecting diverse networks.
|
||||
The interface therefore implements rudimentary routing and reverse-path
|
||||
filtering functions for its tunneled traffic.
|
||||
These functions reference a set of allowed IP ranges configured against
|
||||
each peer.
|
||||
.Pp
|
||||
The interface will route outbound tunneled traffic to the peer configured
|
||||
with the most specific matching allowed IP address range, or drop it
|
||||
if no such match exists.
|
||||
.Pp
|
||||
The interface will accept tunneled traffic only from the peer
|
||||
configured with the most specific matching allowed IP address range
|
||||
for the incoming traffic, or drop it if no such match exists.
|
||||
That is, tunneled traffic routed to a given peer cannot return through
|
||||
another peer of the same
|
||||
.Nm
|
||||
interface.
|
||||
This ensures that peers cannot spoof another's traffic.
|
||||
.It Handshake
|
||||
Two peers handshake to mutually authenticate each other and to
|
||||
establish a shared series of secret ephemeral encryption keys.
|
||||
Any peer may initiate a handshake.
|
||||
Handshakes occur only when there is traffic to send, and recur every
|
||||
two minutes during transfers.
|
||||
.It Connectionless
|
||||
Due to the handshake behavior, there is no connected or disconnected
|
||||
state.
|
||||
.El
|
||||
.Ss Keys
|
||||
Private keys for WireGuard can be generated from any sufficiently
|
||||
secure random source.
|
||||
The Curve25519 keys and the pre-shared keys are both 32 bytes
|
||||
long and are commonly encoded in base64 for ease of use.
|
||||
.Pp
|
||||
Keys can be generated with
|
||||
.Xr wg 8
|
||||
as follows:
|
||||
.Pp
|
||||
.Dl $ wg genkey
|
||||
.Pp
|
||||
Although a valid Curve25519 key must have 5 bits set to
|
||||
specific values, this is done by the interface and so it
|
||||
will accept any random 32-byte base64 string.
|
||||
.Sh EXAMPLES
|
||||
Create a
|
||||
.Nm
|
||||
interface and set random private key.
|
||||
.Bd -literal -offset indent
|
||||
# ifconfig wg0 create
|
||||
# wg genkey | wg set wg0 listen-port 54321 private-key /dev/stdin
|
||||
.Ed
|
||||
.Pp
|
||||
Retrieve the associated public key from a
|
||||
.Nm
|
||||
interface.
|
||||
.Bd -literal -offset indent
|
||||
$ wg show wg0 public-key
|
||||
.Ed
|
||||
.Pp
|
||||
Connect to a specific endpoint using its public-key and set the allowed IP address
|
||||
.Bd -literal -offset indent
|
||||
# wg set wg0 peer '7lWtsDdqaGB3EY9WNxRN3hVaHMtu1zXw71+bOjNOVUw=' endpoint 10.0.1.100:54321 allowed-ips 192.168.2.100/32
|
||||
.Ed
|
||||
.Pp
|
||||
Remove a peer
|
||||
.Bd -literal -offset indent
|
||||
# wg set wg0 peer '7lWtsDdqaGB3EY9WNxRN3hVaHMtu1zXw71+bOjNOVUw=' remove
|
||||
.Ed
|
||||
.Sh DIAGNOSTICS
|
||||
The
|
||||
.Nm
|
||||
interface supports runtime debugging, which can be enabled with:
|
||||
.Pp
|
||||
.D1 Ic ifconfig Cm wg Ns Ar N Cm debug
|
||||
.Pp
|
||||
Some common error messages include:
|
||||
.Bl -diag
|
||||
.It "Handshake for peer X did not complete after 5 seconds, retrying"
|
||||
Peer X did not reply to our initiation packet, for example because:
|
||||
.Bl -bullet
|
||||
.It
|
||||
The peer does not have the local interface configured as a peer.
|
||||
Peers must be able to mutually authenticate each other.
|
||||
.It
|
||||
The peer endpoint IP address is incorrectly configured.
|
||||
.It
|
||||
There are firewall rules preventing communication between hosts.
|
||||
.El
|
||||
.It "Invalid handshake initiation"
|
||||
The incoming handshake packet could not be processed.
|
||||
This is likely due to the local interface not containing
|
||||
the correct public key for the peer.
|
||||
.It "Invalid initiation MAC"
|
||||
The incoming handshake initiation packet had an invalid MAC.
|
||||
This is likely because the initiation sender has the wrong public key
|
||||
for the handshake receiver.
|
||||
.It "Packet has unallowed src IP from peer X"
|
||||
After decryption, an incoming data packet has a source IP address that
|
||||
is not assigned to the allowed IPs of Peer X.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr inet 4 ,
|
||||
.Xr ip 4 ,
|
||||
.Xr netintro 4 ,
|
||||
.Xr ipf 5 ,
|
||||
.Xr pf.conf 5 ,
|
||||
.Xr ifconfig 8 ,
|
||||
.Xr ipfw 8 ,
|
||||
.Xr wg 8
|
||||
.Rs
|
||||
.%T WireGuard whitepaper
|
||||
.%U https://www.wireguard.com/papers/wireguard.pdf
|
||||
.Re
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
device driver first appeared in
|
||||
.Fx 14.0 .
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
device driver written by
|
||||
.An Jason A. Donenfeld Aq Mt Jason@zx2c4.com ,
|
||||
.An Matt Dunwoodie Aq Mt ncon@nconroy.net ,
|
||||
and
|
||||
.An Kyle Evans Aq Mt kevans@FreeBSD.org .
|
||||
.Pp
|
||||
This manual page was written by
|
||||
.An Gordon Bergling Aq Mt gbe@FreeBSD.org
|
||||
and is based on the
|
||||
.Ox
|
||||
manual page written by
|
||||
.An David Gwynne Aq Mt dlg@openbsd.org .
|
|
@ -961,6 +961,9 @@ device enc
|
|||
# Link aggregation interface.
|
||||
device lagg
|
||||
|
||||
# WireGuard interface.
|
||||
device wg
|
||||
|
||||
#
|
||||
# Internet family options:
|
||||
#
|
||||
|
|
|
@ -750,8 +750,8 @@ crypto/sha2/sha256c.c optional crypto | ekcd | geom_bde | \
|
|||
crypto/sha2/sha512c.c optional crypto | geom_bde | zfs
|
||||
crypto/skein/skein.c optional crypto | zfs
|
||||
crypto/skein/skein_block.c optional crypto | zfs
|
||||
crypto/siphash/siphash.c optional inet | inet6
|
||||
crypto/siphash/siphash_test.c optional inet | inet6
|
||||
crypto/siphash/siphash.c optional inet | inet6 | wg
|
||||
crypto/siphash/siphash_test.c optional inet | inet6 | wg
|
||||
ddb/db_access.c optional ddb
|
||||
ddb/db_break.c optional ddb
|
||||
ddb/db_capture.c optional ddb
|
||||
|
@ -3480,6 +3480,14 @@ dev/vt/vt_font.c optional vt
|
|||
dev/vt/vt_sysmouse.c optional vt
|
||||
dev/vte/if_vte.c optional vte pci
|
||||
dev/watchdog/watchdog.c standard
|
||||
dev/wg/if_wg.c optional wg \
|
||||
compile-with "${NORMAL_C} -include $S/dev/wg/compat.h"
|
||||
dev/wg/wg_cookie.c optional wg \
|
||||
compile-with "${NORMAL_C} -include $S/dev/wg/compat.h"
|
||||
dev/wg/wg_crypto.c optional wg \
|
||||
compile-with "${NORMAL_C} -include $S/dev/wg/compat.h"
|
||||
dev/wg/wg_noise.c optional wg \
|
||||
compile-with "${NORMAL_C} -include $S/dev/wg/compat.h"
|
||||
dev/wpi/if_wpi.c optional wpi pci
|
||||
wpifw.c optional wpifw \
|
||||
compile-with "${AWK} -f $S/tools/fw_stub.awk wpi.fw:wpifw:153229 -mwpi -c${.TARGET}" \
|
||||
|
|
118
sys/dev/wg/compat.h
Normal file
118
sys/dev/wg/compat.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (c) 2022 The FreeBSD Foundation
|
||||
*
|
||||
* compat.h contains code that is backported from FreeBSD's main branch.
|
||||
* It is different from support.h, which is for code that is not _yet_ upstream.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#if (__FreeBSD_version < 1400036 && __FreeBSD_version >= 1400000) || __FreeBSD_version < 1300519
|
||||
#define COMPAT_NEED_CHACHA20POLY1305_MBUF
|
||||
#endif
|
||||
|
||||
#if __FreeBSD_version < 1400048
|
||||
#define COMPAT_NEED_CHACHA20POLY1305
|
||||
#endif
|
||||
|
||||
#if __FreeBSD_version < 1400049
|
||||
#define COMPAT_NEED_CURVE25519
|
||||
#endif
|
||||
|
||||
#if __FreeBSD_version < 0x7fffffff /* TODO: update this when implemented */
|
||||
#define COMPAT_NEED_BLAKE2S
|
||||
#endif
|
||||
|
||||
#if __FreeBSD_version < 1400059
|
||||
#include <sys/sockbuf.h>
|
||||
#define sbcreatecontrol(a, b, c, d, e) sbcreatecontrol(a, b, c, d)
|
||||
#endif
|
||||
|
||||
#if __FreeBSD_version < 1300507
|
||||
#include <sys/smp.h>
|
||||
#include <sys/gtaskqueue.h>
|
||||
|
||||
struct taskqgroup_cpu {
|
||||
LIST_HEAD(, grouptask) tgc_tasks;
|
||||
struct gtaskqueue *tgc_taskq;
|
||||
int tgc_cnt;
|
||||
int tgc_cpu;
|
||||
};
|
||||
|
||||
struct taskqgroup {
|
||||
struct taskqgroup_cpu tqg_queue[MAXCPU];
|
||||
/* Other members trimmed from compat. */
|
||||
};
|
||||
|
||||
static inline void taskqgroup_drain_all(struct taskqgroup *tqg)
|
||||
{
|
||||
struct gtaskqueue *q;
|
||||
|
||||
for (int i = 0; i < mp_ncpus; i++) {
|
||||
q = tqg->tqg_queue[i].tgc_taskq;
|
||||
if (q == NULL)
|
||||
continue;
|
||||
gtaskqueue_drain_all(q);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __FreeBSD_version < 1300000
|
||||
#define VIMAGE
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/libkern.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/socketvar.h>
|
||||
#include <sys/protosw.h>
|
||||
#include <net/vnet.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <vm/uma.h>
|
||||
|
||||
#define taskqgroup_attach(a, b, c, d, e, f) taskqgroup_attach((a), (b), (c), -1, (f))
|
||||
#define taskqgroup_attach_cpu(a, b, c, d, e, f, g) taskqgroup_attach_cpu((a), (b), (c), (d), -1, (g))
|
||||
|
||||
#undef NET_EPOCH_ENTER
|
||||
#define NET_EPOCH_ENTER(et) NET_EPOCH_ENTER_ET(et)
|
||||
#undef NET_EPOCH_EXIT
|
||||
#define NET_EPOCH_EXIT(et) NET_EPOCH_EXIT_ET(et)
|
||||
#define NET_EPOCH_CALL(f, c) epoch_call(net_epoch_preempt, (c), (f))
|
||||
#define NET_EPOCH_ASSERT() MPASS(in_epoch(net_epoch_preempt))
|
||||
|
||||
#undef atomic_load_ptr
|
||||
#define atomic_load_ptr(p) (*(volatile __typeof(*p) *)(p))
|
||||
|
||||
#endif
|
||||
|
||||
#if __FreeBSD_version < 1202000
|
||||
static inline uint32_t arc4random_uniform(uint32_t bound)
|
||||
{
|
||||
uint32_t ret, max_mod_bound;
|
||||
|
||||
if (bound < 2)
|
||||
return 0;
|
||||
|
||||
max_mod_bound = (1 + ~bound) % bound;
|
||||
|
||||
do {
|
||||
ret = arc4random();
|
||||
} while (ret < max_mod_bound);
|
||||
|
||||
return ret % bound;
|
||||
}
|
||||
|
||||
typedef void callout_func_t(void *);
|
||||
|
||||
#ifndef CSUM_SND_TAG
|
||||
#define CSUM_SND_TAG 0x80000000
|
||||
#endif
|
||||
|
||||
#endif
|
182
sys/dev/wg/crypto.h
Normal file
182
sys/dev/wg/crypto.h
Normal file
|
@ -0,0 +1,182 @@
|
|||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (c) 2022 The FreeBSD Foundation
|
||||
*/
|
||||
|
||||
#ifndef _WG_CRYPTO
|
||||
#define _WG_CRYPTO
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
struct mbuf;
|
||||
|
||||
int crypto_init(void);
|
||||
void crypto_deinit(void);
|
||||
|
||||
enum chacha20poly1305_lengths {
|
||||
XCHACHA20POLY1305_NONCE_SIZE = 24,
|
||||
CHACHA20POLY1305_KEY_SIZE = 32,
|
||||
CHACHA20POLY1305_AUTHTAG_SIZE = 16
|
||||
};
|
||||
|
||||
#ifdef COMPAT_NEED_CHACHA20POLY1305
|
||||
void
|
||||
chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, const size_t src_len,
|
||||
const uint8_t *ad, const size_t ad_len,
|
||||
const uint64_t nonce,
|
||||
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
|
||||
|
||||
bool
|
||||
chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, const size_t src_len,
|
||||
const uint8_t *ad, const size_t ad_len,
|
||||
const uint64_t nonce,
|
||||
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
|
||||
|
||||
void
|
||||
xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src,
|
||||
const size_t src_len, const uint8_t *ad,
|
||||
const size_t ad_len,
|
||||
const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
|
||||
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
|
||||
|
||||
bool
|
||||
xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src,
|
||||
const size_t src_len, const uint8_t *ad,
|
||||
const size_t ad_len,
|
||||
const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
|
||||
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
|
||||
#else
|
||||
#include <sys/endian.h>
|
||||
#include <crypto/chacha20_poly1305.h>
|
||||
|
||||
static inline void
|
||||
chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, const size_t src_len,
|
||||
const uint8_t *ad, const size_t ad_len,
|
||||
const uint64_t nonce,
|
||||
const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
|
||||
{
|
||||
uint8_t nonce_bytes[8];
|
||||
|
||||
le64enc(nonce_bytes, nonce);
|
||||
chacha20_poly1305_encrypt(dst, src, src_len, ad, ad_len,
|
||||
nonce_bytes, sizeof(nonce_bytes), key);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, const size_t src_len,
|
||||
const uint8_t *ad, const size_t ad_len,
|
||||
const uint64_t nonce,
|
||||
const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
|
||||
{
|
||||
uint8_t nonce_bytes[8];
|
||||
|
||||
le64enc(nonce_bytes, nonce);
|
||||
return (chacha20_poly1305_decrypt(dst, src, src_len, ad, ad_len,
|
||||
nonce_bytes, sizeof(nonce_bytes), key));
|
||||
}
|
||||
|
||||
static inline void
|
||||
xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src,
|
||||
const size_t src_len, const uint8_t *ad,
|
||||
const size_t ad_len,
|
||||
const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
|
||||
const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
|
||||
{
|
||||
xchacha20_poly1305_encrypt(dst, src, src_len, ad, ad_len, nonce, key);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src,
|
||||
const size_t src_len, const uint8_t *ad,
|
||||
const size_t ad_len,
|
||||
const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
|
||||
const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
|
||||
{
|
||||
return (xchacha20_poly1305_decrypt(dst, src, src_len, ad, ad_len, nonce, key));
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
chacha20poly1305_encrypt_mbuf(struct mbuf *, const uint64_t nonce,
|
||||
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
|
||||
|
||||
int
|
||||
chacha20poly1305_decrypt_mbuf(struct mbuf *, const uint64_t nonce,
|
||||
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
|
||||
|
||||
|
||||
enum blake2s_lengths {
|
||||
BLAKE2S_BLOCK_SIZE = 64,
|
||||
BLAKE2S_HASH_SIZE = 32,
|
||||
BLAKE2S_KEY_SIZE = 32
|
||||
};
|
||||
|
||||
#ifdef COMPAT_NEED_BLAKE2S
|
||||
struct blake2s_state {
|
||||
uint32_t h[8];
|
||||
uint32_t t[2];
|
||||
uint32_t f[2];
|
||||
uint8_t buf[BLAKE2S_BLOCK_SIZE];
|
||||
unsigned int buflen;
|
||||
unsigned int outlen;
|
||||
};
|
||||
|
||||
void blake2s_init(struct blake2s_state *state, const size_t outlen);
|
||||
|
||||
void blake2s_init_key(struct blake2s_state *state, const size_t outlen,
|
||||
const uint8_t *key, const size_t keylen);
|
||||
|
||||
void blake2s_update(struct blake2s_state *state, const uint8_t *in, size_t inlen);
|
||||
|
||||
void blake2s_final(struct blake2s_state *state, uint8_t *out);
|
||||
|
||||
static inline void blake2s(uint8_t *out, const uint8_t *in, const uint8_t *key,
|
||||
const size_t outlen, const size_t inlen, const size_t keylen)
|
||||
{
|
||||
struct blake2s_state state;
|
||||
|
||||
if (keylen)
|
||||
blake2s_init_key(&state, outlen, key, keylen);
|
||||
else
|
||||
blake2s_init(&state, outlen);
|
||||
|
||||
blake2s_update(&state, in, inlen);
|
||||
blake2s_final(&state, out);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef COMPAT_NEED_CURVE25519
|
||||
enum curve25519_lengths {
|
||||
CURVE25519_KEY_SIZE = 32
|
||||
};
|
||||
|
||||
bool curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE],
|
||||
const uint8_t secret[static CURVE25519_KEY_SIZE],
|
||||
const uint8_t basepoint[static CURVE25519_KEY_SIZE]);
|
||||
|
||||
static inline bool
|
||||
curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE],
|
||||
const uint8_t secret[static CURVE25519_KEY_SIZE])
|
||||
{
|
||||
static const uint8_t basepoint[CURVE25519_KEY_SIZE] = { 9 };
|
||||
|
||||
return curve25519(pub, secret, basepoint);
|
||||
}
|
||||
|
||||
static inline void curve25519_clamp_secret(uint8_t secret[static CURVE25519_KEY_SIZE])
|
||||
{
|
||||
secret[0] &= 248;
|
||||
secret[31] = (secret[31] & 127) | 64;
|
||||
}
|
||||
|
||||
static inline void curve25519_generate_secret(uint8_t secret[CURVE25519_KEY_SIZE])
|
||||
{
|
||||
arc4random_buf(secret, CURVE25519_KEY_SIZE);
|
||||
curve25519_clamp_secret(secret);
|
||||
}
|
||||
#else
|
||||
#include <crypto/curve25519.h>
|
||||
#endif
|
||||
|
||||
#endif
|
3055
sys/dev/wg/if_wg.c
Normal file
3055
sys/dev/wg/if_wg.c
Normal file
File diff suppressed because it is too large
Load diff
37
sys/dev/wg/if_wg.h
Normal file
37
sys/dev/wg/if_wg.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 2019 Matt Dunwoodie <ncon@noconroy.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __IF_WG_H__
|
||||
#define __IF_WG_H__
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
struct wg_data_io {
|
||||
char wgd_name[IFNAMSIZ];
|
||||
void *wgd_data;
|
||||
size_t wgd_size;
|
||||
};
|
||||
|
||||
#define WG_KEY_SIZE 32
|
||||
|
||||
#define SIOCSWG _IOWR('i', 210, struct wg_data_io)
|
||||
#define SIOCGWG _IOWR('i', 211, struct wg_data_io)
|
||||
|
||||
#endif /* __IF_WG_H__ */
|
21
sys/dev/wg/support.h
Normal file
21
sys/dev/wg/support.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (c) 2021 Kyle Evans <kevans@FreeBSD.org>
|
||||
*
|
||||
* support.h contains code that is not _yet_ upstream in FreeBSD's main branch.
|
||||
* It is different from compat.h, which is strictly for backports.
|
||||
*/
|
||||
|
||||
#ifndef _WG_SUPPORT
|
||||
#define _WG_SUPPORT
|
||||
|
||||
#ifndef ck_pr_store_bool
|
||||
#define ck_pr_store_bool(dst, val) ck_pr_store_8((uint8_t *)(dst), (uint8_t)(val))
|
||||
#endif
|
||||
|
||||
#ifndef ck_pr_load_bool
|
||||
#define ck_pr_load_bool(src) ((bool)ck_pr_load_8((uint8_t *)(src)))
|
||||
#endif
|
||||
|
||||
#endif
|
1
sys/dev/wg/version.h
Normal file
1
sys/dev/wg/version.h
Normal file
|
@ -0,0 +1 @@
|
|||
#define WIREGUARD_VERSION 20220615
|
500
sys/dev/wg/wg_cookie.c
Normal file
500
sys/dev/wg/wg_cookie.c
Normal file
|
@ -0,0 +1,500 @@
|
|||
/* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
|
||||
*/
|
||||
|
||||
#include "opt_inet.h"
|
||||
#include "opt_inet6.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/socket.h>
|
||||
#include <crypto/siphash/siphash.h>
|
||||
#include <netinet/in.h>
|
||||
#include <vm/uma.h>
|
||||
|
||||
#include "wg_cookie.h"
|
||||
|
||||
#define COOKIE_MAC1_KEY_LABEL "mac1----"
|
||||
#define COOKIE_COOKIE_KEY_LABEL "cookie--"
|
||||
#define COOKIE_SECRET_MAX_AGE 120
|
||||
#define COOKIE_SECRET_LATENCY 5
|
||||
|
||||
/* Constants for initiation rate limiting */
|
||||
#define RATELIMIT_SIZE (1 << 13)
|
||||
#define RATELIMIT_MASK (RATELIMIT_SIZE - 1)
|
||||
#define RATELIMIT_SIZE_MAX (RATELIMIT_SIZE * 8)
|
||||
#define INITIATIONS_PER_SECOND 20
|
||||
#define INITIATIONS_BURSTABLE 5
|
||||
#define INITIATION_COST (SBT_1S / INITIATIONS_PER_SECOND)
|
||||
#define TOKEN_MAX (INITIATION_COST * INITIATIONS_BURSTABLE)
|
||||
#define ELEMENT_TIMEOUT 1
|
||||
#define IPV4_MASK_SIZE 4 /* Use all 4 bytes of IPv4 address */
|
||||
#define IPV6_MASK_SIZE 8 /* Use top 8 bytes (/64) of IPv6 address */
|
||||
|
||||
struct ratelimit_key {
|
||||
struct vnet *vnet;
|
||||
uint8_t ip[IPV6_MASK_SIZE];
|
||||
};
|
||||
|
||||
struct ratelimit_entry {
|
||||
LIST_ENTRY(ratelimit_entry) r_entry;
|
||||
struct ratelimit_key r_key;
|
||||
sbintime_t r_last_time; /* sbinuptime */
|
||||
uint64_t r_tokens;
|
||||
};
|
||||
|
||||
struct ratelimit {
|
||||
uint8_t rl_secret[SIPHASH_KEY_LENGTH];
|
||||
struct mtx rl_mtx;
|
||||
struct callout rl_gc;
|
||||
LIST_HEAD(, ratelimit_entry) rl_table[RATELIMIT_SIZE];
|
||||
size_t rl_table_num;
|
||||
};
|
||||
|
||||
static void precompute_key(uint8_t *,
|
||||
const uint8_t[COOKIE_INPUT_SIZE], const char *);
|
||||
static void macs_mac1(struct cookie_macs *, const void *, size_t,
|
||||
const uint8_t[COOKIE_KEY_SIZE]);
|
||||
static void macs_mac2(struct cookie_macs *, const void *, size_t,
|
||||
const uint8_t[COOKIE_COOKIE_SIZE]);
|
||||
static int timer_expired(sbintime_t, uint32_t, uint32_t);
|
||||
static void make_cookie(struct cookie_checker *,
|
||||
uint8_t[COOKIE_COOKIE_SIZE], struct sockaddr *);
|
||||
static void ratelimit_init(struct ratelimit *);
|
||||
static void ratelimit_deinit(struct ratelimit *);
|
||||
static void ratelimit_gc_callout(void *);
|
||||
static void ratelimit_gc_schedule(struct ratelimit *);
|
||||
static void ratelimit_gc(struct ratelimit *, bool);
|
||||
static int ratelimit_allow(struct ratelimit *, struct sockaddr *, struct vnet *);
|
||||
static uint64_t siphash13(const uint8_t [SIPHASH_KEY_LENGTH], const void *, size_t);
|
||||
|
||||
static struct ratelimit ratelimit_v4;
|
||||
#ifdef INET6
|
||||
static struct ratelimit ratelimit_v6;
|
||||
#endif
|
||||
static uma_zone_t ratelimit_zone;
|
||||
|
||||
/* Public Functions */
|
||||
int
|
||||
cookie_init(void)
|
||||
{
|
||||
if ((ratelimit_zone = uma_zcreate("wg ratelimit",
|
||||
sizeof(struct ratelimit_entry), NULL, NULL, NULL, NULL, 0, 0)) == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
ratelimit_init(&ratelimit_v4);
|
||||
#ifdef INET6
|
||||
ratelimit_init(&ratelimit_v6);
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
cookie_deinit(void)
|
||||
{
|
||||
ratelimit_deinit(&ratelimit_v4);
|
||||
#ifdef INET6
|
||||
ratelimit_deinit(&ratelimit_v6);
|
||||
#endif
|
||||
uma_zdestroy(ratelimit_zone);
|
||||
}
|
||||
|
||||
void
|
||||
cookie_checker_init(struct cookie_checker *cc)
|
||||
{
|
||||
bzero(cc, sizeof(*cc));
|
||||
|
||||
rw_init(&cc->cc_key_lock, "cookie_checker_key");
|
||||
mtx_init(&cc->cc_secret_mtx, "cookie_checker_secret", NULL, MTX_DEF);
|
||||
}
|
||||
|
||||
void
|
||||
cookie_checker_free(struct cookie_checker *cc)
|
||||
{
|
||||
rw_destroy(&cc->cc_key_lock);
|
||||
mtx_destroy(&cc->cc_secret_mtx);
|
||||
explicit_bzero(cc, sizeof(*cc));
|
||||
}
|
||||
|
||||
void
|
||||
cookie_checker_update(struct cookie_checker *cc,
|
||||
const uint8_t key[COOKIE_INPUT_SIZE])
|
||||
{
|
||||
rw_wlock(&cc->cc_key_lock);
|
||||
if (key) {
|
||||
precompute_key(cc->cc_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
|
||||
precompute_key(cc->cc_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
|
||||
} else {
|
||||
bzero(cc->cc_mac1_key, sizeof(cc->cc_mac1_key));
|
||||
bzero(cc->cc_cookie_key, sizeof(cc->cc_cookie_key));
|
||||
}
|
||||
rw_wunlock(&cc->cc_key_lock);
|
||||
}
|
||||
|
||||
void
|
||||
cookie_checker_create_payload(struct cookie_checker *cc,
|
||||
struct cookie_macs *macs, uint8_t nonce[COOKIE_NONCE_SIZE],
|
||||
uint8_t ecookie[COOKIE_ENCRYPTED_SIZE], struct sockaddr *sa)
|
||||
{
|
||||
uint8_t cookie[COOKIE_COOKIE_SIZE];
|
||||
|
||||
make_cookie(cc, cookie, sa);
|
||||
arc4random_buf(nonce, COOKIE_NONCE_SIZE);
|
||||
|
||||
rw_rlock(&cc->cc_key_lock);
|
||||
xchacha20poly1305_encrypt(ecookie, cookie, COOKIE_COOKIE_SIZE,
|
||||
macs->mac1, COOKIE_MAC_SIZE, nonce, cc->cc_cookie_key);
|
||||
rw_runlock(&cc->cc_key_lock);
|
||||
|
||||
explicit_bzero(cookie, sizeof(cookie));
|
||||
}
|
||||
|
||||
void
|
||||
cookie_maker_init(struct cookie_maker *cm, const uint8_t key[COOKIE_INPUT_SIZE])
|
||||
{
|
||||
bzero(cm, sizeof(*cm));
|
||||
precompute_key(cm->cm_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
|
||||
precompute_key(cm->cm_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
|
||||
rw_init(&cm->cm_lock, "cookie_maker");
|
||||
}
|
||||
|
||||
void
|
||||
cookie_maker_free(struct cookie_maker *cm)
|
||||
{
|
||||
rw_destroy(&cm->cm_lock);
|
||||
explicit_bzero(cm, sizeof(*cm));
|
||||
}
|
||||
|
||||
int
|
||||
cookie_maker_consume_payload(struct cookie_maker *cm,
|
||||
uint8_t nonce[COOKIE_NONCE_SIZE], uint8_t ecookie[COOKIE_ENCRYPTED_SIZE])
|
||||
{
|
||||
uint8_t cookie[COOKIE_COOKIE_SIZE];
|
||||
int ret;
|
||||
|
||||
rw_rlock(&cm->cm_lock);
|
||||
if (!cm->cm_mac1_sent) {
|
||||
ret = ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!xchacha20poly1305_decrypt(cookie, ecookie, COOKIE_ENCRYPTED_SIZE,
|
||||
cm->cm_mac1_last, COOKIE_MAC_SIZE, nonce, cm->cm_cookie_key)) {
|
||||
ret = EINVAL;
|
||||
goto error;
|
||||
}
|
||||
rw_runlock(&cm->cm_lock);
|
||||
|
||||
rw_wlock(&cm->cm_lock);
|
||||
memcpy(cm->cm_cookie, cookie, COOKIE_COOKIE_SIZE);
|
||||
cm->cm_cookie_birthdate = getsbinuptime();
|
||||
cm->cm_cookie_valid = true;
|
||||
cm->cm_mac1_sent = false;
|
||||
rw_wunlock(&cm->cm_lock);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
rw_runlock(&cm->cm_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
cookie_maker_mac(struct cookie_maker *cm, struct cookie_macs *macs, void *buf,
|
||||
size_t len)
|
||||
{
|
||||
rw_wlock(&cm->cm_lock);
|
||||
macs_mac1(macs, buf, len, cm->cm_mac1_key);
|
||||
memcpy(cm->cm_mac1_last, macs->mac1, COOKIE_MAC_SIZE);
|
||||
cm->cm_mac1_sent = true;
|
||||
|
||||
if (cm->cm_cookie_valid &&
|
||||
!timer_expired(cm->cm_cookie_birthdate,
|
||||
COOKIE_SECRET_MAX_AGE - COOKIE_SECRET_LATENCY, 0)) {
|
||||
macs_mac2(macs, buf, len, cm->cm_cookie);
|
||||
} else {
|
||||
bzero(macs->mac2, COOKIE_MAC_SIZE);
|
||||
cm->cm_cookie_valid = false;
|
||||
}
|
||||
rw_wunlock(&cm->cm_lock);
|
||||
}
|
||||
|
||||
int
|
||||
cookie_checker_validate_macs(struct cookie_checker *cc, struct cookie_macs *macs,
|
||||
void *buf, size_t len, bool check_cookie, struct sockaddr *sa, struct vnet *vnet)
|
||||
{
|
||||
struct cookie_macs our_macs;
|
||||
uint8_t cookie[COOKIE_COOKIE_SIZE];
|
||||
|
||||
/* Validate incoming MACs */
|
||||
rw_rlock(&cc->cc_key_lock);
|
||||
macs_mac1(&our_macs, buf, len, cc->cc_mac1_key);
|
||||
rw_runlock(&cc->cc_key_lock);
|
||||
|
||||
/* If mac1 is invald, we want to drop the packet */
|
||||
if (timingsafe_bcmp(our_macs.mac1, macs->mac1, COOKIE_MAC_SIZE) != 0)
|
||||
return EINVAL;
|
||||
|
||||
if (check_cookie) {
|
||||
make_cookie(cc, cookie, sa);
|
||||
macs_mac2(&our_macs, buf, len, cookie);
|
||||
|
||||
/* If the mac2 is invalid, we want to send a cookie response */
|
||||
if (timingsafe_bcmp(our_macs.mac2, macs->mac2, COOKIE_MAC_SIZE) != 0)
|
||||
return EAGAIN;
|
||||
|
||||
/* If the mac2 is valid, we may want rate limit the peer.
|
||||
* ratelimit_allow will return either 0 or ECONNREFUSED,
|
||||
* implying there is no ratelimiting, or we should ratelimit
|
||||
* (refuse) respectively. */
|
||||
if (sa->sa_family == AF_INET)
|
||||
return ratelimit_allow(&ratelimit_v4, sa, vnet);
|
||||
#ifdef INET6
|
||||
else if (sa->sa_family == AF_INET6)
|
||||
return ratelimit_allow(&ratelimit_v6, sa, vnet);
|
||||
#endif
|
||||
else
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Private functions */
|
||||
static void
|
||||
precompute_key(uint8_t *key, const uint8_t input[COOKIE_INPUT_SIZE],
|
||||
const char *label)
|
||||
{
|
||||
struct blake2s_state blake;
|
||||
blake2s_init(&blake, COOKIE_KEY_SIZE);
|
||||
blake2s_update(&blake, label, strlen(label));
|
||||
blake2s_update(&blake, input, COOKIE_INPUT_SIZE);
|
||||
blake2s_final(&blake, key);
|
||||
}
|
||||
|
||||
static void
|
||||
macs_mac1(struct cookie_macs *macs, const void *buf, size_t len,
|
||||
const uint8_t key[COOKIE_KEY_SIZE])
|
||||
{
|
||||
struct blake2s_state state;
|
||||
blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_KEY_SIZE);
|
||||
blake2s_update(&state, buf, len);
|
||||
blake2s_final(&state, macs->mac1);
|
||||
}
|
||||
|
||||
static void
|
||||
macs_mac2(struct cookie_macs *macs, const void *buf, size_t len,
|
||||
const uint8_t key[COOKIE_COOKIE_SIZE])
|
||||
{
|
||||
struct blake2s_state state;
|
||||
blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_COOKIE_SIZE);
|
||||
blake2s_update(&state, buf, len);
|
||||
blake2s_update(&state, macs->mac1, COOKIE_MAC_SIZE);
|
||||
blake2s_final(&state, macs->mac2);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
timer_expired(sbintime_t timer, uint32_t sec, uint32_t nsec)
|
||||
{
|
||||
sbintime_t now = getsbinuptime();
|
||||
return (now > (timer + sec * SBT_1S + nstosbt(nsec))) ? ETIMEDOUT : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
make_cookie(struct cookie_checker *cc, uint8_t cookie[COOKIE_COOKIE_SIZE],
|
||||
struct sockaddr *sa)
|
||||
{
|
||||
struct blake2s_state state;
|
||||
|
||||
mtx_lock(&cc->cc_secret_mtx);
|
||||
if (timer_expired(cc->cc_secret_birthdate,
|
||||
COOKIE_SECRET_MAX_AGE, 0)) {
|
||||
arc4random_buf(cc->cc_secret, COOKIE_SECRET_SIZE);
|
||||
cc->cc_secret_birthdate = getsbinuptime();
|
||||
}
|
||||
blake2s_init_key(&state, COOKIE_COOKIE_SIZE, cc->cc_secret,
|
||||
COOKIE_SECRET_SIZE);
|
||||
mtx_unlock(&cc->cc_secret_mtx);
|
||||
|
||||
if (sa->sa_family == AF_INET) {
|
||||
blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_addr,
|
||||
sizeof(struct in_addr));
|
||||
blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_port,
|
||||
sizeof(in_port_t));
|
||||
blake2s_final(&state, cookie);
|
||||
#ifdef INET6
|
||||
} else if (sa->sa_family == AF_INET6) {
|
||||
blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_addr,
|
||||
sizeof(struct in6_addr));
|
||||
blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_port,
|
||||
sizeof(in_port_t));
|
||||
blake2s_final(&state, cookie);
|
||||
#endif
|
||||
} else {
|
||||
arc4random_buf(cookie, COOKIE_COOKIE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ratelimit_init(struct ratelimit *rl)
|
||||
{
|
||||
size_t i;
|
||||
mtx_init(&rl->rl_mtx, "ratelimit_lock", NULL, MTX_DEF);
|
||||
callout_init_mtx(&rl->rl_gc, &rl->rl_mtx, 0);
|
||||
arc4random_buf(rl->rl_secret, sizeof(rl->rl_secret));
|
||||
for (i = 0; i < RATELIMIT_SIZE; i++)
|
||||
LIST_INIT(&rl->rl_table[i]);
|
||||
rl->rl_table_num = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ratelimit_deinit(struct ratelimit *rl)
|
||||
{
|
||||
mtx_lock(&rl->rl_mtx);
|
||||
callout_stop(&rl->rl_gc);
|
||||
ratelimit_gc(rl, true);
|
||||
mtx_unlock(&rl->rl_mtx);
|
||||
mtx_destroy(&rl->rl_mtx);
|
||||
}
|
||||
|
||||
static void
|
||||
ratelimit_gc_callout(void *_rl)
|
||||
{
|
||||
/* callout will lock rl_mtx for us */
|
||||
ratelimit_gc(_rl, false);
|
||||
}
|
||||
|
||||
static void
|
||||
ratelimit_gc_schedule(struct ratelimit *rl)
|
||||
{
|
||||
/* Trigger another GC if needed. There is no point calling GC if there
|
||||
* are no entries in the table. We also want to ensure that GC occurs
|
||||
* on a regular interval, so don't override a currently pending GC.
|
||||
*
|
||||
* In the case of a forced ratelimit_gc, there will be no entries left
|
||||
* so we will will not schedule another GC. */
|
||||
if (rl->rl_table_num > 0 && !callout_pending(&rl->rl_gc))
|
||||
callout_reset(&rl->rl_gc, ELEMENT_TIMEOUT * hz,
|
||||
ratelimit_gc_callout, rl);
|
||||
}
|
||||
|
||||
static void
|
||||
ratelimit_gc(struct ratelimit *rl, bool force)
|
||||
{
|
||||
size_t i;
|
||||
struct ratelimit_entry *r, *tr;
|
||||
sbintime_t expiry;
|
||||
|
||||
mtx_assert(&rl->rl_mtx, MA_OWNED);
|
||||
|
||||
if (rl->rl_table_num == 0)
|
||||
return;
|
||||
|
||||
expiry = getsbinuptime() - ELEMENT_TIMEOUT * SBT_1S;
|
||||
|
||||
for (i = 0; i < RATELIMIT_SIZE; i++) {
|
||||
LIST_FOREACH_SAFE(r, &rl->rl_table[i], r_entry, tr) {
|
||||
if (r->r_last_time < expiry || force) {
|
||||
rl->rl_table_num--;
|
||||
LIST_REMOVE(r, r_entry);
|
||||
uma_zfree(ratelimit_zone, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ratelimit_gc_schedule(rl);
|
||||
}
|
||||
|
||||
static int
|
||||
ratelimit_allow(struct ratelimit *rl, struct sockaddr *sa, struct vnet *vnet)
|
||||
{
|
||||
uint64_t bucket, tokens;
|
||||
sbintime_t diff, now;
|
||||
struct ratelimit_entry *r;
|
||||
int ret = ECONNREFUSED;
|
||||
struct ratelimit_key key = { .vnet = vnet };
|
||||
size_t len = sizeof(key);
|
||||
|
||||
if (sa->sa_family == AF_INET) {
|
||||
memcpy(key.ip, &satosin(sa)->sin_addr, IPV4_MASK_SIZE);
|
||||
len -= IPV6_MASK_SIZE - IPV4_MASK_SIZE;
|
||||
}
|
||||
#ifdef INET6
|
||||
else if (sa->sa_family == AF_INET6)
|
||||
memcpy(key.ip, &satosin6(sa)->sin6_addr, IPV6_MASK_SIZE);
|
||||
#endif
|
||||
else
|
||||
return ret;
|
||||
|
||||
bucket = siphash13(rl->rl_secret, &key, len) & RATELIMIT_MASK;
|
||||
mtx_lock(&rl->rl_mtx);
|
||||
|
||||
LIST_FOREACH(r, &rl->rl_table[bucket], r_entry) {
|
||||
if (bcmp(&r->r_key, &key, len) != 0)
|
||||
continue;
|
||||
|
||||
/* If we get to here, we've found an entry for the endpoint.
|
||||
* We apply standard token bucket, by calculating the time
|
||||
* lapsed since our last_time, adding that, ensuring that we
|
||||
* cap the tokens at TOKEN_MAX. If the endpoint has no tokens
|
||||
* left (that is tokens <= INITIATION_COST) then we block the
|
||||
* request, otherwise we subtract the INITITIATION_COST and
|
||||
* return OK. */
|
||||
now = getsbinuptime();
|
||||
diff = now - r->r_last_time;
|
||||
r->r_last_time = now;
|
||||
|
||||
tokens = r->r_tokens + diff;
|
||||
|
||||
if (tokens > TOKEN_MAX)
|
||||
tokens = TOKEN_MAX;
|
||||
|
||||
if (tokens >= INITIATION_COST) {
|
||||
r->r_tokens = tokens - INITIATION_COST;
|
||||
goto ok;
|
||||
} else {
|
||||
r->r_tokens = tokens;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we get to here, we didn't have an entry for the endpoint, let's
|
||||
* add one if we have space. */
|
||||
if (rl->rl_table_num >= RATELIMIT_SIZE_MAX)
|
||||
goto error;
|
||||
|
||||
/* Goto error if out of memory */
|
||||
if ((r = uma_zalloc(ratelimit_zone, M_NOWAIT | M_ZERO)) == NULL)
|
||||
goto error;
|
||||
|
||||
rl->rl_table_num++;
|
||||
|
||||
/* Insert entry into the hashtable and ensure it's initialised */
|
||||
LIST_INSERT_HEAD(&rl->rl_table[bucket], r, r_entry);
|
||||
r->r_key = key;
|
||||
r->r_last_time = getsbinuptime();
|
||||
r->r_tokens = TOKEN_MAX - INITIATION_COST;
|
||||
|
||||
/* If we've added a new entry, let's trigger GC. */
|
||||
ratelimit_gc_schedule(rl);
|
||||
ok:
|
||||
ret = 0;
|
||||
error:
|
||||
mtx_unlock(&rl->rl_mtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint64_t siphash13(const uint8_t key[SIPHASH_KEY_LENGTH], const void *src, size_t len)
|
||||
{
|
||||
SIPHASH_CTX ctx;
|
||||
return (SipHashX(&ctx, 1, 3, key, src, len));
|
||||
}
|
||||
|
||||
#ifdef SELFTESTS
|
||||
#include "selftest/cookie.c"
|
||||
#endif /* SELFTESTS */
|
72
sys/dev/wg/wg_cookie.h
Normal file
72
sys/dev/wg/wg_cookie.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
|
||||
*/
|
||||
|
||||
#ifndef __COOKIE_H__
|
||||
#define __COOKIE_H__
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#define COOKIE_MAC_SIZE 16
|
||||
#define COOKIE_KEY_SIZE 32
|
||||
#define COOKIE_NONCE_SIZE XCHACHA20POLY1305_NONCE_SIZE
|
||||
#define COOKIE_COOKIE_SIZE 16
|
||||
#define COOKIE_SECRET_SIZE 32
|
||||
#define COOKIE_INPUT_SIZE 32
|
||||
#define COOKIE_ENCRYPTED_SIZE (COOKIE_COOKIE_SIZE + COOKIE_MAC_SIZE)
|
||||
|
||||
struct vnet;
|
||||
|
||||
struct cookie_macs {
|
||||
uint8_t mac1[COOKIE_MAC_SIZE];
|
||||
uint8_t mac2[COOKIE_MAC_SIZE];
|
||||
};
|
||||
|
||||
struct cookie_maker {
|
||||
uint8_t cm_mac1_key[COOKIE_KEY_SIZE];
|
||||
uint8_t cm_cookie_key[COOKIE_KEY_SIZE];
|
||||
|
||||
struct rwlock cm_lock;
|
||||
bool cm_cookie_valid;
|
||||
uint8_t cm_cookie[COOKIE_COOKIE_SIZE];
|
||||
sbintime_t cm_cookie_birthdate; /* sbinuptime */
|
||||
bool cm_mac1_sent;
|
||||
uint8_t cm_mac1_last[COOKIE_MAC_SIZE];
|
||||
};
|
||||
|
||||
struct cookie_checker {
|
||||
struct rwlock cc_key_lock;
|
||||
uint8_t cc_mac1_key[COOKIE_KEY_SIZE];
|
||||
uint8_t cc_cookie_key[COOKIE_KEY_SIZE];
|
||||
|
||||
struct mtx cc_secret_mtx;
|
||||
sbintime_t cc_secret_birthdate; /* sbinuptime */
|
||||
uint8_t cc_secret[COOKIE_SECRET_SIZE];
|
||||
};
|
||||
|
||||
int cookie_init(void);
|
||||
void cookie_deinit(void);
|
||||
void cookie_checker_init(struct cookie_checker *);
|
||||
void cookie_checker_free(struct cookie_checker *);
|
||||
void cookie_checker_update(struct cookie_checker *,
|
||||
const uint8_t[COOKIE_INPUT_SIZE]);
|
||||
void cookie_checker_create_payload(struct cookie_checker *,
|
||||
struct cookie_macs *cm, uint8_t[COOKIE_NONCE_SIZE],
|
||||
uint8_t [COOKIE_ENCRYPTED_SIZE], struct sockaddr *);
|
||||
void cookie_maker_init(struct cookie_maker *, const uint8_t[COOKIE_INPUT_SIZE]);
|
||||
void cookie_maker_free(struct cookie_maker *);
|
||||
int cookie_maker_consume_payload(struct cookie_maker *,
|
||||
uint8_t[COOKIE_NONCE_SIZE], uint8_t[COOKIE_ENCRYPTED_SIZE]);
|
||||
void cookie_maker_mac(struct cookie_maker *, struct cookie_macs *,
|
||||
void *, size_t);
|
||||
int cookie_checker_validate_macs(struct cookie_checker *,
|
||||
struct cookie_macs *, void *, size_t, bool, struct sockaddr *,
|
||||
struct vnet *);
|
||||
|
||||
#ifdef SELFTESTS
|
||||
bool cookie_selftest(void);
|
||||
#endif /* SELFTESTS */
|
||||
|
||||
#endif /* __COOKIE_H__ */
|
1830
sys/dev/wg/wg_crypto.c
Normal file
1830
sys/dev/wg/wg_crypto.c
Normal file
File diff suppressed because it is too large
Load diff
1410
sys/dev/wg/wg_noise.c
Normal file
1410
sys/dev/wg/wg_noise.c
Normal file
File diff suppressed because it is too large
Load diff
131
sys/dev/wg/wg_noise.h
Normal file
131
sys/dev/wg/wg_noise.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
/* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
|
||||
*/
|
||||
|
||||
#ifndef __NOISE_H__
|
||||
#define __NOISE_H__
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#define NOISE_PUBLIC_KEY_LEN CURVE25519_KEY_SIZE
|
||||
#define NOISE_SYMMETRIC_KEY_LEN CHACHA20POLY1305_KEY_SIZE
|
||||
#define NOISE_TIMESTAMP_LEN (sizeof(uint64_t) + sizeof(uint32_t))
|
||||
#define NOISE_AUTHTAG_LEN CHACHA20POLY1305_AUTHTAG_SIZE
|
||||
#define NOISE_HASH_LEN BLAKE2S_HASH_SIZE
|
||||
|
||||
#define REJECT_AFTER_TIME 180
|
||||
#define REKEY_TIMEOUT 5
|
||||
#define KEEPALIVE_TIMEOUT 10
|
||||
|
||||
struct noise_local;
|
||||
struct noise_remote;
|
||||
struct noise_keypair;
|
||||
|
||||
/* Local configuration */
|
||||
struct noise_local *
|
||||
noise_local_alloc(void *);
|
||||
struct noise_local *
|
||||
noise_local_ref(struct noise_local *);
|
||||
void noise_local_put(struct noise_local *);
|
||||
void noise_local_free(struct noise_local *, void (*)(struct noise_local *));
|
||||
void * noise_local_arg(struct noise_local *);
|
||||
|
||||
void noise_local_private(struct noise_local *,
|
||||
const uint8_t[NOISE_PUBLIC_KEY_LEN]);
|
||||
int noise_local_keys(struct noise_local *,
|
||||
uint8_t[NOISE_PUBLIC_KEY_LEN],
|
||||
uint8_t[NOISE_PUBLIC_KEY_LEN]);
|
||||
|
||||
/* Remote configuration */
|
||||
struct noise_remote *
|
||||
noise_remote_alloc(struct noise_local *, void *,
|
||||
const uint8_t[NOISE_PUBLIC_KEY_LEN]);
|
||||
int noise_remote_enable(struct noise_remote *);
|
||||
void noise_remote_disable(struct noise_remote *);
|
||||
struct noise_remote *
|
||||
noise_remote_lookup(struct noise_local *, const uint8_t[NOISE_PUBLIC_KEY_LEN]);
|
||||
struct noise_remote *
|
||||
noise_remote_index(struct noise_local *, uint32_t);
|
||||
struct noise_remote *
|
||||
noise_remote_ref(struct noise_remote *);
|
||||
void noise_remote_put(struct noise_remote *);
|
||||
void noise_remote_free(struct noise_remote *, void (*)(struct noise_remote *));
|
||||
struct noise_local *
|
||||
noise_remote_local(struct noise_remote *);
|
||||
void * noise_remote_arg(struct noise_remote *);
|
||||
|
||||
void noise_remote_set_psk(struct noise_remote *,
|
||||
const uint8_t[NOISE_SYMMETRIC_KEY_LEN]);
|
||||
int noise_remote_keys(struct noise_remote *,
|
||||
uint8_t[NOISE_PUBLIC_KEY_LEN],
|
||||
uint8_t[NOISE_SYMMETRIC_KEY_LEN]);
|
||||
int noise_remote_initiation_expired(struct noise_remote *);
|
||||
void noise_remote_handshake_clear(struct noise_remote *);
|
||||
void noise_remote_keypairs_clear(struct noise_remote *);
|
||||
|
||||
/* Keypair functions */
|
||||
struct noise_keypair *
|
||||
noise_keypair_lookup(struct noise_local *, uint32_t);
|
||||
struct noise_keypair *
|
||||
noise_keypair_current(struct noise_remote *);
|
||||
struct noise_keypair *
|
||||
noise_keypair_ref(struct noise_keypair *);
|
||||
int noise_keypair_received_with(struct noise_keypair *);
|
||||
void noise_keypair_put(struct noise_keypair *);
|
||||
|
||||
struct noise_remote *
|
||||
noise_keypair_remote(struct noise_keypair *);
|
||||
|
||||
int noise_keypair_nonce_next(struct noise_keypair *, uint64_t *);
|
||||
int noise_keypair_nonce_check(struct noise_keypair *, uint64_t);
|
||||
|
||||
int noise_keep_key_fresh_send(struct noise_remote *);
|
||||
int noise_keep_key_fresh_recv(struct noise_remote *);
|
||||
int noise_keypair_encrypt(
|
||||
struct noise_keypair *,
|
||||
uint32_t *r_idx,
|
||||
uint64_t nonce,
|
||||
struct mbuf *);
|
||||
int noise_keypair_decrypt(
|
||||
struct noise_keypair *,
|
||||
uint64_t nonce,
|
||||
struct mbuf *);
|
||||
|
||||
/* Handshake functions */
|
||||
int noise_create_initiation(
|
||||
struct noise_remote *,
|
||||
uint32_t *s_idx,
|
||||
uint8_t ue[NOISE_PUBLIC_KEY_LEN],
|
||||
uint8_t es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN],
|
||||
uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN]);
|
||||
|
||||
int noise_consume_initiation(
|
||||
struct noise_local *,
|
||||
struct noise_remote **,
|
||||
uint32_t s_idx,
|
||||
uint8_t ue[NOISE_PUBLIC_KEY_LEN],
|
||||
uint8_t es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN],
|
||||
uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN]);
|
||||
|
||||
int noise_create_response(
|
||||
struct noise_remote *,
|
||||
uint32_t *s_idx,
|
||||
uint32_t *r_idx,
|
||||
uint8_t ue[NOISE_PUBLIC_KEY_LEN],
|
||||
uint8_t en[0 + NOISE_AUTHTAG_LEN]);
|
||||
|
||||
int noise_consume_response(
|
||||
struct noise_local *,
|
||||
struct noise_remote **,
|
||||
uint32_t s_idx,
|
||||
uint32_t r_idx,
|
||||
uint8_t ue[NOISE_PUBLIC_KEY_LEN],
|
||||
uint8_t en[0 + NOISE_AUTHTAG_LEN]);
|
||||
|
||||
#ifdef SELFTESTS
|
||||
bool noise_counter_selftest(void);
|
||||
#endif /* SELFTESTS */
|
||||
|
||||
#endif /* __NOISE_H__ */
|
|
@ -3758,6 +3758,7 @@ prison_priv_check(struct ucred *cred, int priv)
|
|||
case PRIV_NET_SETIFFIB:
|
||||
case PRIV_NET_OVPN:
|
||||
case PRIV_NET_ME:
|
||||
case PRIV_NET_WG:
|
||||
|
||||
/*
|
||||
* 802.11-related privileges.
|
||||
|
|
|
@ -164,6 +164,7 @@ SUBDIR= \
|
|||
if_tuntap \
|
||||
if_vlan \
|
||||
if_vxlan \
|
||||
${_if_wg} \
|
||||
iflib \
|
||||
${_igc} \
|
||||
imgact_binmisc \
|
||||
|
@ -449,6 +450,9 @@ _toecore= toecore
|
|||
_if_enc= if_enc
|
||||
_if_gif= if_gif
|
||||
_if_gre= if_gre
|
||||
.if ${MK_CRYPT} != "no" || defined(ALL_MODULES)
|
||||
_if_wg= if_wg
|
||||
.endif
|
||||
_ipfw_pmod= ipfw_pmod
|
||||
.if ${KERN_OPTS:MIPSEC_SUPPORT} && !${KERN_OPTS:MIPSEC}
|
||||
_ipsec= ipsec
|
||||
|
|
10
sys/modules/if_wg/Makefile
Normal file
10
sys/modules/if_wg/Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
.PATH: ${SRCTOP}/sys/dev/wg
|
||||
|
||||
KMOD= if_wg
|
||||
|
||||
SRCS= if_wg.c wg_cookie.c wg_crypto.c wg_noise.c
|
||||
SRCS+= opt_inet.h opt_inet6.h device_if.h bus_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
||||
CFLAGS+= -include ${SRCTOP}/sys/dev/wg/compat.h
|
|
@ -256,6 +256,7 @@ typedef enum {
|
|||
IFT_ENC = 0xf4, /* Encapsulating interface */
|
||||
IFT_PFLOG = 0xf6, /* PF packet filter logging */
|
||||
IFT_PFSYNC = 0xf7, /* PF packet filter synchronization */
|
||||
IFT_WIREGUARD = 0xf8, /* WireGuard tunnel */
|
||||
} ifType;
|
||||
|
||||
/*
|
||||
|
|
|
@ -284,8 +284,8 @@ nd6_ifattach(struct ifnet *ifp)
|
|||
* default regardless of the V_ip6_auto_linklocal configuration to
|
||||
* give a reasonable default behavior.
|
||||
*/
|
||||
if ((V_ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE) ||
|
||||
(ifp->if_flags & IFF_LOOPBACK))
|
||||
if ((V_ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE &&
|
||||
ifp->if_type != IFT_WIREGUARD) || (ifp->if_flags & IFF_LOOPBACK))
|
||||
nd->flags |= ND6_IFF_AUTO_LINKLOCAL;
|
||||
/*
|
||||
* A loopback interface does not need to accept RTADV.
|
||||
|
|
|
@ -350,6 +350,7 @@
|
|||
#define PRIV_NET_SETVLANPCP PRIV_NET_SETLANPCP /* Alias Set VLAN priority */
|
||||
#define PRIV_NET_OVPN 422 /* Administer OpenVPN DCO. */
|
||||
#define PRIV_NET_ME 423 /* Administer ME interface. */
|
||||
#define PRIV_NET_WG 424 /* Administer WireGuard interface. */
|
||||
|
||||
/*
|
||||
* 802.11-related privileges.
|
||||
|
|
Loading…
Reference in a new issue