diff options
-rw-r--r-- | release/Makefile.vm | 20 | ||||
-rw-r--r-- | release/tools/basic-ci.conf | 4 | ||||
-rw-r--r-- | release/tools/vmimage.subr | 44 | ||||
-rw-r--r-- | sys/compat/linuxkpi/common/include/linux/math.h | 2 | ||||
-rw-r--r-- | sys/conf/files | 2 | ||||
-rw-r--r-- | sys/dev/sound/pcm/mixer.c | 2 | ||||
-rw-r--r-- | sys/dev/usb/controller/usb_controller.c | 2 | ||||
-rw-r--r-- | sys/modules/Makefile | 1 | ||||
-rw-r--r-- | sys/modules/wlan_gcmp/Makefile | 9 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto_gcm.c | 363 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto_gcm.h | 58 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto_gcmp.c | 682 | ||||
-rw-r--r-- | sys/net80211/ieee80211_hostap.c | 42 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.h | 7 | ||||
-rw-r--r-- | sys/net80211/ieee80211_vht.c | 4 | ||||
-rw-r--r-- | usr.sbin/wlanstats/wlanstats.c | 24 |
16 files changed, 1247 insertions, 19 deletions
diff --git a/release/Makefile.vm b/release/Makefile.vm index ec3e734285ce..1f56bdb03f97 100644 --- a/release/Makefile.vm +++ b/release/Makefile.vm @@ -21,6 +21,10 @@ VMDK_DESC= VMWare, VirtualBox disk image QCOW2_DESC= Qemu, KVM disk image RAW_DESC= Unformatted raw disk image +.if defined(WITHOUT_QEMU) +CLOUDWARE_TYPES?= \ + BASIC-CI +.else CLOUDWARE_TYPES?= AZURE \ BASIC-CI \ BASIC-CLOUDINIT \ @@ -28,6 +32,7 @@ CLOUDWARE_TYPES?= AZURE \ GCE \ ORACLE \ VAGRANT +.endif AZURE_FORMAT= vhdf AZURE_FSLIST?= ufs zfs AZURE_DESC= Microsoft Azure platform image @@ -94,7 +99,7 @@ emulator-portinstall: .endif touch ${.TARGET} -.if ${TARGET_ARCH} != ${MACHINE_ARCH} +.if ${TARGET_ARCH} != ${MACHINE_ARCH} && !defined(WITHOUT_QEMU) .if ( ${TARGET_ARCH} != "i386" ) || ( ${MACHINE_ARCH} != "amd64" ) QEMUSTATIC=/usr/local/bin/qemu-${TARGET_ARCH}-static QEMUTGT=emulator-portinstall @@ -103,6 +108,10 @@ QEMUTGT=emulator-portinstall QEMUTGT?= .if defined(WITH_CLOUDWARE) && !empty(WITH_CLOUDWARE) && !empty(CLOUDWARE) +.if (defined(WITHOUT_QEMU) && !defined(NO_ROOT)) || \ + (!defined(WITHOUT_QEMU) && defined(NO_ROOT)) +.error WITHOUT_QEMU requires NO_ROOT (and vice versa) +.endif . for _CW in ${CLOUDWARE} . if exists(${.CURDIR}/tools/${_CW:tl}.conf) && !defined(${_CW:tu}CONF) ${_CW:tu}CONF?= ${.CURDIR}/tools/${_CW:tl}.conf @@ -120,6 +129,8 @@ cw-${_CW:tl}-${_FS}-${_FMT}: ${QEMUTGT} mkdir -p ${.OBJDIR}/${.TARGET} env TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} SWAPSIZE=${SWAPSIZE} \ QEMUSTATIC=${QEMUSTATIC} \ + WITHOUT_QEMU=${WITHOUT_QEMU:Dtrue} \ + NO_ROOT=${NO_ROOT:Dtrue} \ ${.CURDIR}/scripts/mk-vmimage.sh \ -C ${.CURDIR}/tools/vmimage.subr -d ${.OBJDIR}/${.TARGET} -F ${_FS} \ -i ${.OBJDIR}/${_CW:tl}.${_FS}.${_FMT}.img -s ${VMSIZE} -f ${_FMT} \ @@ -169,6 +180,7 @@ vm-image: ${QEMUTGT} mkdir -p ${.OBJDIR}/${.TARGET}-${FORMAT}-${FS} env TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} SWAPSIZE=${SWAPSIZE} \ QEMUSTATIC=${QEMUSTATIC} \ + WITHOUT_QEMU=${WITHOUT_QEMU:Dtrue} \ ${.CURDIR}/scripts/mk-vmimage.sh \ -C ${.CURDIR}/tools/vmimage.subr \ -d ${.OBJDIR}/${.TARGET}-${FORMAT}-${FS} -F ${FS} \ @@ -210,7 +222,7 @@ vm-install: . for FORMAT in ${VMFORMATS} . for FS in ${VMFSLIST} # Don't keep the originals. There is a copy in ${.OBJDIR} if needed. - ${XZ_CMD} ${DESTDIR}/vmimages/${OSRELEASE}-${FS}.${FORMAT} + ${XZ_CMD} -f ${DESTDIR}/vmimages/${OSRELEASE}-${FS}.${FORMAT} . endfor . endfor . endif @@ -226,7 +238,7 @@ cloud-install-BASIC-CI: . for _FMT in ${BASIC-CI_FORMAT} cp -p ${.OBJDIR}/${BASIC-CI${_FS:tu}${_FMT:tu}IMAGE} \ ${DESTDIR}/ciimages/${OSRELEASE}-BASIC-CI-${_FS}.${_FMT} - ${XZ_CMD} ${DESTDIR}/ciimages/${OSRELEASE}-BASIC-CI-${_FS}.${_FMT} + ${XZ_CMD} -f ${DESTDIR}/ciimages/${OSRELEASE}-BASIC-CI-${_FS}.${_FMT} . endfor .endfor cd ${DESTDIR}/ciimages && sha512 ${OSRELEASE}* > \ @@ -240,7 +252,7 @@ cloud-install-BASIC-CLOUDINIT: . for _FMT in ${BASIC-CLOUDINIT_FORMAT} cp -p ${.OBJDIR}/${BASIC-CLOUDINIT${_FS:tu}${_FMT:tu}IMAGE} \ ${DESTDIR}/vmimages/${OSRELEASE}-BASIC-CLOUDINIT-${_FS}.${_FMT} - ${XZ_CMD} ${DESTDIR}/vmimages/${OSRELEASE}-BASIC-CLOUDINIT-${_FS}.${_FMT} + ${XZ_CMD} -f ${DESTDIR}/vmimages/${OSRELEASE}-BASIC-CLOUDINIT-${_FS}.${_FMT} . endfor .endfor cd ${DESTDIR}/vmimages && sha512 ${OSRELEASE}* > \ diff --git a/release/tools/basic-ci.conf b/release/tools/basic-ci.conf index 5acc04b1e530..12c9abb15fb1 100644 --- a/release/tools/basic-ci.conf +++ b/release/tools/basic-ci.conf @@ -21,6 +21,8 @@ beastie_disable="YES" loader_logo="none" console="comconsole,vidconsole" EOF + metalog_add_data ./boot/loader.conf + cat <<EOF >> ${DESTDIR}/etc/ssh/sshd_config PermitRootLogin yes PasswordAuthentication yes @@ -28,7 +30,7 @@ PermitEmptyPasswords yes UsePAM no EOF - touch ${DESTDIR}/firstboot + touch_firstboot return 0 } diff --git a/release/tools/vmimage.subr b/release/tools/vmimage.subr index 7ebf2144efe4..c647097e8ced 100644 --- a/release/tools/vmimage.subr +++ b/release/tools/vmimage.subr @@ -37,6 +37,13 @@ cleanup() { return 0 } +metalog_add_data() { + if [ -n "${NO_ROOT}" ]; then + echo "$1 type=file uname=root gname=wheel mode=0644" >> \ + ${DESTDIR}/METALOG + fi +} + vm_create_base() { mkdir -p ${DESTDIR} @@ -61,7 +68,14 @@ vm_install_base() { etcupdate extract -B \ -M "TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}" \ -s ${WORLDDIR} -d ${DESTDIR}/var/db/etcupdate \ - -L /dev/stdout + -L /dev/stdout ${NO_ROOT:+-N} + if [ -n "${NO_ROOT}" ]; then + # Reroot etcupdate's internal METALOG to the whole tree + sed -n 's,^\.,./var/db/etcupdate/current,p' \ + ${DESTDIR}/var/db/etcupdate/current/METALOG | \ + env -i LC_COLLATE=C sort >> ${DESTDIR}/METALOG + rm ${DESTDIR}/var/db/etcupdate/current/METALOG + fi echo '# Custom /etc/fstab for FreeBSD VM images' \ > ${DESTDIR}/etc/fstab @@ -73,22 +87,28 @@ vm_install_base() { echo '/dev/gpt/swapfs none swap sw 0 0' \ >> ${DESTDIR}/etc/fstab fi + metalog_add_data ./etc/fstab local hostname hostname="$(echo $(uname -o) | tr '[:upper:]' '[:lower:]')" echo "hostname=\"${hostname}\"" >> ${DESTDIR}/etc/rc.conf + metalog_add_data ./etc/rc.conf if [ "${VMFS}" = zfs ]; then echo "zfs_enable=\"YES\"" >> ${DESTDIR}/etc/rc.conf echo "zpool_reguid=\"zroot\"" >> ${DESTDIR}/etc/rc.conf echo "zpool_upgrade=\"zroot\"" >> ${DESTDIR}/etc/rc.conf echo "kern.geom.label.disk_ident.enable=0" >> ${DESTDIR}/boot/loader.conf echo "zfs_load=YES" >> ${DESTDIR}/boot/loader.conf + metalog_add_data ./boot/loader.conf fi return 0 } vm_emulation_setup() { + if [ -n "${WITHOUT_QEMU}" ]; then + return 0 + fi if ! [ -z "${QEMUSTATIC}" ]; then export EMULATOR=/qemu cp ${QEMUSTATIC} ${DESTDIR}/${EMULATOR} @@ -130,6 +150,10 @@ vm_extra_enable_services() { } vm_extra_install_packages() { + if [ -n "${WITHOUT_QEMU}" ]; then + return 0 + fi + if [ -z "${VM_EXTRA_PACKAGES}" ]; then return 0 fi @@ -158,6 +182,10 @@ vm_extra_pre_umount() { } vm_emulation_cleanup() { + if [ -n "${WITHOUT_QEMU}" ]; then + return 0 + fi + if ! [ -z "${QEMUSTATIC}" ]; then rm -f ${DESTDIR}/${EMULATOR} fi @@ -180,11 +208,11 @@ buildfs() { case "${VMFS}" in ufs) - makefs ${MAKEFSARGS} -o label=rootfs -o version=2 -o softupdates=1 \ - ${VMBASE} ${DESTDIR} + cd ${DESTDIR} && makefs ${MAKEFSARGS} -o label=rootfs -o version=2 -o softupdates=1 \ + ${VMBASE} .${NO_ROOT:+/METALOG} ;; zfs) - makefs -t zfs ${MAKEFSARGS} \ + cd ${DESTDIR} && makefs -t zfs ${MAKEFSARGS} \ -o poolname=zroot -o bootfs=zroot/ROOT/default -o rootpath=/ \ -o fs=zroot\;mountpoint=none \ -o fs=zroot/ROOT\;mountpoint=none \ @@ -201,7 +229,7 @@ buildfs() { -o fs=zroot/var/log\;setuid=off\;exec=off \ -o fs=zroot/var/mail\;atime=on \ -o fs=zroot/var/tmp\;setuid=off \ - ${VMBASE} ${DESTDIR} + ${VMBASE} .${NO_ROOT:+/METALOG} ;; *) echo "Unexpected VMFS value '${VMFS}'" @@ -265,7 +293,7 @@ vm_create_disk() { WITH_UNIFIED_OBJDIR=yes \ make -C ${WORLDDIR}/stand -V .OBJDIR)" BOOTFILES="$(realpath ${BOOTFILES})" - MAKEFSARGS="-s ${VMSIZE}" + MAKEFSARGS="-s ${VMSIZE} -D" case "${TARGET}:${TARGET_ARCH}" in amd64:amd64 | i386:i386) @@ -334,3 +362,7 @@ vm_extra_create_disk() { return 0 } +touch_firstboot() { + touch ${DESTDIR}/firstboot + metalog_add_data ./firstboot +} diff --git a/sys/compat/linuxkpi/common/include/linux/math.h b/sys/compat/linuxkpi/common/include/linux/math.h index dad7fa7fbe9c..5a348a57747b 100644 --- a/sys/compat/linuxkpi/common/include/linux/math.h +++ b/sys/compat/linuxkpi/common/include/linux/math.h @@ -46,7 +46,7 @@ #define DIV_ROUND_UP(x, n) howmany(x, n) #define DIV_ROUND_UP_ULL(x, n) DIV_ROUND_UP((unsigned long long)(x), (n)) -#define DIV_ROUND_DOWN_ULL(x, n) (((unsigned long long)(x) / (n)) * (n)) +#define DIV_ROUND_DOWN_ULL(x, n) ((unsigned long long)(x) / (n)) #define DIV_ROUND_CLOSEST(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) #define DIV_ROUND_CLOSEST_ULL(x, divisor) ({ \ diff --git a/sys/conf/files b/sys/conf/files index 157cda2c4b60..9173fc9b81e5 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -4201,6 +4201,8 @@ net80211/ieee80211_amrr.c optional wlan | wlan_amrr net80211/ieee80211_crypto.c optional wlan \ compile-with "${NORMAL_C} -Wno-unused-function" net80211/ieee80211_crypto_ccmp.c optional wlan wlan_ccmp +net80211/ieee80211_crypto_gcmp.c optional wlan wlan_gcmp +net80211/ieee80211_crypto_gcm.c optional wlan wlan_gcmp net80211/ieee80211_crypto_none.c optional wlan net80211/ieee80211_crypto_tkip.c optional wlan wlan_tkip net80211/ieee80211_crypto_wep.c optional wlan wlan_wep diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c index b5b5814e667b..7bd0a2e14c46 100644 --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c @@ -1365,7 +1365,7 @@ mixer_clone(void *arg, bus_topo_lock(); d = devclass_get_softc(pcm_devclass, snd_unit); /* See related comment in dsp_clone(). */ - if (d != NULL && PCM_REGISTERED(d) && d->mixer_dev != NULL) { + if (PCM_REGISTERED(d) && d->mixer_dev != NULL) { *dev = d->mixer_dev; dev_ref(*dev); } diff --git a/sys/dev/usb/controller/usb_controller.c b/sys/dev/usb/controller/usb_controller.c index d717ad56adea..7e89a5ab0155 100644 --- a/sys/dev/usb/controller/usb_controller.c +++ b/sys/dev/usb/controller/usb_controller.c @@ -438,7 +438,7 @@ usb_bus_detach(struct usb_proc_msg *pm) /* detach children first */ bus_topo_lock(); - bus_generic_detach(dev); + bus_detach_children(dev); bus_topo_unlock(); /* diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 294cb5a224de..fda662286cde 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -420,6 +420,7 @@ SUBDIR= \ wlan_acl \ wlan_amrr \ wlan_ccmp \ + wlan_gcmp \ wlan_rssadapt \ wlan_tkip \ wlan_wep \ diff --git a/sys/modules/wlan_gcmp/Makefile b/sys/modules/wlan_gcmp/Makefile new file mode 100644 index 000000000000..fb0a94b0a9df --- /dev/null +++ b/sys/modules/wlan_gcmp/Makefile @@ -0,0 +1,9 @@ +.PATH: ${SRCTOP}/sys/net80211 +.PATH: ${SRCTOP}/sys/crypto/rijndael + +KMOD= wlan_gcmp +SRCS= ieee80211_crypto_gcmp.c ieee80211_crypto_gcm.c +SRCS+= rijndael-alg-fst.c rijndael-api.c +SRCS+= opt_wlan.h + +.include <bsd.kmod.mk> diff --git a/sys/net80211/ieee80211_crypto_gcm.c b/sys/net80211/ieee80211_crypto_gcm.c new file mode 100644 index 000000000000..daf02cb96298 --- /dev/null +++ b/sys/net80211/ieee80211_crypto_gcm.c @@ -0,0 +1,363 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * All rights reserved. + * + * Galois/Counter Mode (GCM) and GMAC with AES + * + * Originally sourced from hostapd 2.11 (src/crypto/aes-gcm.c). + * + * 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 ``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 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. + */ + +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> + +#include <crypto/rijndael/rijndael.h> +#include <net80211/ieee80211_crypto_gcm.h> + +#define AES_BLOCK_LEN 16 + +#define BIT(x) (1U << (x)) + +static __inline void +xor_block(uint8_t *b, const uint8_t *a, size_t len) +{ + int i; + for (i = 0; i < len; i++) + b[i] ^= a[i]; +} + +static inline +void WPA_PUT_BE64(uint8_t *a, uint64_t val) +{ + a[0] = val >> 56; + a[1] = val >> 48; + a[2] = val >> 40; + a[3] = val >> 32; + a[4] = val >> 24; + a[5] = val >> 16; + a[6] = val >> 8; + a[7] = val & 0xff; +} + +static inline void +WPA_PUT_BE32(uint8_t *a, uint32_t val) +{ + a[0] = (val >> 24) & 0xff; + a[1] = (val >> 16) & 0xff; + a[2] = (val >> 8) & 0xff; + a[3] = val & 0xff; +} + +static inline uint32_t +WPA_GET_BE32(const uint8_t *a) +{ + return (((uint32_t) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]); +} + +static void +inc32(uint8_t *block) +{ + uint32_t val; + + val = WPA_GET_BE32(block + AES_BLOCK_LEN - 4); + val++; + WPA_PUT_BE32(block + AES_BLOCK_LEN - 4, val); +} + +static void +shift_right_block(uint8_t *v) +{ + uint32_t val; + + val = WPA_GET_BE32(v + 12); + val >>= 1; + if (v[11] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 12, val); + + val = WPA_GET_BE32(v + 8); + val >>= 1; + if (v[7] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 8, val); + + val = WPA_GET_BE32(v + 4); + val >>= 1; + if (v[3] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 4, val); + + val = WPA_GET_BE32(v); + val >>= 1; + WPA_PUT_BE32(v, val); +} + + +/* Multiplication in GF(2^128) */ +static void +gf_mult(const uint8_t *x, const uint8_t *y, uint8_t *z) +{ + uint8_t v[16]; + int i, j; + + memset(z, 0, 16); /* Z_0 = 0^128 */ + memcpy(v, y, 16); /* V_0 = Y */ + + for (i = 0; i < 16; i++) { + for (j = 0; j < 8; j++) { + if (x[i] & BIT(7 - j)) { + /* Z_(i + 1) = Z_i XOR V_i */ + xor_block(z, v, AES_BLOCK_LEN); + } else { + /* Z_(i + 1) = Z_i */ + } + + if (v[15] & 0x01) { + /* V_(i + 1) = (V_i >> 1) XOR R */ + shift_right_block(v); + /* R = 11100001 || 0^120 */ + v[0] ^= 0xe1; + } else { + /* V_(i + 1) = V_i >> 1 */ + shift_right_block(v); + } + } + } +} + +static void +ghash_start(uint8_t *y) +{ + /* Y_0 = 0^128 */ + memset(y, 0, 16); +} + +static void +ghash(const uint8_t *h, const uint8_t *x, size_t xlen, uint8_t *y) +{ + size_t m, i; + const uint8_t *xpos = x; + uint8_t tmp[16]; + + m = xlen / 16; + + for (i = 0; i < m; i++) { + /* Y_i = (Y^(i-1) XOR X_i) dot H */ + xor_block(y, xpos, AES_BLOCK_LEN); + xpos += 16; + + /* dot operation: + * multiplication operation for binary Galois (finite) field of + * 2^128 elements */ + gf_mult(y, h, tmp); + memcpy(y, tmp, 16); + } + + if (x + xlen > xpos) { + /* Add zero padded last block */ + size_t last = x + xlen - xpos; + memcpy(tmp, xpos, last); + memset(tmp + last, 0, sizeof(tmp) - last); + + /* Y_i = (Y^(i-1) XOR X_i) dot H */ + xor_block(y, tmp, AES_BLOCK_LEN); + + /* dot operation: + * multiplication operation for binary Galois (finite) field of + * 2^128 elements */ + gf_mult(y, h, tmp); + memcpy(y, tmp, 16); + } + + /* Return Y_m */ +} + +/* + * Execute the GCTR call with the counter block icb + * on payload x (size len), output into y. + */ +static void +aes_gctr(rijndael_ctx *aes, const uint8_t *icb, + const uint8_t *x, size_t xlen, uint8_t *y) +{ + size_t i, n, last; + uint8_t cb[AES_BLOCK_LEN], tmp[AES_BLOCK_LEN]; + const uint8_t *xpos = x; + uint8_t *ypos = y; + + if (xlen == 0) + return; + + n = xlen / 16; + + memcpy(cb, icb, AES_BLOCK_LEN); + + /* Full blocks */ + for (i = 0; i < n; i++) { + rijndael_encrypt(aes, cb, ypos); + xor_block(ypos, xpos, AES_BLOCK_LEN); + xpos += AES_BLOCK_LEN; + ypos += AES_BLOCK_LEN; + inc32(cb); + } + + last = x + xlen - xpos; + if (last) { + /* Last, partial block */ + rijndael_encrypt(aes, cb, tmp); + for (i = 0; i < last; i++) + *ypos++ = *xpos++ ^ tmp[i]; + } +} + +static void +aes_gcm_init_hash_subkey(rijndael_ctx *aes, uint8_t *H) +{ + /* Generate hash subkey H = AES_K(0^128) */ + memset(H, 0, AES_BLOCK_LEN); + + rijndael_encrypt(aes, H, H); +} + +static void +aes_gcm_prepare_j0(const uint8_t *iv, size_t iv_len, const uint8_t *H, + uint8_t *J0) +{ + uint8_t len_buf[16]; + + if (iv_len == 12) { + /* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */ + memcpy(J0, iv, iv_len); + memset(J0 + iv_len, 0, AES_BLOCK_LEN - iv_len); + J0[AES_BLOCK_LEN - 1] = 0x01; + } else { + /* + * s = 128 * ceil(len(IV)/128) - len(IV) + * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64) + */ + ghash_start(J0); + ghash(H, iv, iv_len, J0); + WPA_PUT_BE64(len_buf, 0); + WPA_PUT_BE64(len_buf + 8, iv_len * 8); + ghash(H, len_buf, sizeof(len_buf), J0); + } +} + +static void +aes_gcm_gctr(rijndael_ctx *aes, const uint8_t *J0, const uint8_t *in, + size_t len, uint8_t *out) +{ + uint8_t J0inc[AES_BLOCK_LEN]; + + if (len == 0) + return; + + memcpy(J0inc, J0, AES_BLOCK_LEN); + inc32(J0inc); + + aes_gctr(aes, J0inc, in, len, out); +} + +static void +aes_gcm_ghash(const uint8_t *H, const uint8_t *aad, size_t aad_len, + const uint8_t *crypt, size_t crypt_len, uint8_t *S) +{ + uint8_t len_buf[16]; + + /* + * u = 128 * ceil[len(C)/128] - len(C) + * v = 128 * ceil[len(A)/128] - len(A) + * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64) + * (i.e., zero padded to block size A || C and lengths of each in bits) + */ + ghash_start(S); + ghash(H, aad, aad_len, S); + ghash(H, crypt, crypt_len, S); + WPA_PUT_BE64(len_buf, aad_len * 8); + WPA_PUT_BE64(len_buf + 8, crypt_len * 8); + ghash(H, len_buf, sizeof(len_buf), S); +} + +/** + * aes_gcm_ae - GCM-AE_K(IV, P, A) + */ +void +ieee80211_crypto_aes_gcm_ae(rijndael_ctx *aes, const uint8_t *iv, size_t iv_len, + const uint8_t *plain, size_t plain_len, + const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *tag) +{ + uint8_t H[AES_BLOCK_LEN]; + uint8_t J0[AES_BLOCK_LEN]; + uint8_t S[GCMP_MIC_LEN]; + + aes_gcm_init_hash_subkey(aes, H); + + aes_gcm_prepare_j0(iv, iv_len, H, J0); + + /* C = GCTR_K(inc_32(J_0), P) */ + aes_gcm_gctr(aes, J0, plain, plain_len, crypt); + + aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S); + + /* T = MSB_t(GCTR_K(J_0, S)) */ + aes_gctr(aes, J0, S, sizeof(S), tag); + + /* Return (C, T) */ +} + +/** + * aes_gcm_ad - GCM-AD_K(IV, C, A, T) + * + * Return 0 if OK, -1 if decrypt failure. + */ +int +ieee80211_crypto_aes_gcm_ad(rijndael_ctx *aes, const uint8_t *iv, size_t iv_len, + const uint8_t *crypt, size_t crypt_len, + const uint8_t *aad, size_t aad_len, const uint8_t *tag, uint8_t *plain) +{ + uint8_t H[AES_BLOCK_LEN]; + uint8_t J0[AES_BLOCK_LEN]; + uint8_t S[16], T[GCMP_MIC_LEN]; + + aes_gcm_init_hash_subkey(aes, H); + + aes_gcm_prepare_j0(iv, iv_len, H, J0); + + /* P = GCTR_K(inc_32(J_0), C) */ + aes_gcm_gctr(aes, J0, crypt, crypt_len, plain); + + aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S); + + /* T' = MSB_t(GCTR_K(J_0, S)) */ + aes_gctr(aes, J0, S, sizeof(S), T); + + if (memcmp(tag, T, 16) != 0) { + return (-1); + } + + return (0); +} diff --git a/sys/net80211/ieee80211_crypto_gcm.h b/sys/net80211/ieee80211_crypto_gcm.h new file mode 100644 index 000000000000..bdb41bfaee4c --- /dev/null +++ b/sys/net80211/ieee80211_crypto_gcm.h @@ -0,0 +1,58 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * All rights reserved. + * + * Galois/Counter Mode (GCM) and GMAC with AES + * + * 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 ``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 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. + */ +#ifndef __IEEE80211_CRYPTO_GCM_H__ +#define __IEEE80211_CRYPTO_GCM_H__ + +#if defined(__KERNEL__) || defined(_KERNEL) + +#include <crypto/rijndael/rijndael.h> + +#define AES_BLOCK_LEN 16 + +/* + * buffer is 2x the AES_BLOCK_LEN, but the AAD contents may be variable + * and are padded. + */ +#define GCM_AAD_LEN (AES_BLOCK_LEN * 2) + +/* GCMP is always 128 bit / 16 byte MIC */ +#define GCMP_MIC_LEN 16 + +void ieee80211_crypto_aes_gcm_ae(rijndael_ctx *aes, const uint8_t *iv, + size_t iv_len, const uint8_t *plain, size_t plain_len, + const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *tag); + +int ieee80211_crypto_aes_gcm_ad(rijndael_ctx *aes, const uint8_t *iv, + size_t iv_len, const uint8_t *crypt, size_t crypt_len, + const uint8_t *aad, size_t aad_len, const uint8_t *tag, + uint8_t *plain); + +#endif /* __KERNEL__ */ + +#endif /* __IEEE80211_CRYPTO_GCM_H__ */ diff --git a/sys/net80211/ieee80211_crypto_gcmp.c b/sys/net80211/ieee80211_crypto_gcmp.c new file mode 100644 index 000000000000..290111235b4e --- /dev/null +++ b/sys/net80211/ieee80211_crypto_gcmp.c @@ -0,0 +1,682 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * Copyright (c) 2025 Adrian Chadd <adrian@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 ``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 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. + */ + +/* + * IEEE 802.11 AES-GCMP crypto support. + * + * The AES-GCM crypto routines in sys/net80211/ieee80211_crypto_gcm.[ch] + * are derived from similar code in hostapd 2.11 (src/crypto/aes-gcm.c). + * The code is used with the consent of the author and its licence is + * included in the above source files. + */ +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> + +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/ethernet.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_crypto_gcm.h> + +#include <crypto/rijndael/rijndael.h> + +#define AES_BLOCK_LEN 16 + +/* + * Note: GCMP_MIC_LEN defined in ieee80211_crypto_gcm.h, as it is also + * used by the AES-GCM routines for sizing the S and T hashes which are + * used by GCMP as the MIC. + */ +#define GCMP_PN_LEN 6 +#define GCMP_IV_LEN 12 + +struct gcmp_ctx { + struct ieee80211vap *cc_vap; /* for diagnostics+statistics */ + struct ieee80211com *cc_ic; + rijndael_ctx cc_aes; +}; + +static void *gcmp_attach(struct ieee80211vap *, struct ieee80211_key *); +static void gcmp_detach(struct ieee80211_key *); +static int gcmp_setkey(struct ieee80211_key *); +static void gcmp_setiv(struct ieee80211_key *, uint8_t *); +static int gcmp_encap(struct ieee80211_key *, struct mbuf *); +static int gcmp_decap(struct ieee80211_key *, struct mbuf *, int); +static int gcmp_enmic(struct ieee80211_key *, struct mbuf *, int); +static int gcmp_demic(struct ieee80211_key *, struct mbuf *, int); + +static const struct ieee80211_cipher gcmp = { + .ic_name = "AES-GCMP", + .ic_cipher = IEEE80211_CIPHER_AES_GCM_128, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN, + .ic_trailer = GCMP_MIC_LEN, + .ic_miclen = 0, + .ic_attach = gcmp_attach, + .ic_detach = gcmp_detach, + .ic_setkey = gcmp_setkey, + .ic_setiv = gcmp_setiv, + .ic_encap = gcmp_encap, + .ic_decap = gcmp_decap, + .ic_enmic = gcmp_enmic, + .ic_demic = gcmp_demic, +}; + +static const struct ieee80211_cipher gcmp_256 = { + .ic_name = "AES-GCMP-256", + .ic_cipher = IEEE80211_CIPHER_AES_GCM_256, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN, + .ic_trailer = GCMP_MIC_LEN, + .ic_miclen = 0, + .ic_attach = gcmp_attach, + .ic_detach = gcmp_detach, + .ic_setkey = gcmp_setkey, + .ic_setiv = gcmp_setiv, + .ic_encap = gcmp_encap, + .ic_decap = gcmp_decap, + .ic_enmic = gcmp_enmic, + .ic_demic = gcmp_demic, +}; + + +static int gcmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); +static int gcmp_decrypt(struct ieee80211_key *, u_int64_t pn, + struct mbuf *, int hdrlen); + +/* number of references from net80211 layer */ +static int nrefs = 0; + +static void * +gcmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k) +{ + struct gcmp_ctx *ctx; + + ctx = (struct gcmp_ctx *) IEEE80211_MALLOC(sizeof(struct gcmp_ctx), + M_80211_CRYPTO, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); + if (ctx == NULL) { + vap->iv_stats.is_crypto_nomem++; + return (NULL); + } + ctx->cc_vap = vap; + ctx->cc_ic = vap->iv_ic; + nrefs++; /* NB: we assume caller locking */ + return (ctx); +} + +static void +gcmp_detach(struct ieee80211_key *k) +{ + struct gcmp_ctx *ctx = k->wk_private; + + IEEE80211_FREE(ctx, M_80211_CRYPTO); + KASSERT(nrefs > 0, ("imbalanced attach/detach")); + nrefs--; /* NB: we assume caller locking */ +} + +static int +gcmp_get_trailer_len(struct ieee80211_key *k) +{ + return (k->wk_cipher->ic_trailer); +} + +static int +gcmp_get_header_len(struct ieee80211_key *k) +{ + return (k->wk_cipher->ic_header); +} + +static int +gcmp_setkey(struct ieee80211_key *k) +{ + uint32_t keylen; + + struct gcmp_ctx *ctx = k->wk_private; + + switch (k->wk_cipher->ic_cipher) { + case IEEE80211_CIPHER_AES_GCM_128: + keylen = 128; + break; + case IEEE80211_CIPHER_AES_GCM_256: + keylen = 256; + break; + default: + IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO, + "%s: Unexpected cipher (%u)", + __func__, k->wk_cipher->ic_cipher); + return (0); + } + + if (k->wk_keylen != (keylen/NBBY)) { + IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO, + "%s: Invalid key length %u, expecting %u\n", + __func__, k->wk_keylen, keylen/NBBY); + return (0); + } + if (k->wk_flags & IEEE80211_KEY_SWENCRYPT) + rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY); + return (1); +} + +static void +gcmp_setiv(struct ieee80211_key *k, uint8_t *ivp) +{ + struct gcmp_ctx *ctx = k->wk_private; + struct ieee80211vap *vap = ctx->cc_vap; + uint8_t keyid; + + keyid = ieee80211_crypto_get_keyid(vap, k) << 6; + + k->wk_keytsc++; + ivp[0] = k->wk_keytsc >> 0; /* PN0 */ + ivp[1] = k->wk_keytsc >> 8; /* PN1 */ + ivp[2] = 0; /* Reserved */ + ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ + ivp[4] = k->wk_keytsc >> 16; /* PN2 */ + ivp[5] = k->wk_keytsc >> 24; /* PN3 */ + ivp[6] = k->wk_keytsc >> 32; /* PN4 */ + ivp[7] = k->wk_keytsc >> 40; /* PN5 */ +} + +/* + * Add privacy headers appropriate for the specified key. + */ +static int +gcmp_encap(struct ieee80211_key *k, struct mbuf *m) +{ + const struct ieee80211_frame *wh; + struct gcmp_ctx *ctx = k->wk_private; + struct ieee80211com *ic = ctx->cc_ic; + uint8_t *ivp; + int hdrlen; + int is_mgmt; + + hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); + wh = mtod(m, const struct ieee80211_frame *); + is_mgmt = IEEE80211_IS_MGMT(wh); + + /* + * Check to see if we need to insert IV/MIC. + * + * Some offload devices don't require the IV to be inserted + * as part of the hardware encryption. + */ + if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT)) + return (1); + if (!is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIV)) + return (1); + + /* + * Copy down 802.11 header and add the IV, KeyID, and ExtIV. + */ + M_PREPEND(m, gcmp_get_header_len(k), IEEE80211_M_NOWAIT); + if (m == NULL) + return (0); + ivp = mtod(m, uint8_t *); + ovbcopy(ivp + gcmp_get_header_len(k), ivp, hdrlen); + ivp += hdrlen; + + gcmp_setiv(k, ivp); + + /* + * Finally, do software encrypt if needed. + */ + if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) && + !gcmp_encrypt(k, m, hdrlen)) + return (0); + + return (1); +} + +/* + * Add MIC to the frame as needed. + */ +static int +gcmp_enmic(struct ieee80211_key *k, struct mbuf *m, int force) +{ + return (1); +} + +static __inline uint64_t +READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) +{ + uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24); + uint16_t iv16 = (b4 << 0) | (b5 << 8); + return ((((uint64_t)iv16) << 32) | iv32); +} + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame. The specified key should be correct but + * is also verified. + */ +static int +gcmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) +{ + const struct ieee80211_rx_stats *rxs; + struct gcmp_ctx *ctx = k->wk_private; + struct ieee80211vap *vap = ctx->cc_vap; + struct ieee80211_frame *wh; + uint8_t *ivp, tid; + uint64_t pn; + bool noreplaycheck; + + rxs = ieee80211_get_rx_params_ptr(m); + + if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) != 0) + goto finish; + + /* + * Header should have extended IV and sequence number; + * verify the former and validate the latter. + */ + wh = mtod(m, struct ieee80211_frame *); + ivp = mtod(m, uint8_t *) + hdrlen; + if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { + /* + * No extended IV; discard frame. + */ + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "%s", "missing ExtIV for AES-GCM cipher"); + vap->iv_stats.is_rx_gcmpformat++; + return (0); + } + tid = ieee80211_gettid(wh); + pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]); + + noreplaycheck = (k->wk_flags & IEEE80211_KEY_NOREPLAY) != 0; + noreplaycheck |= (rxs != NULL) && + (rxs->c_pktflags & IEEE80211_RX_F_PN_VALIDATED) != 0; + if (pn <= k->wk_keyrsc[tid] && !noreplaycheck) { + /* + * Replay violation. + */ + ieee80211_notify_replay_failure(vap, wh, k, pn, tid); + vap->iv_stats.is_rx_gcmpreplay++; + return (0); + } + + /* + * Check if the device handled the decrypt in hardware. + * If so we just strip the header; otherwise we need to + * handle the decrypt in software. Note that for the + * latter we leave the header in place for use in the + * decryption work. + */ + if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) && + !gcmp_decrypt(k, pn, m, hdrlen)) + return (0); + +finish: + /* + * Copy up 802.11 header and strip crypto bits. + */ + if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) == 0) { + ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + + gcmp_get_header_len(k), hdrlen); + m_adj(m, gcmp_get_header_len(k)); + } + + if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) == 0) + m_adj(m, -gcmp_get_trailer_len(k)); + + /* + * Ok to update rsc now. + */ + if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) == 0) { + /* + * Do not go backwards in the IEEE80211_KEY_NOREPLAY cases + * or in case hardware has checked but frames are arriving + * reordered (e.g., LinuxKPI drivers doing RSS which we are + * not prepared for at all). + */ + if (pn > k->wk_keyrsc[tid]) + k->wk_keyrsc[tid] = pn; + } + + return (1); +} + +/* + * Verify and strip MIC from the frame. + */ +static int +gcmp_demic(struct ieee80211_key *k, struct mbuf *m, int force) +{ + return (1); +} + +/** + * @brief Calculate the AAD required for this frame for AES-GCM. + * + * Note: This code was first copied over from ieee80211_crypto_ccmp.c, so + * it has some CCMP-isms. + * + * NOTE: the first two bytes are a 16 bit big-endian length, which are used + * by AES-CCM. AES-GCM doesn't require the length at the beginning. + * + * @param wh 802.11 frame to calculate the AAD over + * @param aad AAD buffer, GCM_AAD_LEN bytes + * @param The AAD length in bytes. + */ +static int +gcmp_init_aad(const struct ieee80211_frame *wh, uint8_t *aad) +{ + int aad_len; + + memset(aad, 0, GCM_AAD_LEN); + +#define IS_QOS_DATA(wh) IEEE80211_QOS_HAS_SEQ(wh) + /* AAD: + * FC with bits 4..6 and 11..13 masked to zero; 14 is always one + * A1 | A2 | A3 + * SC with bits 4..15 (seq#) masked to zero + * A4 (if present) + * QC (if present) + */ + aad[0] = 0; /* AAD length >> 8 */ + /* NB: aad[1] set below */ + + /* + * TODO: go back over this in 802.11-2020 and triple check + * the AAD assembly with regards to packet flags. + */ + + aad[2] = wh->i_fc[0] & 0x8f; /* XXX magic #s */ + /* + * TODO: 12.5.3.3.3 - bit 14 should always be set; bit 15 masked to 0 + * if QoS control field, unmasked otherwise + */ + aad[3] = wh->i_fc[1] & 0xc7; /* XXX magic #s */ + /* NB: we know 3 addresses are contiguous */ + memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN); + aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK; + aad[23] = 0; /* all bits masked */ + /* + * Construct variable-length portion of AAD based + * on whether this is a 4-address frame/QOS frame. + * We always zero-pad to 32 bytes before running it + * through the cipher. + */ + if (IEEE80211_IS_DSTODS(wh)) { + IEEE80211_ADDR_COPY(aad + 24, + ((const struct ieee80211_frame_addr4 *)wh)->i_addr4); + if (IS_QOS_DATA(wh)) { + const struct ieee80211_qosframe_addr4 *qwh4 = + (const struct ieee80211_qosframe_addr4 *) wh; + aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */ + aad[31] = 0; + aad_len = aad[1] = 22 + IEEE80211_ADDR_LEN + 2; + } else { + *(uint16_t *)&aad[30] = 0; + aad_len = aad[1] = 22 + IEEE80211_ADDR_LEN; + } + } else { + if (IS_QOS_DATA(wh)) { + const struct ieee80211_qosframe *qwh = + (const struct ieee80211_qosframe*) wh; + aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */ + aad[25] = 0; + aad_len = aad[1] = 22 + 2; + } else { + *(uint16_t *)&aad[24] = 0; + aad_len = aad[1] = 22; + } + *(uint16_t *)&aad[26] = 0; + *(uint32_t *)&aad[28] = 0; + } +#undef IS_QOS_DATA + + return (aad_len); +} + +/* + * Populate the 12 byte / 96 bit IV buffer. + */ +static int +gcmp_init_iv(uint8_t *iv, const struct ieee80211_frame *wh, u_int64_t pn) +{ + uint8_t j_pn[GCMP_PN_LEN]; + + /* Construct the pn buffer */ + j_pn[0] = pn >> 40; + j_pn[1] = pn >> 32; + j_pn[2] = pn >> 24; + j_pn[3] = pn >> 16; + j_pn[4] = pn >> 8; + j_pn[5] = pn >> 0; + + memcpy(iv, wh->i_addr2, IEEE80211_ADDR_LEN); + memcpy(iv + IEEE80211_ADDR_LEN, j_pn, GCMP_PN_LEN); + + return (GCMP_IV_LEN); /* 96 bits */ +} + +/* + * @brief Encrypt an mbuf. + * + * This uses a temporary memory buffer to encrypt; the + * current AES-GCM code expects things in a contiguous buffer + * and this avoids the need of breaking out the GCTR and + * GHASH routines into using mbuf iterators. + * + * @param key ieee80211_key to use + * @param mbuf 802.11 frame to encrypt + * @param hdrlen the length of the 802.11 header, including any padding + * @returns 0 if error, > 0 if OK. + */ +static int +gcmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) +{ + struct gcmp_ctx *ctx = key->wk_private; + struct ieee80211_frame *wh; + struct mbuf *m = m0; + int data_len, aad_len, iv_len, ret; + uint8_t aad[GCM_AAD_LEN]; + uint8_t T[GCMP_MIC_LEN]; + uint8_t iv[GCMP_IV_LEN]; + uint8_t *p_pktbuf = NULL; + uint8_t *c_pktbuf = NULL; + + wh = mtod(m, struct ieee80211_frame *); + data_len = m->m_pkthdr.len - (hdrlen + gcmp_get_header_len(key)); + + ctx->cc_vap->iv_stats.is_crypto_gcmp++; + + p_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP, + IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); + if (p_pktbuf == NULL) { + IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO, + wh->i_addr2, "%s", + "AES-GCM encrypt failed; couldn't allocate buffer"); + ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++; + return (0); + } + c_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP, + IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); + if (c_pktbuf == NULL) { + IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO, + wh->i_addr2, "%s", + "AES-GCM encrypt failed; couldn't allocate buffer"); + ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++; + IEEE80211_FREE(p_pktbuf, M_TEMP); + return (0); + } + + /* Initialise AAD */ + aad_len = gcmp_init_aad(wh, aad); + + /* Initialise local Nonce to work on */ + /* TODO: rename iv stuff here to nonce */ + iv_len = gcmp_init_iv(iv, wh, key->wk_keytsc); + + /* Copy mbuf data part into plaintext pktbuf */ + m_copydata(m0, hdrlen + gcmp_get_header_len(key), data_len, + p_pktbuf); + + /* Run encrypt */ + /* + * Note: aad + 2 to skip over the 2 byte length populated + * at the beginning, since it's based on the AAD code in CCMP. + */ + ieee80211_crypto_aes_gcm_ae(&ctx->cc_aes, iv, iv_len, + p_pktbuf, data_len, aad + 2, aad_len, c_pktbuf, T); + + /* Copy data back over mbuf */ + m_copyback(m0, hdrlen + gcmp_get_header_len(key), data_len, + c_pktbuf); + + /* Append MIC */ + ret = m_append(m0, gcmp_get_trailer_len(key), T); + if (ret == 0) { + IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO, + wh->i_addr2, "%s", + "AES-GCM encrypt failed; couldn't append T"); + ctx->cc_vap->iv_stats.is_crypto_gcmp_nospc++; + } + + IEEE80211_FREE(p_pktbuf, M_TEMP); + IEEE80211_FREE(c_pktbuf, M_TEMP); + + return (ret); +} + +/* + * @brief Decrypt an mbuf. + * + * This uses a temporary memory buffer to decrypt; the + * current AES-GCM code expects things in a contiguous buffer + * and this avoids the need of breaking out the GCTR and + * GHASH routines into using mbuf iterators. + * + * @param key ieee80211_key to use + * @param mbuf 802.11 frame to decrypt + * @param hdrlen the length of the 802.11 header, including any padding + * @returns 0 if error, > 0 if OK. + */ +static int +gcmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, + int hdrlen) +{ + const struct ieee80211_rx_stats *rxs; + struct gcmp_ctx *ctx = key->wk_private; + struct ieee80211_frame *wh; + int data_len, aad_len, iv_len, ret; + uint8_t aad[GCM_AAD_LEN]; + uint8_t T[GCMP_MIC_LEN]; + uint8_t iv[GCMP_IV_LEN]; + uint8_t *p_pktbuf = NULL; + uint8_t *c_pktbuf = NULL; + + rxs = ieee80211_get_rx_params_ptr(m); + if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED) != 0) + return (1); + + wh = mtod(m, struct ieee80211_frame *); + + /* Data length doesn't include the MIC at the end */ + data_len = m->m_pkthdr.len - + (hdrlen + gcmp_get_header_len(key) + GCMP_MIC_LEN); + + ctx->cc_vap->iv_stats.is_crypto_gcmp++; + + p_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP, + IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); + if (p_pktbuf == NULL) { + ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++; + return (0); + } + c_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP, + IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); + if (c_pktbuf == NULL) { + ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++; + IEEE80211_FREE(p_pktbuf, M_TEMP); + return (0); + } + + /* Initialise AAD */ + aad_len = gcmp_init_aad(wh, aad); + + /* Initialise local IV copy to work on */ + iv_len = gcmp_init_iv(iv, wh, pn); + + /* Copy mbuf into ciphertext pktbuf */ + m_copydata(m, hdrlen + gcmp_get_header_len(key), data_len, + c_pktbuf); + + /* Copy the MIC into the tag buffer */ + m_copydata(m, hdrlen + gcmp_get_header_len(key) + data_len, + GCMP_MIC_LEN, T); + + /* Run decrypt */ + /* + * Note: aad + 2 to skip over the 2 byte length populated + * at the beginning, since it's based on the AAD code in CCMP. + */ + ret = ieee80211_crypto_aes_gcm_ad(&ctx->cc_aes, iv, iv_len, + c_pktbuf, data_len, aad + 2, aad_len, T, p_pktbuf); + + /* If the MIC was stripped by HW/driver we are done. */ + if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) != 0) + goto skip_ok; + + if (ret != 0) { + /* Decrypt failure */ + ctx->cc_vap->iv_stats.is_rx_gcmpmic++; + IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO, + wh->i_addr2, "%s", "AES-GCM decrypt failed; MIC mismatch"); + IEEE80211_FREE(p_pktbuf, M_TEMP); + IEEE80211_FREE(c_pktbuf, M_TEMP); + return (0); + } + +skip_ok: + /* Copy data back over mbuf */ + m_copyback(m, hdrlen + gcmp_get_header_len(key), data_len, + p_pktbuf); + + IEEE80211_FREE(p_pktbuf, M_TEMP); + IEEE80211_FREE(c_pktbuf, M_TEMP); + + return (1); +} + +/* + * Module glue. + */ +IEEE80211_CRYPTO_MODULE(gcmp, 1); +IEEE80211_CRYPTO_MODULE_ADD(gcmp_256); diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c index 76c419f5bdf8..c9e2c4896f15 100644 --- a/sys/net80211/ieee80211_hostap.c +++ b/sys/net80211/ieee80211_hostap.c @@ -1206,6 +1206,7 @@ wpa_cipher(const uint8_t *sel, uint8_t *keylen, uint8_t *cipher) case WPA_SEL(WPA_CSE_CCMP): *cipher = IEEE80211_CIPHER_AES_CCM; break; + /* Note: no GCM cipher in the legacy WPA1 OUI */ default: return (EINVAL); } @@ -1384,6 +1385,9 @@ rsn_cipher(const uint8_t *sel, uint8_t *keylen, uint8_t *cipher) case RSN_SEL(RSN_CSE_WRAP): *cipher = IEEE80211_CIPHER_AES_OCB; break; + case RSN_SEL(RSN_CSE_GCMP_128): + *cipher = IEEE80211_CIPHER_AES_GCM_128; + break; default: return (EINVAL); } @@ -1496,8 +1500,10 @@ ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm, frm += 4, len -= 4; } - if (w & (1 << IEEE80211_CIPHER_AES_CCM)) - rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; + if (w & (1 << IEEE80211_CIPHER_AES_GCM_128)) + rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_GCM_128; + else if (w & (1 << IEEE80211_CIPHER_AES_CCM)) + rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; else if (w & (1 << IEEE80211_CIPHER_AES_OCB)) rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_OCB; else if (w & (1 << IEEE80211_CIPHER_TKIP)) @@ -1756,6 +1762,29 @@ is11bclient(const uint8_t *rates, const uint8_t *xrates) return 1; } +/** + * Check if the given cipher is valid for 802.11 HT operation. + * + * The 802.11 specification only allows HT A-MPDU to be performed + * on CCMP / GCMP encrypted frames. The WEP/TKIP hardware crypto + * implementations may not meet the timing required for A-MPDU + * operation. + * + * @param cipher the IEEE80211_CIPHER_ value to check + * @returns true if the cipher is valid for HT A-MPDU, false otherwise + */ +static bool +hostapd_validate_cipher_for_ht_ampdu(uint8_t cipher) +{ + switch (cipher) { + case IEEE80211_CIPHER_AES_CCM: + case IEEE80211_CIPHER_AES_GCM_128: + return true; + default: + return false; + } +} + static void hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) @@ -2222,13 +2251,16 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, #endif /* * Allow AMPDU operation only with unencrypted traffic - * or AES-CCM; the 11n spec only specifies these ciphers - * so permitting any others is undefined and can lead + * or AES-CCM / AES-GCM; the 802.11n spec only specifies these + * ciphers so permitting any others is undefined and can lead * to interoperability problems. + * + * TODO: before landing, find exactly where in 802.11-2020 this + * is called out! */ if ((ni->ni_flags & IEEE80211_NODE_HT) && (((vap->iv_flags & IEEE80211_F_WPA) && - rsnparms.rsn_ucastcipher != IEEE80211_CIPHER_AES_CCM) || + !hostapd_validate_cipher_for_ht_ampdu(rsnparms.rsn_ucastcipher)) || (vap->iv_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) { IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index 18152495c499..6064f586c923 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -253,6 +253,13 @@ struct ieee80211_stats { uint32_t is_ff_encapfail; /* failed FF encap */ uint32_t is_amsdu_encapfail; /* failed A-MSDU encap */ + uint32_t is_crypto_gcmp; /* gcmp crypto done in s/w */ + uint32_t is_rx_gcmpreplay; /* rx seq# violation (GCMP) */ + uint32_t is_rx_gcmpformat; /* rx format bad (GCMP) */ + uint32_t is_rx_gcmpmic; /* rx MIC check failed (GCMP) */ + uint32_t is_crypto_gcmp_nomem; /* gcmp crypto failed; no mem */ + uint32_t is_crypto_gcmp_nospc; /* gcmp crypto failed; no mbuf space */ + uint32_t is_spare[5]; }; diff --git a/sys/net80211/ieee80211_vht.c b/sys/net80211/ieee80211_vht.c index 703447945845..0df95d87d01a 100644 --- a/sys/net80211/ieee80211_vht.c +++ b/sys/net80211/ieee80211_vht.c @@ -237,6 +237,8 @@ ieee80211_vht_node_cleanup(struct ieee80211_node *ni) /* * Parse an 802.11ac VHT operation IE. + * + * 802.11-2020 9.4.2.158 (VHT Operation element) */ void ieee80211_parse_vhtopmode(struct ieee80211_node *ni, const uint8_t *ie) @@ -256,6 +258,8 @@ ieee80211_parse_vhtopmode(struct ieee80211_node *ni, const uint8_t *ie) /* * Parse an 802.11ac VHT capability IE. + * + * 802.11-2020 9.4.2.157 (VHT Capabilities element) */ void ieee80211_parse_vhtcap(struct ieee80211_node *ni, const uint8_t *ie) diff --git a/usr.sbin/wlanstats/wlanstats.c b/usr.sbin/wlanstats/wlanstats.c index 8795333ccb17..588b67ddd417 100644 --- a/usr.sbin/wlanstats/wlanstats.c +++ b/usr.sbin/wlanstats/wlanstats.c @@ -382,6 +382,18 @@ static const struct fmt wlanstats[] = { { 9, "ampdu_bartxfail", "bartx_fail", "BAR frames failed to send" }, #define S_AMPDU_BARTX_RETRY AFTER(S_AMPDU_BARTX_FAIL) { 10, "ampdu_bartxretry", "bartx_retry", "BAR frames retried" }, +#define S_CRYPTO_GCMP AFTER(S_AMPDU_BARTX_RETRY) + { 11, "crypto_gcmp", "crypto_gcmp", "gcmp crypto done in s/w" }, +#define S_RX_GCMPREPLAY AFTER(S_CRYPTO_GCMP) + { 10, "rx_gcmpreplay", "gcmpreplay", "rx seq# violation (GCMP)" }, +#define S_RX_GCMPFORMAT AFTER(S_RX_GCMPREPLAY) + { 10, "rx_gcmpformat", "gcmpformat", "rx format bad (GCMP)" }, +#define S_RX_GCMPMIC AFTER(S_RX_GCMPFORMAT) + { 7, "rx_gcmpmic", "gcmpmic", "rx MIC check failed (GCMP)" }, +#define S_RX_GCMPNOMEM AFTER(S_RX_GCMPMIC) + { 9, "gcmp_nomem", "gcmpnomem", "No memory available (GCMP)" }, +#define S_RX_GCMPNOSPC AFTER(S_RX_GCMPNOMEM) + { 9, "gcmp_nospc", "gcmpnospc", "No mbuf space available (GCMP)" }, }; struct wlanstatfoo_p { @@ -830,6 +842,12 @@ wlan_get_curstat(struct bsdstat *sf, int s, char b[], size_t bs) case S_AMPDU_BARTX: STAT(ampdu_bar_tx); case S_AMPDU_BARTX_RETRY: STAT(ampdu_bar_tx_retry); case S_AMPDU_BARTX_FAIL: STAT(ampdu_bar_tx_fail); + case S_CRYPTO_GCMP: STAT(crypto_gcmp); + case S_RX_GCMPREPLAY: STAT(rx_gcmpreplay); + case S_RX_GCMPFORMAT: STAT(rx_gcmpformat); + case S_RX_GCMPMIC: STAT(rx_gcmpmic); + case S_RX_GCMPNOMEM: STAT(crypto_gcmp_nomem); + case S_RX_GCMPNOSPC: STAT(crypto_gcmp_nospc); } return wlan_getinfo(wf, s, b, bs); #undef NSTAT @@ -995,6 +1013,12 @@ wlan_get_totstat(struct bsdstat *sf, int s, char b[], size_t bs) case S_AMPDU_BARTX: STAT(ampdu_bar_tx); case S_AMPDU_BARTX_RETRY: STAT(ampdu_bar_tx_retry); case S_AMPDU_BARTX_FAIL: STAT(ampdu_bar_tx_fail); + case S_CRYPTO_GCMP: STAT(crypto_gcmp); + case S_RX_GCMPREPLAY: STAT(rx_gcmpreplay); + case S_RX_GCMPFORMAT: STAT(rx_gcmpformat); + case S_RX_GCMPMIC: STAT(rx_gcmpmic); + case S_RX_GCMPNOMEM: STAT(crypto_gcmp_nomem); + case S_RX_GCMPNOSPC: STAT(crypto_gcmp_nospc); } return wlan_getinfo(wf, s, b, bs); #undef NSTAT |