Index: releng/11.1/UPDATING =================================================================== --- releng/11.1/UPDATING (revision 324698) +++ releng/11.1/UPDATING (revision 324699) @@ -1,1708 +1,1712 @@ Updating Information for FreeBSD current users. This file is maintained and copyrighted by M. Warner Losh . See end of file for further details. For commonly done items, please see the COMMON ITEMS: section later in the file. These instructions assume that you basically know what you are doing. If not, then please consult the FreeBSD handbook: https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/updating-src.html Items affecting the ports and packages system can be found in /usr/ports/UPDATING. Please read that file before running portupgrade. NOTE: FreeBSD has switched from gcc to clang. If you have trouble bootstrapping from older versions of FreeBSD, try WITHOUT_CLANG and WITH_GCC to bootstrap to the tip of head, and then rebuild without this option. The bootstrap process from older version of current across the gcc/clang cutover is a bit fragile. +20171017 p13 FreeBSD-SA-17:07.wpa + + Fix WPA2 protocol vulnerability. [SA-17:07] + 20170810 p1 FreeBSD-SA-17:06.openssh FreeBSD-EN-17:07.vnet FreeBSD-EN-17:08.pf Fix OpenSSH Denial of Service vulnerability. [SA-17:06] Fix VNET kernel panic with asynchronous I/O. [EN-17:07] Fix pf(4) housekeeping thread causes kernel panic. [EN-17:08] 20170725: 11.1-RELEASE. 20170518: arm64 builds now use the base system LLD 4.0.0 linker by default, instead of requiring that the aarch64-binutils port or package be installed. To continue using aarch64-binutils, set CROSS_BINUTILS_PREFIX=/usr/local/aarch64-freebsd/bin . 20170529: The ctl.ko module no longer implements the iSCSI target frontend: cfiscsi.ko does instead. If building cfiscsi.ko as a kernel module, the module can be loaded via one of the following methods: - `cfiscsi_load="YES"` in loader.conf(5). - Add `cfiscsi` to `$kld_list` in rc.conf(5). - ctladm(8)/ctld(8), when compiled with iSCSI support (`WITH_ISCSI=yes` in src.conf(5)) Please see cfiscsi(4) for more details. 20170511: The mmcsd.ko module now additionally depends on geom_flashmap.ko. Also, mmc.ko and mmcsd.ko need to be a matching pair built from the same source (previously, the dependency of mmcsd.ko on mmc.ko was missing, but mmcsd.ko now will refuse to load if it is incompatible with mmc.ko). 20170414: Binds and sends to the loopback addresses, IPv6 and IPv4, will now use any explicitly assigned loopback address available in the jail instead of using the first assigned address of the jail. 20170413: As of r316810 for ipfilter, keep frags is no longer assumed when keep state is specified in a rule. r316810 aligns ipfilter with documentation in man pages separating keep frags from keep state. This allows keep state to specified without forcing keep frags and allows keep frags to be specified independently of keep state. To maintain previous behaviour, also specify keep frags with keep state (as documented in ipf.conf.5). 20170402: Clang, llvm, lldb, compiler-rt and libc++ have been upgraded to 4.0.0. Please see the 20141231 entry below for information about prerequisites and upgrading, if you are not already using clang 3.5.0 or higher. 20170323: The code that provides support for ZFS .zfs/ directory functionality has been reimplemented. It's not possible now to create a snapshot by mkdir under .zfs/snapshot/. That should be the only user visible change. 20170319: Many changes in the IPsec code have been merged from the FreeBSD-CURRENT branch. The IPSEC_FILTERTUNNEL kernel option is removed in favour of corresponding sysctl. The IPSEC_NAT_T kernel option is also removed, and now NAT-T is supported by default. Security associations now use the single namespace for SPI allocation, so if you use several manually configured security associations with the same SPI, this configuration needs modification. 20161217: Clang, llvm, lldb, compiler-rt and libc++ have been upgraded to 3.9.1. Please see the 20141231 entry below for information about prerequisites and upgrading, if you are not already using clang 3.5.0 or higher. 20161124: Clang, llvm, lldb, compiler-rt and libc++ have been upgraded to 3.9.0. Please see the 20141231 entry below for information about prerequisites and upgrading, if you are not already using clang 3.5.0 or higher. 20161119: The layout of the pmap structure has changed for powerpc to put the pmap statistics at the front for all CPU variations. libkvm(3) and all tools that link against it need to be recompiled. 20161030: isl(4) and cyapa(4) drivers now require a new driver, chromebook_platform(4), to work properly on Chromebook-class hardware. On other types of hardware the drivers may need to be configured using device hints. Please see the corresponding manual pages for details. 20161210: Relocatable object files with the extension of .So have been renamed to use an extension of .pico instead. The purpose of this change is to avoid a name clash with shared libraries on case-insensitive file systems. On those file systems, foo.So is the same file as foo.so. 20160622: The libc stub for the pipe(2) system call has been replaced with a wrapper that calls the pipe2(2) system call and the pipe(2) system call is now only implemented by the kernels that include "options COMPAT_FREEBSD10" in their config file (this is the default). Users should ensure that this option is enabled in their kernel or upgrade userspace to r302092 before upgrading their kernel. 20160527: CAM will now strip leading spaces from SCSI disks' serial numbers. This will effect users who create UFS filesystems on SCSI disks using those disk's diskid device nodes. For example, if /etc/fstab previously contained a line like "/dev/diskid/DISK-%20%20%20%20%20%20%20ABCDEFG0123456", you should change it to "/dev/diskid/DISK-ABCDEFG0123456". Users of geom transforms like gmirror may also be affected. ZFS users should generally be fine. 20160523: The bitstring(3) API has been updated with new functionality and improved performance. But it is binary-incompatible with the old API. Objects built with the new headers may not be linked against objects built with the old headers. 20160520: The brk and sbrk functions have been removed from libc on arm64. Binutils from ports has been updated to not link to these functions and should be updated to the latest version before installing a new libc. 20160517: The armv6 port now defaults to hard float ABI. Limited support for running both hardfloat and soft float on the same system is available using the libraries installed with -DWITH_LIBSOFT. This has only been tested as an upgrade path for installworld and packages may fail or need manual intervention to run. New packages will be needed. To update an existing self-hosted armv6hf system, you must add TARGET_ARCH=armv6 on the make command line for both the build and the install steps. 20160510: Kernel modules compiled outside of a kernel build now default to installing to /boot/modules instead of /boot/kernel. Many kernel modules built this way (such as those in ports) already overrode KMODDIR explicitly to install into /boot/modules. However, manually building and installing a module from /sys/modules will now install to /boot/modules instead of /boot/kernel. 20160414: The CAM I/O scheduler has been committed to the kernel. There should be no user visible impact. This does enable NCQ Trim on ada SSDs. While the list of known rogues that claim support for this but actually corrupt data is believed to be complete, be on the lookout for data corruption. The known rogue list is believed to be complete: o Crucial MX100, M550 drives with MU01 firmware. o Micron M510 and M550 drives with MU01 firmware. o Micron M500 prior to MU07 firmware o Samsung 830, 840, and 850 all firmwares o FCCT M500 all firmwares Crucial has firmware http://www.crucial.com/usa/en/support-ssd-firmware with working NCQ TRIM. For Micron branded drives, see your sales rep for updated firmware. Black listed drives will work correctly because these drives work correctly so long as no NCQ TRIMs are sent to them. Given this list is the same as found in Linux, it's believed there are no other rogues in the market place. All other models from the above vendors work. To be safe, if you are at all concerned, you can quirk each of your drives to prevent NCQ from being sent by setting: kern.cam.ada.X.quirks="0x2" in loader.conf. If the drive requires the 4k sector quirk, set the quirks entry to 0x3. 20160330: The FAST_DEPEND build option has been removed and its functionality is now the one true way. The old mkdep(1) style of 'make depend' has been removed. See 20160311 for further details. 20160317: Resource range types have grown from unsigned long to uintmax_t. All drivers, and anything using libdevinfo, need to be recompiled. 20160311: WITH_FAST_DEPEND is now enabled by default for in-tree and out-of-tree builds. It no longer runs mkdep(1) during 'make depend', and the 'make depend' stage can safely be skipped now as it is auto ran when building 'make all' and will generate all SRCS and DPSRCS before building anything else. Dependencies are gathered at compile time with -MF flags kept in separate .depend files per object file. Users should run 'make cleandepend' once if using -DNO_CLEAN to clean out older stale .depend files. 20160306: On amd64, clang 3.8.0 can now insert sections of type AMD64_UNWIND into kernel modules. Therefore, if you load any kernel modules at boot time, please install the boot loaders after you install the kernel, but before rebooting, e.g.: make buildworld make kernel KERNCONF=YOUR_KERNEL_HERE make -C sys/boot install Then follow the usual steps, described in the General Notes section, below. 20160305: Clang, llvm, lldb and compiler-rt have been upgraded to 3.8.0. Please see the 20141231 entry below for information about prerequisites and upgrading, if you are not already using clang 3.5.0 or higher. 20160301: The AIO subsystem is now a standard part of the kernel. The VFS_AIO kernel option and aio.ko kernel module have been removed. Due to stability concerns, asynchronous I/O requests are only permitted on sockets and raw disks by default. To enable asynchronous I/O requests on all file types, set the vfs.aio.enable_unsafe sysctl to a non-zero value. 20160226: The ELF object manipulation tool objcopy is now provided by the ELF Tool Chain project rather than by GNU binutils. It should be a drop-in replacement, with the addition of arm64 support. The (temporary) src.conf knob WITHOUT_ELFCOPY_AS_OBJCOPY knob may be set to obtain the GNU version if necessary. 20160129: Building ZFS pools on top of zvols is prohibited by default. That feature has never worked safely; it's always been prone to deadlocks. Using a zvol as the backing store for a VM guest's virtual disk will still work, even if the guest is using ZFS. Legacy behavior can be restored by setting vfs.zfs.vol.recursive=1. 20160119: The NONE and HPN patches has been removed from OpenSSH. They are still available in the security/openssh-portable port. 20160113: With the addition of ypldap(8), a new _ypldap user is now required during installworld. "mergemaster -p" can be used to add the user prior to installworld, as documented in the handbook. 20151216: The tftp loader (pxeboot) now uses the option root-path directive. As a consequence it no longer looks for a pxeboot.4th file on the tftp server. Instead it uses the regular /boot infrastructure as with the other loaders. 20151211: The code to start recording plug and play data into the modules has been committed. While the old tools will properly build a new kernel, a number of warnings about "unknown metadata record 4" will be produced for an older kldxref. To avoid such warnings, make sure to rebuild the kernel toolchain (or world). Make sure that you have r292078 or later when trying to build 292077 or later before rebuilding. 20151207: Debug data files are now built by default with 'make buildworld' and installed with 'make installworld'. This facilitates debugging but requires more disk space both during the build and for the installed world. Debug files may be disabled by setting WITHOUT_DEBUG_FILES=yes in src.conf(5). 20151130: r291527 changed the internal interface between the nfsd.ko and nfscommon.ko modules. As such, they must both be upgraded to-gether. __FreeBSD_version has been bumped because of this. 20151108: Add support for unicode collation strings leads to a change of order of files listed by ls(1) for example. To get back to the old behaviour, set LC_COLLATE environment variable to "C". Databases administrators will need to reindex their databases given collation results will be different. Due to a bug in install(1) it is recommended to remove the ancient locales before running make installworld. rm -rf /usr/share/locale/* 20151030: The OpenSSL has been upgraded to 1.0.2d. Any binaries requiring libcrypto.so.7 or libssl.so.7 must be recompiled. 20151020: Qlogic 24xx/25xx firmware images were updated from 5.5.0 to 7.3.0. Kernel modules isp_2400_multi and isp_2500_multi were removed and should be replaced with isp_2400 and isp_2500 modules respectively. 20151017: The build previously allowed using 'make -n' to not recurse into sub-directories while showing what commands would be executed, and 'make -n -n' to recursively show commands. Now 'make -n' will recurse and 'make -N' will not. 20151012: If you specify SENDMAIL_MC or SENDMAIL_CF in make.conf, mergemaster and etcupdate will now use this file. A custom sendmail.cf is now updated via this mechanism rather than via installworld. If you had excluded sendmail.cf in mergemaster.rc or etcupdate.conf, you may want to remove the exclusion or change it to "always install". /etc/mail/sendmail.cf is now managed the same way regardless of whether SENDMAIL_MC/SENDMAIL_CF is used. If you are not using SENDMAIL_MC/SENDMAIL_CF there should be no change in behavior. 20151011: Compatibility shims for legacy ATA device names have been removed. It includes ATA_STATIC_ID kernel option, kern.cam.ada.legacy_aliases and kern.geom.raid.legacy_aliases loader tunables, kern.devalias.* environment variables, /dev/ad* and /dev/ar* symbolic links. 20151006: Clang, llvm, lldb, compiler-rt and libc++ have been upgraded to 3.7.0. Please see the 20141231 entry below for information about prerequisites and upgrading, if you are not already using clang 3.5.0 or higher. 20150924: Kernel debug files have been moved to /usr/lib/debug/boot/kernel/, and renamed from .symbols to .debug. This reduces the size requirements on the boot partition or file system and provides consistency with userland debug files. When using the supported kernel installation method the /usr/lib/debug/boot/kernel directory will be renamed (to kernel.old) as is done with /boot/kernel. Developers wishing to maintain the historical behavior of installing debug files in /boot/kernel/ can set KERN_DEBUGDIR="" in src.conf(5). 20150827: The wireless drivers had undergone changes that remove the 'parent interface' from the ifconfig -l output. The rc.d network scripts used to check presence of a parent interface in the list, so old scripts would fail to start wireless networking. Thus, etcupdate(3) or mergemaster(8) run is required after kernel update, to update your rc.d scripts in /etc. 20150827: pf no longer supports 'scrub fragment crop' or 'scrub fragment drop-ovl' These configurations are now automatically interpreted as 'scrub fragment reassemble'. 20150817: Kernel-loadable modules for the random(4) device are back. To use them, the kernel must have device random options RANDOM_LOADABLE kldload(8) can then be used to load random_fortuna.ko or random_yarrow.ko. Please note that due to the indirect function calls that the loadable modules need to provide, the build-in variants will be slightly more efficient. The random(4) kernel option RANDOM_DUMMY has been retired due to unpopularity. It was not all that useful anyway. 20150813: The WITHOUT_ELFTOOLCHAIN_TOOLS src.conf(5) knob has been retired. Control over building the ELF Tool Chain tools is now provided by the WITHOUT_TOOLCHAIN knob. 20150810: The polarity of Pulse Per Second (PPS) capture events with the uart(4) driver has been corrected. Prior to this change the PPS "assert" event corresponded to the trailing edge of a positive PPS pulse and the "clear" event was the leading edge of the next pulse. As the width of a PPS pulse in a typical GPS receiver is on the order of 1 millisecond, most users will not notice any significant difference with this change. Anyone who has compensated for the historical polarity reversal by configuring a negative offset equal to the pulse width will need to remove that workaround. 20150809: The default group assigned to /dev/dri entries has been changed from 'wheel' to 'video' with the id of '44'. If you want to have access to the dri devices please add yourself to the video group with: # pw groupmod video -m $USER 20150806: The menu.rc and loader.rc files will now be replaced during upgrades. Please migrate local changes to menu.rc.local and loader.rc.local instead. 20150805: GNU Binutils versions of addr2line, c++filt, nm, readelf, size, strings and strip have been removed. The src.conf(5) knob WITHOUT_ELFTOOLCHAIN_TOOLS no longer provides the binutils tools. 20150728: As ZFS requires more kernel stack pages than is the default on some architectures e.g. i386, it now warns if KSTACK_PAGES is less than ZFS_MIN_KSTACK_PAGES (which is 4 at the time of writing). Please consider using 'options KSTACK_PAGES=X' where X is greater than or equal to ZFS_MIN_KSTACK_PAGES i.e. 4 in such configurations. 20150706: sendmail has been updated to 8.15.2. Starting with FreeBSD 11.0 and sendmail 8.15, sendmail uses uncompressed IPv6 addresses by default, i.e., they will not contain "::". For example, instead of ::1, it will be 0:0:0:0:0:0:0:1. This permits a zero subnet to have a more specific match, such as different map entries for IPv6:0:0 vs IPv6:0. This change requires that configuration data (including maps, files, classes, custom ruleset, etc.) must use the same format, so make certain such configuration data is upgrading. As a very simple check search for patterns like 'IPv6:[0-9a-fA-F:]*::' and 'IPv6::'. To return to the old behavior, set the m4 option confUSE_COMPRESSED_IPV6_ADDRESSES or the cf option UseCompressedIPv6Addresses. 20150630: The default kernel entropy-processing algorithm is now Fortuna, replacing Yarrow. Assuming you have 'device random' in your kernel config file, the configurations allow a kernel option to override this default. You may choose *ONE* of: options RANDOM_YARROW # Legacy /dev/random algorithm. options RANDOM_DUMMY # Blocking-only driver. If you have neither, you get Fortuna. For most people, read no further, Fortuna will give a /dev/random that works like it always used to, and the difference will be irrelevant. If you remove 'device random', you get *NO* kernel-processed entropy at all. This may be acceptable to folks building embedded systems, but has complications. Carry on reading, and it is assumed you know what you need. *PLEASE* read random(4) and random(9) if you are in the habit of tweaking kernel configs, and/or if you are a member of the embedded community, wanting specific and not-usual behaviour from your security subsystems. NOTE!! If you use RANDOM_DUMMY and/or have no 'device random', you will NOT have a functioning /dev/random, and many cryptographic features will not work, including SSH. You may also find strange behaviour from the random(3) set of library functions, in particular sranddev(3), srandomdev(3) and arc4random(3). The reason for this is that the KERN_ARND sysctl only returns entropy if it thinks it has some to share, and with RANDOM_DUMMY or no 'device random' this will never happen. 20150623: An additional fix for the issue described in the 20150614 sendmail entry below has been been committed in revision 284717. 20150616: FreeBSD's old make (fmake) has been removed from the system. It is available as the devel/fmake port or via pkg install fmake. 20150615: The fix for the issue described in the 20150614 sendmail entry below has been been committed in revision 284436. The work around described in that entry is no longer needed unless the default setting is overridden by a confDH_PARAMETERS configuration setting of '5' or pointing to a 512 bit DH parameter file. 20150614: ALLOW_DEPRECATED_ATF_TOOLS/ATFFILE support has been removed from atf.test.mk (included from bsd.test.mk). Please upgrade devel/atf and devel/kyua to version 0.20+ and adjust any calling code to work with Kyuafile and kyua. 20150614: The import of openssl to address the FreeBSD-SA-15:10.openssl security advisory includes a change which rejects handshakes with DH parameters below 768 bits. sendmail releases prior to 8.15.2 (not yet released), defaulted to a 512 bit DH parameter setting for client connections. To work around this interoperability, sendmail can be configured to use a 2048 bit DH parameter by: 1. Edit /etc/mail/`hostname`.mc 2. If a setting for confDH_PARAMETERS does not exist or exists and is set to a string beginning with '5', replace it with '2'. 3. If a setting for confDH_PARAMETERS exists and is set to a file path, create a new file with: openssl dhparam -out /path/to/file 2048 4. Rebuild the .cf file: cd /etc/mail/; make; make install 5. Restart sendmail: cd /etc/mail/; make restart A sendmail patch is coming, at which time this file will be updated. 20150604: Generation of legacy formatted entries have been disabled by default in pwd_mkdb(8), as all base system consumers of the legacy formatted entries were converted to use the new format by default when the new, machine independent format have been added and supported since FreeBSD 5.x. Please see the pwd_mkdb(8) manual page for further details. 20150525: Clang and llvm have been upgraded to 3.6.1 release. Please see the 20141231 entry below for information about prerequisites and upgrading, if you are not already using 3.5.0 or higher. 20150521: TI platform code switched to using vendor DTS files and this update may break existing systems running on Beaglebone, Beaglebone Black, and Pandaboard: - dtb files should be regenerated/reinstalled. Filenames are the same but content is different now - GPIO addressing was changed, now each GPIO bank (32 pins per bank) has its own /dev/gpiocX device, e.g. pin 121 on /dev/gpioc0 in old addressing scheme is now pin 25 on /dev/gpioc3. - Pandaboard: /etc/ttys should be updated, serial console device is now /dev/ttyu2, not /dev/ttyu0 20150501: soelim(1) from gnu/usr.bin/groff has been replaced by usr.bin/soelim. If you need the GNU extension from groff soelim(1), install groff from package: pkg install groff, or via ports: textproc/groff. 20150423: chmod, chflags, chown and chgrp now affect symlinks in -R mode as defined in symlink(7); previously symlinks were silently ignored. 20150415: The const qualifier has been removed from iconv(3) to comply with POSIX. The ports tree is aware of this from r384038 onwards. 20150416: Libraries specified by LIBADD in Makefiles must have a corresponding DPADD_ variable to ensure correct dependencies. This is now enforced in src.libnames.mk. 20150324: From legacy ata(4) driver was removed support for SATA controllers supported by more functional drivers ahci(4), siis(4) and mvs(4). Kernel modules ataahci and ataadaptec were removed completely, replaced by ahci and mvs modules respectively. 20150315: Clang, llvm and lldb have been upgraded to 3.6.0 release. Please see the 20141231 entry below for information about prerequisites and upgrading, if you are not already using 3.5.0 or higher. 20150307: The 32-bit PowerPC kernel has been changed to a position-independent executable. This can only be booted with a version of loader(8) newer than January 31, 2015, so make sure to update both world and kernel before rebooting. 20150217: If you are running a -CURRENT kernel since r273872 (Oct 30th, 2014), but before r278950, the RNG was not seeded properly. Immediately upgrade the kernel to r278950 or later and regenerate any keys (e.g. ssh keys or openssl keys) that were generated w/ a kernel from that range. This does not affect programs that directly used /dev/random or /dev/urandom. All userland uses of arc4random(3) are affected. 20150210: The autofs(4) ABI was changed in order to restore binary compatibility with 10.1-RELEASE. The automountd(8) daemon needs to be rebuilt to work with the new kernel. 20150131: The powerpc64 kernel has been changed to a position-independent executable. This can only be booted with a new version of loader(8), so make sure to update both world and kernel before rebooting. 20150118: Clang and llvm have been upgraded to 3.5.1 release. This is a bugfix only release, no new features have been added. Please see the 20141231 entry below for information about prerequisites and upgrading, if you are not already using 3.5.0. 20150107: ELF tools addr2line, elfcopy (strip), nm, size, and strings are now taken from the ELF Tool Chain project rather than GNU binutils. They should be drop-in replacements, with the addition of arm64 support. The WITHOUT_ELFTOOLCHAIN_TOOLS= knob may be used to obtain the binutils tools, if necessary. See 20150805 for updated information. 20150105: The default Unbound configuration now enables remote control using a local socket. Users who have already enabled the local_unbound service should regenerate their configuration by running "service local_unbound setup" as root. 20150102: The GNU texinfo and GNU info pages have been removed. To be able to view GNU info pages please install texinfo from ports. 20141231: Clang, llvm and lldb have been upgraded to 3.5.0 release. As of this release, a prerequisite for building clang, llvm and lldb is a C++11 capable compiler and C++11 standard library. This means that to be able to successfully build the cross-tools stage of buildworld, with clang as the bootstrap compiler, your system compiler or cross compiler should either be clang 3.3 or later, or gcc 4.8 or later, and your system C++ library should be libc++, or libdstdc++ from gcc 4.8 or later. On any standard FreeBSD 10.x or 11.x installation, where clang and libc++ are on by default (that is, on x86 or arm), this should work out of the box. On 9.x installations where clang is enabled by default, e.g. on x86 and powerpc, libc++ will not be enabled by default, so libc++ should be built (with clang) and installed first. If both clang and libc++ are missing, build clang first, then use it to build libc++. On 8.x and earlier installations, upgrade to 9.x first, and then follow the instructions for 9.x above. Sparc64 and mips users are unaffected, as they still use gcc 4.2.1 by default, and do not build clang. Many embedded systems are resource constrained, and will not be able to build clang in a reasonable time, or in some cases at all. In those cases, cross building bootable systems on amd64 is a workaround. This new version of clang introduces a number of new warnings, of which the following are most likely to appear: -Wabsolute-value This warns in two cases, for both C and C++: * When the code is trying to take the absolute value of an unsigned quantity, which is effectively a no-op, and almost never what was intended. The code should be fixed, if at all possible. If you are sure that the unsigned quantity can be safely cast to signed, without loss of information or undefined behavior, you can add an explicit cast, or disable the warning. * When the code is trying to take an absolute value, but the called abs() variant is for the wrong type, which can lead to truncation. If you want to disable the warning instead of fixing the code, please make sure that truncation will not occur, or it might lead to unwanted side-effects. -Wtautological-undefined-compare and -Wundefined-bool-conversion These warn when C++ code is trying to compare 'this' against NULL, while 'this' should never be NULL in well-defined C++ code. However, there is some legacy (pre C++11) code out there, which actively abuses this feature, which was less strictly defined in previous C++ versions. Squid and openjdk do this, for example. The warning can be turned off for C++98 and earlier, but compiling the code in C++11 mode might result in unexpected behavior; for example, the parts of the program that are unreachable could be optimized away. 20141222: The old NFS client and server (kernel options NFSCLIENT, NFSSERVER) kernel sources have been removed. The .h files remain, since some utilities include them. This will need to be fixed later. If "mount -t oldnfs ..." is attempted, it will fail. If the "-o" option on mountd(8), nfsd(8) or nfsstat(1) is used, the utilities will report errors. 20141121: The handling of LOCAL_LIB_DIRS has been altered to skip addition of directories to top level SUBDIR variable when their parent directory is included in LOCAL_DIRS. Users with build systems with such hierarchies and without SUBDIR entries in the parent directory Makefiles should add them or add the directories to LOCAL_DIRS. 20141109: faith(4) and faithd(8) have been removed from the base system. Faith has been obsolete for a very long time. 20141104: vt(4), the new console driver, is enabled by default. It brings support for Unicode and double-width characters, as well as support for UEFI and integration with the KMS kernel video drivers. You may need to update your console settings in /etc/rc.conf, most probably the keymap. During boot, /etc/rc.d/syscons will indicate what you need to do. vt(4) still has issues and lacks some features compared to syscons(4). See the wiki for up-to-date information: https://wiki.freebsd.org/Newcons If you want to keep using syscons(4), you can do so by adding the following line to /boot/loader.conf: kern.vty=sc 20141102: pjdfstest has been integrated into kyua as an opt-in test suite. Please see share/doc/pjdfstest/README for more details on how to execute it. 20141009: gperf has been removed from the base system for architectures that use clang. Ports that require gperf will obtain it from the devel/gperf port. 20140923: pjdfstest has been moved from tools/regression/pjdfstest to contrib/pjdfstest . 20140922: At svn r271982, The default linux compat kernel ABI has been adjusted to 2.6.18 in support of the linux-c6 compat ports infrastructure update. If you wish to continue using the linux-f10 compat ports, add compat.linux.osrelease=2.6.16 to your local sysctl.conf. Users are encouraged to update their linux-compat packages to linux-c6 during their next update cycle. 20140729: The ofwfb driver, used to provide a graphics console on PowerPC when using vt(4), no longer allows mmap() of all physical memory. This will prevent Xorg on PowerPC with some ATI graphics cards from initializing properly unless x11-servers/xorg-server is updated to 1.12.4_8 or newer. 20140723: The xdev targets have been converted to using TARGET and TARGET_ARCH instead of XDEV and XDEV_ARCH. 20140719: The default unbound configuration has been modified to address issues with reverse lookups on networks that use private address ranges. If you use the local_unbound service, run "service local_unbound setup" as root to regenerate your configuration, then "service local_unbound reload" to load the new configuration. 20140709: The GNU texinfo and GNU info pages are not built and installed anymore, WITH_INFO knob has been added to allow to built and install them again. UPDATE: see 20150102 entry on texinfo's removal 20140708: The GNU readline library is now an INTERNALLIB - that is, it is statically linked into consumers (GDB and variants) in the base system, and the shared library is no longer installed. The devel/readline port is available for third party software that requires readline. 20140702: The Itanium architecture (ia64) has been removed from the list of known architectures. This is the first step in the removal of the architecture. 20140701: Commit r268115 has added NFSv4.1 server support, merged from projects/nfsv4.1-server. Since this includes changes to the internal interfaces between the NFS related modules, a full build of the kernel and modules will be necessary. __FreeBSD_version has been bumped. 20140629: The WITHOUT_VT_SUPPORT kernel config knob has been renamed WITHOUT_VT. (The other _SUPPORT knobs have a consistent meaning which differs from the behaviour controlled by this knob.) 20140619: Maximal length of the serial number in CTL was increased from 16 to 64 chars, that breaks ABI. All CTL-related tools, such as ctladm and ctld, need to be rebuilt to work with a new kernel. 20140606: The libatf-c and libatf-c++ major versions were downgraded to 0 and 1 respectively to match the upstream numbers. They were out of sync because, when they were originally added to FreeBSD, the upstream versions were not respected. These libraries are private and not yet built by default, so renumbering them should be a non-issue. However, unclean source trees will yield broken test programs once the operator executes "make delete-old-libs" after a "make installworld". Additionally, the atf-sh binary was made private by moving it into /usr/libexec/. Already-built shell test programs will keep the path to the old binary so they will break after "make delete-old" is run. If you are using WITH_TESTS=yes (not the default), wipe the object tree and rebuild from scratch to prevent spurious test failures. This is only needed once: the misnumbered libraries and misplaced binaries have been added to OptionalObsoleteFiles.inc so they will be removed during a clean upgrade. 20140512: Clang and llvm have been upgraded to 3.4.1 release. 20140508: We bogusly installed src.opts.mk in /usr/share/mk. This file should be removed to avoid issues in the future (and has been added to ObsoleteFiles.inc). 20140505: /etc/src.conf now affects only builds of the FreeBSD src tree. In the past, it affected all builds that used the bsd.*.mk files. The old behavior was a bug, but people may have relied upon it. To get this behavior back, you can .include /etc/src.conf from /etc/make.conf (which is still global and isn't changed). This also changes the behavior of incremental builds inside the tree of individual directories. Set MAKESYSPATH to ".../share/mk" to do that. Although this has survived make universe and some upgrade scenarios, other upgrade scenarios may have broken. At least one form of temporary breakage was fixed with MAKESYSPATH settings for buildworld as well... In cases where MAKESYSPATH isn't working with this setting, you'll need to set it to the full path to your tree. One side effect of all this cleaning up is that bsd.compiler.mk is no longer implicitly included by bsd.own.mk. If you wish to use COMPILER_TYPE, you must now explicitly include bsd.compiler.mk as well. 20140430: The lindev device has been removed since /dev/full has been made a standard device. __FreeBSD_version has been bumped. 20140424: The knob WITHOUT_VI was added to the base system, which controls building ex(1), vi(1), etc. Older releases of FreeBSD required ex(1) in order to reorder files share/termcap and didn't build ex(1) as a build tool, so building/installing with WITH_VI is highly advised for build hosts for older releases. This issue has been fixed in stable/9 and stable/10 in r277022 and r276991, respectively. 20140418: The YES_HESIOD knob has been removed. It has been obsolete for a decade. Please move to using WITH_HESIOD instead or your builds will silently lack HESIOD. 20140405: The uart(4) driver has been changed with respect to its handling of the low-level console. Previously the uart(4) driver prevented any process from changing the baudrate or the CLOCAL and HUPCL control flags. By removing the restrictions, operators can make changes to the serial console port without having to reboot. However, when getty(8) is started on the serial device that is associated with the low-level console, a misconfigured terminal line in /etc/ttys will now have a real impact. Before upgrading the kernel, make sure that /etc/ttys has the serial console device configured as 3wire without baudrate to preserve the previous behaviour. E.g: ttyu0 "/usr/libexec/getty 3wire" vt100 on secure 20140306: Support for libwrap (TCP wrappers) in rpcbind was disabled by default to improve performance. To re-enable it, if needed, run rpcbind with command line option -W. 20140226: Switched back to the GPL dtc compiler due to updates in the upstream dts files not being supported by the BSDL dtc compiler. You will need to rebuild your kernel toolchain to pick up the new compiler. Core dumps may result while building dtb files during a kernel build if you fail to do so. Set WITHOUT_GPL_DTC if you require the BSDL compiler. 20140216: Clang and llvm have been upgraded to 3.4 release. 20140216: The nve(4) driver has been removed. Please use the nfe(4) driver for NVIDIA nForce MCP Ethernet adapters instead. 20140212: An ABI incompatibility crept into the libc++ 3.4 import in r261283. This could cause certain C++ applications using shared libraries built against the previous version of libc++ to crash. The incompatibility has now been fixed, but any C++ applications or shared libraries built between r261283 and r261801 should be recompiled. 20140204: OpenSSH will now ignore errors caused by kernel lacking of Capsicum capability mode support. Please note that enabling the feature in kernel is still highly recommended. 20140131: OpenSSH is now built with sandbox support, and will use sandbox as the default privilege separation method. This requires Capsicum capability mode support in kernel. 20140128: The libelf and libdwarf libraries have been updated to newer versions from upstream. Shared library version numbers for these two libraries were bumped. Any ports or binaries requiring these two libraries should be recompiled. __FreeBSD_version is bumped to 1100006. 20140110: If a Makefile in a tests/ directory was auto-generating a Kyuafile instead of providing an explicit one, this would prevent such Makefile from providing its own Kyuafile in the future during NO_CLEAN builds. This has been fixed in the Makefiles but manual intervention is needed to clean an objdir if you use NO_CLEAN: # find /usr/obj -name Kyuafile | xargs rm -f 20131213: The behavior of gss_pseudo_random() for the krb5 mechanism has changed, for applications requesting a longer random string than produced by the underlying enctype's pseudo-random() function. In particular, the random string produced from a session key of enctype aes256-cts-hmac-sha1-96 or aes256-cts-hmac-sha1-96 will be different at the 17th octet and later, after this change. The counter used in the PRF+ construction is now encoded as a big-endian integer in accordance with RFC 4402. __FreeBSD_version is bumped to 1100004. 20131108: The WITHOUT_ATF build knob has been removed and its functionality has been subsumed into the more generic WITHOUT_TESTS. If you were using the former to disable the build of the ATF libraries, you should change your settings to use the latter. 20131025: The default version of mtree is nmtree which is obtained from NetBSD. The output is generally the same, but may vary slightly. If you found you need identical output adding "-F freebsd9" to the command line should do the trick. For the time being, the old mtree is available as fmtree. 20131014: libbsdyml has been renamed to libyaml and moved to /usr/lib/private. This will break ports-mgmt/pkg. Rebuild the port, or upgrade to pkg 1.1.4_8 and verify bsdyml not linked in, before running "make delete-old-libs": # make -C /usr/ports/ports-mgmt/pkg build deinstall install clean or # pkg install pkg; ldd /usr/local/sbin/pkg | grep bsdyml 20131010: The stable/10 branch has been created in subversion from head revision r256279. 20131010: The rc.d/jail script has been updated to support jail(8) configuration file. The "jail__*" rc.conf(5) variables for per-jail configuration are automatically converted to /var/run/jail..conf before the jail(8) utility is invoked. This is transparently backward compatible. See below about some incompatibilities and rc.conf(5) manual page for more details. These variables are now deprecated in favor of jail(8) configuration file. One can use "rc.d/jail config " command to generate a jail(8) configuration file in /var/run/jail..conf without running the jail(8) utility. The default pathname of the configuration file is /etc/jail.conf and can be specified by using $jail_conf or $jail__conf variables. Please note that jail_devfs_ruleset accepts an integer at this moment. Please consider to rewrite the ruleset name with an integer. 20130930: BIND has been removed from the base system. If all you need is a local resolver, simply enable and start the local_unbound service instead. Otherwise, several versions of BIND are available in the ports tree. The dns/bind99 port is one example. With this change, nslookup(1) and dig(1) are no longer in the base system. Users should instead use host(1) and drill(1) which are in the base system. Alternatively, nslookup and dig can be obtained by installing the dns/bind-tools port. 20130916: With the addition of unbound(8), a new unbound user is now required during installworld. "mergemaster -p" can be used to add the user prior to installworld, as documented in the handbook. 20130911: OpenSSH is now built with DNSSEC support, and will by default silently trust signed SSHFP records. This can be controlled with the VerifyHostKeyDNS client configuration setting. DNSSEC support can be disabled entirely with the WITHOUT_LDNS option in src.conf. 20130906: The GNU Compiler Collection and C++ standard library (libstdc++) are no longer built by default on platforms where clang is the system compiler. You can enable them with the WITH_GCC and WITH_GNUCXX options in src.conf. 20130905: The PROCDESC kernel option is now part of the GENERIC kernel configuration and is required for the rwhod(8) to work. If you are using custom kernel configuration, you should include 'options PROCDESC'. 20130905: The API and ABI related to the Capsicum framework was modified in backward incompatible way. The userland libraries and programs have to be recompiled to work with the new kernel. This includes the following libraries and programs, but the whole buildworld is advised: libc, libprocstat, dhclient, tcpdump, hastd, hastctl, kdump, procstat, rwho, rwhod, uniq. 20130903: AES-NI intrinsic support has been added to gcc. The AES-NI module has been updated to use this support. A new gcc is required to build the aesni module on both i386 and amd64. 20130821: The PADLOCK_RNG and RDRAND_RNG kernel options are now devices. Thus "device padlock_rng" and "device rdrand_rng" should be used instead of "options PADLOCK_RNG" & "options RDRAND_RNG". 20130813: WITH_ICONV has been split into two feature sets. WITH_ICONV now enables just the iconv* functionality and is now on by default. WITH_LIBICONV_COMPAT enables the libiconv api and link time compatibility. Set WITHOUT_ICONV to build the old way. If you have been using WITH_ICONV before, you will very likely need to turn on WITH_LIBICONV_COMPAT. 20130806: INVARIANTS option now enables DEBUG for code with OpenSolaris and Illumos origin, including ZFS. If you have INVARIANTS in your kernel configuration, then there is no need to set DEBUG or ZFS_DEBUG explicitly. DEBUG used to enable witness(9) tracking of OpenSolaris (mostly ZFS) locks if WITNESS option was set. Because that generated a lot of witness(9) reports and all of them were believed to be false positives, this is no longer done. New option OPENSOLARIS_WITNESS can be used to achieve the previous behavior. 20130806: Timer values in IPv6 data structures now use time_uptime instead of time_second. Although this is not a user-visible functional change, userland utilities which directly use them---ndp(8), rtadvd(8), and rtsold(8) in the base system---need to be updated to r253970 or later. 20130802: find -delete can now delete the pathnames given as arguments, instead of only files found below them or if the pathname did not contain any slashes. Formerly, the following error message would result: find: -delete: : relative path potentially not safe Deleting the pathnames given as arguments can be prevented without error messages using -mindepth 1 or by changing directory and passing "." as argument to find. This works in the old as well as the new version of find. 20130726: Behavior of devfs rules path matching has been changed. Pattern is now always matched against fully qualified devfs path and slash characters must be explicitly matched by slashes in pattern (FNM_PATHNAME). Rulesets involving devfs subdirectories must be reviewed. 20130716: The default ARM ABI has changed to the ARM EABI. The old ABI is incompatible with the ARM EABI and all programs and modules will need to be rebuilt to work with a new kernel. To keep using the old ABI ensure the WITHOUT_ARM_EABI knob is set. NOTE: Support for the old ABI will be removed in the future and users are advised to upgrade. 20130709: pkg_install has been disconnected from the build if you really need it you should add WITH_PKGTOOLS in your src.conf(5). 20130709: Most of network statistics structures were changed to be able keep 64-bits counters. Thus all tools, that work with networking statistics, must be rebuilt (netstat(1), bsnmpd(1), etc.) 20130618: Fix a bug that allowed a tracing process (e.g. gdb) to write to a memory-mapped file in the traced process's address space even if neither the traced process nor the tracing process had write access to that file. 20130615: CVS has been removed from the base system. An exact copy of the code is available from the devel/cvs port. 20130613: Some people report the following error after the switch to bmake: make: illegal option -- J usage: make [-BPSXeiknpqrstv] [-C directory] [-D variable] ... *** [buildworld] Error code 2 this likely due to an old instance of make in ${MAKEPATH} (${MAKEOBJDIRPREFIX}${.CURDIR}/make.${MACHINE}) which src/Makefile will use that blindly, if it exists, so if you see the above error: rm -rf `make -V MAKEPATH` should resolve it. 20130516: Use bmake by default. Whereas before one could choose to build with bmake via -DWITH_BMAKE one must now use -DWITHOUT_BMAKE to use the old make. The goal is to remove these knobs for 10-RELEASE. It is worth noting that bmake (like gmake) treats the command line as the unit of failure, rather than statements within the command line. Thus '(cd some/where && dosomething)' is safer than 'cd some/where; dosomething'. The '()' allows consistent behavior in parallel build. 20130429: Fix a bug that allows NFS clients to issue READDIR on files. 20130426: The WITHOUT_IDEA option has been removed because the IDEA patent expired. 20130426: The sysctl which controls TRIM support under ZFS has been renamed from vfs.zfs.trim_disable -> vfs.zfs.trim.enabled and has been enabled by default. 20130425: The mergemaster command now uses the default MAKEOBJDIRPREFIX rather than creating it's own in the temporary directory in order allow access to bootstrapped versions of tools such as install and mtree. When upgrading from version of FreeBSD where the install command does not support -l, you will need to install a new mergemaster command if mergemaster -p is required. This can be accomplished with the command (cd src/usr.sbin/mergemaster && make install). 20130404: Legacy ATA stack, disabled and replaced by new CAM-based one since FreeBSD 9.0, completely removed from the sources. Kernel modules atadisk and atapi*, user-level tools atacontrol and burncd are removed. Kernel option `options ATA_CAM` is now permanently enabled and removed. 20130319: SOCK_CLOEXEC and SOCK_NONBLOCK flags have been added to socket(2) and socketpair(2). Software, in particular Kerberos, may automatically detect and use these during building. The resulting binaries will not work on older kernels. 20130308: CTL_DISABLE has also been added to the sparc64 GENERIC (for further information, see the respective 20130304 entry). 20130304: Recent commits to callout(9) changed the size of struct callout, so the KBI is probably heavily disturbed. Also, some functions in callout(9)/sleep(9)/sleepqueue(9)/condvar(9) KPIs were replaced by macros. Every kernel module using it won't load, so rebuild is requested. The ctl device has been re-enabled in GENERIC for i386 and amd64, but does not initialize by default (because of the new CTL_DISABLE option) to save memory. To re-enable it, remove the CTL_DISABLE option from the kernel config file or set kern.cam.ctl.disable=0 in /boot/loader.conf. 20130301: The ctl device has been disabled in GENERIC for i386 and amd64. This was done due to the extra memory being allocated at system initialisation time by the ctl driver which was only used if a CAM target device was created. This makes a FreeBSD system unusable on 128MB or less of RAM. 20130208: A new compression method (lz4) has been merged to -HEAD. Please refer to zpool-features(7) for more information. Please refer to the "ZFS notes" section of this file for information on upgrading boot ZFS pools. 20130129: A BSD-licensed patch(1) variant has been added and is installed as bsdpatch, being the GNU version the default patch. To inverse the logic and use the BSD-licensed one as default, while having the GNU version installed as gnupatch, rebuild and install world with the WITH_BSD_PATCH knob set. 20130121: Due to the use of the new -l option to install(1) during build and install, you must take care not to directly set the INSTALL make variable in your /etc/make.conf, /etc/src.conf, or on the command line. If you wish to use the -C flag for all installs you may be able to add INSTALL+=-C to /etc/make.conf or /etc/src.conf. 20130118: The install(1) option -M has changed meaning and now takes an argument that is a file or path to append logs to. In the unlikely event that -M was the last option on the command line and the command line contained at least two files and a target directory the first file will have logs appended to it. The -M option served little practical purpose in the last decade so its use is expected to be extremely rare. 20121223: After switching to Clang as the default compiler some users of ZFS on i386 systems started to experience stack overflow kernel panics. Please consider using 'options KSTACK_PAGES=4' in such configurations. 20121222: GEOM_LABEL now mangles label names read from file system metadata. Mangling affect labels containing spaces, non-printable characters, '%' or '"'. Device names in /etc/fstab and other places may need to be updated. 20121217: By default, only the 10 most recent kernel dumps will be saved. To restore the previous behaviour (no limit on the number of kernel dumps stored in the dump directory) add the following line to /etc/rc.conf: savecore_flags="" 20121201: With the addition of auditdistd(8), a new auditdistd user is now required during installworld. "mergemaster -p" can be used to add the user prior to installworld, as documented in the handbook. 20121117: The sin6_scope_id member variable in struct sockaddr_in6 is now filled by the kernel before passing the structure to the userland via sysctl or routing socket. This means the KAME-specific embedded scope id in sin6_addr.s6_addr[2] is always cleared in userland application. This behavior can be controlled by net.inet6.ip6.deembed_scopeid. __FreeBSD_version is bumped to 1000025. 20121105: On i386 and amd64 systems WITH_CLANG_IS_CC is now the default. This means that the world and kernel will be compiled with clang and that clang will be installed as /usr/bin/cc, /usr/bin/c++, and /usr/bin/cpp. To disable this behavior and revert to building with gcc, compile with WITHOUT_CLANG_IS_CC. Really old versions of current may need to bootstrap WITHOUT_CLANG first if the clang build fails (its compatibility window doesn't extend to the 9 stable branch point). 20121102: The IPFIREWALL_FORWARD kernel option has been removed. Its functionality now turned on by default. 20121023: The ZERO_COPY_SOCKET kernel option has been removed and split into SOCKET_SEND_COW and SOCKET_RECV_PFLIP. NB: SOCKET_SEND_COW uses the VM page based copy-on-write mechanism which is not safe and may result in kernel crashes. NB: The SOCKET_RECV_PFLIP mechanism is useless as no current driver supports disposeable external page sized mbuf storage. Proper replacements for both zero-copy mechanisms are under consideration and will eventually lead to complete removal of the two kernel options. 20121023: The IPv4 network stack has been converted to network byte order. The following modules need to be recompiled together with kernel: carp(4), divert(4), gif(4), siftr(4), gre(4), pf(4), ipfw(4), ng_ipfw(4), stf(4). 20121022: Support for non-MPSAFE filesystems was removed from VFS. The VFS_VERSION was bumped, all filesystem modules shall be recompiled. 20121018: All the non-MPSAFE filesystems have been disconnected from the build. The full list includes: codafs, hpfs, ntfs, nwfs, portalfs, smbfs, xfs. 20121016: The interface cloning API and ABI has changed. The following modules need to be recompiled together with kernel: ipfw(4), pfsync(4), pflog(4), usb(4), wlan(4), stf(4), vlan(4), disc(4), edsc(4), if_bridge(4), gif(4), tap(4), faith(4), epair(4), enc(4), tun(4), if_lagg(4), gre(4). 20121015: The sdhci driver was split in two parts: sdhci (generic SD Host Controller logic) and sdhci_pci (actual hardware driver). No kernel config modifications are required, but if you load sdhc as a module you must switch to sdhci_pci instead. 20121014: Import the FUSE kernel and userland support into base system. 20121013: The GNU sort(1) program has been removed since the BSD-licensed sort(1) has been the default for quite some time and no serious problems have been reported. The corresponding WITH_GNU_SORT knob has also gone. 20121006: The pfil(9) API/ABI for AF_INET family has been changed. Packet filtering modules: pf(4), ipfw(4), ipfilter(4) need to be recompiled with new kernel. 20121001: The net80211(4) ABI has been changed to allow for improved driver PS-POLL and power-save support. All wireless drivers need to be recompiled to work with the new kernel. 20120913: The random(4) support for the VIA hardware random number generator (`PADLOCK') is no longer enabled unconditionally. Add the padlock_rng device in the custom kernel config if needed. The GENERIC kernels on i386 and amd64 do include the device, so the change only affects the custom kernel configurations. 20120908: The pf(4) packet filter ABI has been changed. pfctl(8) and snmp_pf module need to be recompiled to work with new kernel. 20120828: A new ZFS feature flag "com.delphix:empty_bpobj" has been merged to -HEAD. Pools that have empty_bpobj in active state can not be imported read-write with ZFS implementations that do not support this feature. For more information read the zpool-features(5) manual page. 20120727: The sparc64 ZFS loader has been changed to no longer try to auto- detect ZFS providers based on diskN aliases but now requires these to be explicitly listed in the OFW boot-device environment variable. 20120712: The OpenSSL has been upgraded to 1.0.1c. Any binaries requiring libcrypto.so.6 or libssl.so.6 must be recompiled. Also, there are configuration changes. Make sure to merge /etc/ssl/openssl.cnf. 20120712: The following sysctls and tunables have been renamed for consistency with other variables: kern.cam.da.da_send_ordered -> kern.cam.da.send_ordered kern.cam.ada.ada_send_ordered -> kern.cam.ada.send_ordered 20120628: The sort utility has been replaced with BSD sort. For now, GNU sort is also available as "gnusort" or the default can be set back to GNU sort by setting WITH_GNU_SORT. In this case, BSD sort will be installed as "bsdsort". 20120611: A new version of ZFS (pool version 5000) has been merged to -HEAD. Starting with this version the old system of ZFS pool versioning is superseded by "feature flags". This concept enables forward compatibility against certain future changes in functionality of ZFS pools. The first read-only compatible "feature flag" for ZFS pools is named "com.delphix:async_destroy". For more information read the new zpool-features(5) manual page. Please refer to the "ZFS notes" section of this file for information on upgrading boot ZFS pools. 20120417: The malloc(3) implementation embedded in libc now uses sources imported as contrib/jemalloc. The most disruptive API change is to /etc/malloc.conf. If your system has an old-style /etc/malloc.conf, delete it prior to installworld, and optionally re-create it using the new format after rebooting. See malloc.conf(5) for details (specifically the TUNING section and the "opt.*" entries in the MALLCTL NAMESPACE section). 20120328: Big-endian MIPS TARGET_ARCH values no longer end in "eb". mips64eb is now spelled mips64. mipsn32eb is now spelled mipsn32. mipseb is now spelled mips. This is to aid compatibility with third-party software that expects this naming scheme in uname(3). Little-endian settings are unchanged. If you are updating a big-endian mips64 machine from before this change, you may need to set MACHINE_ARCH=mips64 in your environment before the new build system will recognize your machine. 20120306: Disable by default the option VFS_ALLOW_NONMPSAFE for all supported platforms. 20120229: Now unix domain sockets behave "as expected" on nullfs(5). Previously nullfs(5) did not pass through all behaviours to the underlying layer, as a result if we bound to a socket on the lower layer we could connect only to the lower path; if we bound to the upper layer we could connect only to the upper path. The new behavior is one can connect to both the lower and the upper paths regardless what layer path one binds to. 20120211: The getifaddrs upgrade path broken with 20111215 has been restored. If you have upgraded in between 20111215 and 20120209 you need to recompile libc again with your kernel. You still need to recompile world to be able to configure CARP but this restriction already comes from 20111215. 20120114: The set_rcvar() function has been removed from /etc/rc.subr. All base and ports rc.d scripts have been updated, so if you have a port installed with a script in /usr/local/etc/rc.d you can either hand-edit the rcvar= line, or reinstall the port. An easy way to handle the mass-update of /etc/rc.d: rm /etc/rc.d/* && mergemaster -i 20120109: panic(9) now stops other CPUs in the SMP systems, disables interrupts on the current CPU and prevents other threads from running. This behavior can be reverted using the kern.stop_scheduler_on_panic tunable/sysctl. The new behavior can be incompatible with kern.sync_on_panic. 20111215: The carp(4) facility has been changed significantly. Configuration of the CARP protocol via ifconfig(8) has changed, as well as format of CARP events submitted to devd(8) has changed. See manual pages for more information. The arpbalance feature of carp(4) is currently not supported anymore. Size of struct in_aliasreq, struct in6_aliasreq has changed. User utilities using SIOCAIFADDR, SIOCAIFADDR_IN6, e.g. ifconfig(8), need to be recompiled. 20111122: The acpi_wmi(4) status device /dev/wmistat has been renamed to /dev/wmistat0. 20111108: The option VFS_ALLOW_NONMPSAFE option has been added in order to explicitely support non-MPSAFE filesystems. It is on by default for all supported platform at this present time. 20111101: The broken amd(4) driver has been replaced with esp(4) in the amd64, i386 and pc98 GENERIC kernel configuration files. 20110930: sysinstall has been removed 20110923: The stable/9 branch created in subversion. This corresponds to the RELENG_9 branch in CVS. COMMON ITEMS: General Notes ------------- Avoid using make -j when upgrading. While generally safe, there are sometimes problems using -j to upgrade. If your upgrade fails with -j, please try again without -j. From time to time in the past there have been problems using -j with buildworld and/or installworld. This is especially true when upgrading between "distant" versions (eg one that cross a major release boundary or several minor releases, or when several months have passed on the -current branch). Sometimes, obscure build problems are the result of environment poisoning. This can happen because the make utility reads its environment when searching for values for global variables. To run your build attempts in an "environmental clean room", prefix all make commands with 'env -i '. See the env(1) manual page for more details. When upgrading from one major version to another it is generally best to upgrade to the latest code in the currently installed branch first, then do an upgrade to the new branch. This is the best-tested upgrade path, and has the highest probability of being successful. Please try this approach before reporting problems with a major version upgrade. When upgrading a live system, having a root shell around before installing anything can help undo problems. Not having a root shell around can lead to problems if pam has changed too much from your starting point to allow continued authentication after the upgrade. This file should be read as a log of events. When a later event changes information of a prior event, the prior event should not be deleted. Instead, a pointer to the entry with the new information should be placed in the old entry. Readers of this file should also sanity check older entries before relying on them blindly. Authors of new entries should write them with this in mind. ZFS notes --------- When upgrading the boot ZFS pool to a new version, always follow these two steps: 1.) recompile and reinstall the ZFS boot loader and boot block (this is part of "make buildworld" and "make installworld") 2.) update the ZFS boot block on your boot drive The following example updates the ZFS boot block on the first partition (freebsd-boot) of a GPT partitioned drive ada0: "gpart bootcode -p /boot/gptzfsboot -i 1 ada0" Non-boot pools do not need these updates. To build a kernel ----------------- If you are updating from a prior version of FreeBSD (even one just a few days old), you should follow this procedure. It is the most failsafe as it uses a /usr/obj tree with a fresh mini-buildworld, make kernel-toolchain make -DALWAYS_CHECK_MAKE buildkernel KERNCONF=YOUR_KERNEL_HERE make -DALWAYS_CHECK_MAKE installkernel KERNCONF=YOUR_KERNEL_HERE To test a kernel once --------------------- If you just want to boot a kernel once (because you are not sure if it works, or if you want to boot a known bad kernel to provide debugging information) run make installkernel KERNCONF=YOUR_KERNEL_HERE KODIR=/boot/testkernel nextboot -k testkernel To just build a kernel when you know that it won't mess you up -------------------------------------------------------------- This assumes you are already running a CURRENT system. Replace ${arch} with the architecture of your machine (e.g. "i386", "arm", "amd64", "ia64", "pc98", "sparc64", "powerpc", "mips", etc). cd src/sys/${arch}/conf config KERNEL_NAME_HERE cd ../compile/KERNEL_NAME_HERE make depend make make install If this fails, go to the "To build a kernel" section. To rebuild everything and install it on the current system. ----------------------------------------------------------- # Note: sometimes if you are running current you gotta do more than # is listed here if you are upgrading from a really old current. make buildworld make kernel KERNCONF=YOUR_KERNEL_HERE [1] [3] mergemaster -Fp [5] make installworld mergemaster -Fi [4] make delete-old [6] To cross-install current onto a separate partition -------------------------------------------------- # In this approach we use a separate partition to hold # current's root, 'usr', and 'var' directories. A partition # holding "/", "/usr" and "/var" should be about 2GB in # size. make buildworld make buildkernel KERNCONF=YOUR_KERNEL_HERE make installworld DESTDIR=${CURRENT_ROOT} -DDB_FROM_SRC make distribution DESTDIR=${CURRENT_ROOT} # if newfs'd make installkernel KERNCONF=YOUR_KERNEL_HERE DESTDIR=${CURRENT_ROOT} cp /etc/fstab ${CURRENT_ROOT}/etc/fstab # if newfs'd To upgrade in-place from stable to current ---------------------------------------------- make buildworld [9] make kernel KERNCONF=YOUR_KERNEL_HERE [8] [1] [3] mergemaster -Fp [5] make installworld mergemaster -Fi [4] make delete-old [6] Make sure that you've read the UPDATING file to understand the tweaks to various things you need. At this point in the life cycle of current, things change often and you are on your own to cope. The defaults can also change, so please read ALL of the UPDATING entries. Also, if you are tracking -current, you must be subscribed to freebsd-current@freebsd.org. Make sure that before you update your sources that you have read and understood all the recent messages there. If in doubt, please track -stable which has much fewer pitfalls. [1] If you have third party modules, such as vmware, you should disable them at this point so they don't crash your system on reboot. [3] From the bootblocks, boot -s, and then do fsck -p mount -u / mount -a cd src adjkerntz -i # if CMOS is wall time Also, when doing a major release upgrade, it is required that you boot into single user mode to do the installworld. [4] Note: This step is non-optional. Failure to do this step can result in a significant reduction in the functionality of the system. Attempting to do it by hand is not recommended and those that pursue this avenue should read this file carefully, as well as the archives of freebsd-current and freebsd-hackers mailing lists for potential gotchas. The -U option is also useful to consider. See mergemaster(8) for more information. [5] Usually this step is a noop. However, from time to time you may need to do this if you get unknown user in the following step. It never hurts to do it all the time. You may need to install a new mergemaster (cd src/usr.sbin/mergemaster && make install) after the buildworld before this step if you last updated from current before 20130425 or from -stable before 20130430. [6] This only deletes old files and directories. Old libraries can be deleted by "make delete-old-libs", but you have to make sure that no program is using those libraries anymore. [8] In order to have a kernel that can run the 4.x binaries needed to do an installworld, you must include the COMPAT_FREEBSD4 option in your kernel. Failure to do so may leave you with a system that is hard to boot to recover. A similar kernel option COMPAT_FREEBSD5 is required to run the 5.x binaries on more recent kernels. And so on for COMPAT_FREEBSD6 and COMPAT_FREEBSD7. Make sure that you merge any new devices from GENERIC since the last time you updated your kernel config file. [9] When checking out sources, you must include the -P flag to have cvs prune empty directories. If CPUTYPE is defined in your /etc/make.conf, make sure to use the "?=" instead of the "=" assignment operator, so that buildworld can override the CPUTYPE if it needs to. MAKEOBJDIRPREFIX must be defined in an environment variable, and not on the command line, or in /etc/make.conf. buildworld will warn if it is improperly defined. FORMAT: This file contains a list, in reverse chronological order, of major breakages in tracking -current. It is not guaranteed to be a complete list of such breakages, and only contains entries since September 23, 2011. If you need to see UPDATING entries from before that date, you will need to fetch an UPDATING file from an older FreeBSD release. Copyright information: Copyright 1998-2009 M. Warner Losh. All Rights Reserved. Redistribution, publication, translation and use, with or without modification, in full or in part, in any form or format of this document are permitted without further permission from the author. THIS DOCUMENT IS PROVIDED BY WARNER LOSH ``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 WARNER LOSH 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. Contact Warner Losh if you have any questions about your use of this document. $FreeBSD$ Index: releng/11.1/contrib/wpa/src/ap/wpa_auth.c =================================================================== --- releng/11.1/contrib/wpa/src/ap/wpa_auth.c (revision 324698) +++ releng/11.1/contrib/wpa/src/ap/wpa_auth.c (revision 324699) @@ -1,3500 +1,3526 @@ /* * IEEE 802.11 RSN / WPA Authenticator * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "utils/state_machine.h" #include "utils/bitfield.h" #include "common/ieee802_11_defs.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "eapol_auth/eapol_auth_sm.h" #include "ap_config.h" #include "ieee802_11.h" #include "wpa_auth.h" #include "pmksa_cache_auth.h" #include "wpa_auth_i.h" #include "wpa_auth_ie.h" #define STATE_MACHINE_DATA struct wpa_state_machine #define STATE_MACHINE_DEBUG_PREFIX "WPA" #define STATE_MACHINE_ADDR sm->addr static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_sm_step(struct wpa_state_machine *sm); static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, size_t data_len); static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_request_new_ptk(struct wpa_state_machine *sm); static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, struct wpa_ptk *ptk); static void wpa_group_free(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_get(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_put(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; static const u32 eapol_key_timeout_first = 100; /* ms */ static const u32 eapol_key_timeout_subseq = 1000; /* ms */ static const u32 eapol_key_timeout_first_group = 500; /* ms */ /* TODO: make these configurable */ static const int dot11RSNAConfigPMKLifetime = 43200; static const int dot11RSNAConfigPMKReauthThreshold = 70; static const int dot11RSNAConfigSATimeout = 60; static inline int wpa_auth_mic_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.mic_failure_report) return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); return 0; } static inline void wpa_auth_psk_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.psk_failure_report) wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr); } static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var, int value) { if (wpa_auth->cb.set_eapol) wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); } static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var) { if (wpa_auth->cb.get_eapol == NULL) return -1; return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); } static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk) { if (wpa_auth->cb.get_psk == NULL) return NULL; return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr, prev_psk); } static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth, const u8 *addr, u8 *msk, size_t *len) { if (wpa_auth->cb.get_msk == NULL) return -1; return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len); } static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { if (wpa_auth->cb.set_key == NULL) return -1; return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, key, key_len); } static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, const u8 *addr, int idx, u8 *seq) { if (wpa_auth->cb.get_seqnum == NULL) return -1; return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); } static inline int wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *data, size_t data_len, int encrypt) { if (wpa_auth->cb.send_eapol == NULL) return -1; return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, encrypt); } #ifdef CONFIG_MESH static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.start_ampe == NULL) return -1; return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr); } #endif /* CONFIG_MESH */ int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) { if (wpa_auth->cb.for_each_sta == NULL) return 0; return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); } int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx) { if (wpa_auth->cb.for_each_auth == NULL) return 0; return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); } void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *txt) { if (wpa_auth->cb.logger == NULL) return; wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); } void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *fmt, ...) { char *format; int maxlen; va_list ap; if (wpa_auth->cb.logger == NULL) return; maxlen = os_strlen(fmt) + 100; format = os_malloc(maxlen); if (!format) return; va_start(ap, fmt); vsnprintf(format, maxlen, fmt, ap); va_end(ap); wpa_auth_logger(wpa_auth, addr, level, format); os_free(format); } static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.disconnect == NULL) return; wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr)); wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } static int wpa_use_aes_cmac(struct wpa_state_machine *sm) { int ret = 0; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) ret = 1; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) ret = 1; #endif /* CONFIG_IEEE80211W */ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) ret = 1; return ret; } static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); } else { wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); wpa_hexdump_key(MSG_DEBUG, "GMK", wpa_auth->group->GMK, WPA_GMK_LEN); } if (wpa_auth->conf.wpa_gmk_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, wpa_rekey_gmk, wpa_auth, NULL); } } static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_group *group, *next; wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); group = wpa_auth->group; while (group) { wpa_group_get(wpa_auth, group); group->GTKReKey = TRUE; do { group->changed = FALSE; wpa_group_sm_step(wpa_auth, group); } while (group->changed); next = group->next; wpa_group_put(wpa_auth, group); group = next; } if (wpa_auth->conf.wpa_group_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, wpa_rekey_gtk, wpa_auth, NULL); } } static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_state_machine *sm = timeout_ctx; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK"); wpa_request_new_ptk(sm); wpa_sm_step(sm); } static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) { if (sm->pmksa == ctx) sm->pmksa = NULL; return 0; } static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, void *ctx) { struct wpa_authenticator *wpa_auth = ctx; wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); } static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)]; u8 rkey[32]; unsigned long ptr; if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN); /* * Counter = PRF-256(Random number, "Init Counter", * Local MAC Address || Time) */ os_memcpy(buf, wpa_auth->addr, ETH_ALEN); wpa_get_ntp_timestamp(buf + ETH_ALEN); ptr = (unsigned long) group; os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr)); if (random_get_bytes(rkey, sizeof(rkey)) < 0) return -1; if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), group->Counter, WPA_NONCE_LEN) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "Key Counter", group->Counter, WPA_NONCE_LEN); return 0; } static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, int vlan_id, int delay_init) { struct wpa_group *group; group = os_zalloc(sizeof(struct wpa_group)); if (group == NULL) return NULL; group->GTKAuthenticator = TRUE; group->vlan_id = vlan_id; group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); if (random_pool_ready() != 1) { wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " "for secure operations - update keys later when " "the first station connects"); } /* * Set initial GMK/Counter value here. The actual values that will be * used in negotiations will be set once the first station tries to * connect. This allows more time for collecting additional randomness * on embedded devices. */ if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); os_free(group); return NULL; } group->GInit = TRUE; if (delay_init) { wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start " "until Beacon frames have been configured"); /* Initialization is completed in wpa_init_keys(). */ } else { wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; wpa_group_sm_step(wpa_auth, group); } return group; } /** * wpa_init - Initialize WPA authenticator * @addr: Authenticator address * @conf: Configuration for WPA authenticator * @cb: Callback functions for WPA authenticator * Returns: Pointer to WPA authenticator data or %NULL on failure */ struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, struct wpa_auth_callbacks *cb) { struct wpa_authenticator *wpa_auth; wpa_auth = os_zalloc(sizeof(struct wpa_authenticator)); if (wpa_auth == NULL) return NULL; os_memcpy(wpa_auth->addr, addr, ETH_ALEN); os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); os_memcpy(&wpa_auth->cb, cb, sizeof(*cb)); if (wpa_auth_gen_wpa_ie(wpa_auth)) { wpa_printf(MSG_ERROR, "Could not generate WPA IE."); os_free(wpa_auth); return NULL; } wpa_auth->group = wpa_group_init(wpa_auth, 0, 1); if (wpa_auth->group == NULL) { os_free(wpa_auth->wpa_ie); os_free(wpa_auth); return NULL; } wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb, wpa_auth); if (wpa_auth->pmksa == NULL) { wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); os_free(wpa_auth->group); os_free(wpa_auth->wpa_ie); os_free(wpa_auth); return NULL; } #ifdef CONFIG_IEEE80211R wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); if (wpa_auth->ft_pmk_cache == NULL) { wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); os_free(wpa_auth->group); os_free(wpa_auth->wpa_ie); pmksa_cache_auth_deinit(wpa_auth->pmksa); os_free(wpa_auth); return NULL; } #endif /* CONFIG_IEEE80211R */ if (wpa_auth->conf.wpa_gmk_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, wpa_rekey_gmk, wpa_auth, NULL); } if (wpa_auth->conf.wpa_group_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, wpa_rekey_gtk, wpa_auth, NULL); } #ifdef CONFIG_P2P if (WPA_GET_BE32(conf->ip_addr_start)) { int count = WPA_GET_BE32(conf->ip_addr_end) - WPA_GET_BE32(conf->ip_addr_start) + 1; if (count > 1000) count = 1000; if (count > 0) wpa_auth->ip_pool = bitfield_alloc(count); } #endif /* CONFIG_P2P */ return wpa_auth; } int wpa_init_keys(struct wpa_authenticator *wpa_auth) { struct wpa_group *group = wpa_auth->group; wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial " "keys"); wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; wpa_group_sm_step(wpa_auth, group); if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) return -1; return 0; } /** * wpa_deinit - Deinitialize WPA authenticator * @wpa_auth: Pointer to WPA authenticator data from wpa_init() */ void wpa_deinit(struct wpa_authenticator *wpa_auth) { struct wpa_group *group, *prev; eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); #ifdef CONFIG_PEERKEY while (wpa_auth->stsl_negotiations) wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); #endif /* CONFIG_PEERKEY */ pmksa_cache_auth_deinit(wpa_auth->pmksa); #ifdef CONFIG_IEEE80211R wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); wpa_auth->ft_pmk_cache = NULL; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P bitfield_free(wpa_auth->ip_pool); #endif /* CONFIG_P2P */ os_free(wpa_auth->wpa_ie); group = wpa_auth->group; while (group) { prev = group; group = group->next; os_free(prev); } os_free(wpa_auth); } /** * wpa_reconfig - Update WPA authenticator configuration * @wpa_auth: Pointer to WPA authenticator data from wpa_init() * @conf: Configuration for WPA authenticator */ int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_auth_config *conf) { struct wpa_group *group; if (wpa_auth == NULL) return 0; os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); if (wpa_auth_gen_wpa_ie(wpa_auth)) { wpa_printf(MSG_ERROR, "Could not generate WPA IE."); return -1; } /* * Reinitialize GTK to make sure it is suitable for the new * configuration. */ group = wpa_auth->group; group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); group->GInit = TRUE; wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; wpa_group_sm_step(wpa_auth, group); return 0; } struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr) { struct wpa_state_machine *sm; if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) return NULL; sm = os_zalloc(sizeof(struct wpa_state_machine)); if (sm == NULL) return NULL; os_memcpy(sm->addr, addr, ETH_ALEN); if (p2p_dev_addr) os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); sm->wpa_auth = wpa_auth; sm->group = wpa_auth->group; wpa_group_get(sm->wpa_auth, sm->group); return sm; } int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm) { if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return -1; #ifdef CONFIG_IEEE80211R if (sm->ft_completed) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "FT authentication already completed - do not " "start 4-way handshake"); /* Go to PTKINITDONE state to allow GTK rekeying */ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; return 0; } #endif /* CONFIG_IEEE80211R */ if (sm->started) { os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); sm->ReAuthenticationRequest = TRUE; return wpa_sm_step(sm); } wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "start authentication"); sm->started = 1; sm->Init = TRUE; if (wpa_sm_step(sm) == 1) return 1; /* should not really happen */ sm->Init = FALSE; sm->AuthenticationRequest = TRUE; return wpa_sm_step(sm); } void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) { /* WPA/RSN was not used - clear WPA state. This is needed if the STA * reassociates back to the same AP while the previous entry for the * STA has not yet been removed. */ if (sm == NULL) return; sm->wpa_key_mgmt = 0; } static void wpa_free_sta_sm(struct wpa_state_machine *sm) { #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr)) { u32 start; wpa_printf(MSG_DEBUG, "P2P: Free assigned IP " "address %u.%u.%u.%u from " MACSTR, sm->ip_addr[0], sm->ip_addr[1], sm->ip_addr[2], sm->ip_addr[3], MAC2STR(sm->addr)); start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start); bitfield_clear(sm->wpa_auth->ip_pool, WPA_GET_BE32(sm->ip_addr) - start); } #endif /* CONFIG_P2P */ if (sm->GUpdateStationKeys) { sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; } #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ftie); wpabuf_free(sm->ft_pending_req_ies); #endif /* CONFIG_IEEE80211R */ os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); wpa_group_put(sm->wpa_auth, sm->group); os_free(sm); } void wpa_auth_sta_deinit(struct wpa_state_machine *sm) { if (sm == NULL) return; if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "strict rekeying - force GTK rekey since STA " "is leaving"); eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, NULL); } eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); if (sm->in_step_loop) { /* Must not free state machine while wpa_sm_step() is running. * Freeing will be completed in the end of wpa_sm_step(). */ wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state " "machine deinit for " MACSTR, MAC2STR(sm->addr)); sm->pending_deinit = 1; } else wpa_free_sta_sm(sm); } static void wpa_request_new_ptk(struct wpa_state_machine *sm) { if (sm == NULL) return; sm->PTKRequest = TRUE; sm->PTK_valid = 0; } static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr, const u8 *replay_counter) { int i; for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { if (!ctr[i].valid) break; if (os_memcmp(replay_counter, ctr[i].counter, WPA_REPLAY_COUNTER_LEN) == 0) return 1; } return 0; } static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr, const u8 *replay_counter) { int i; for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { if (ctr[i].valid && (replay_counter == NULL || os_memcmp(replay_counter, ctr[i].counter, WPA_REPLAY_COUNTER_LEN) == 0)) ctr[i].valid = FALSE; } } #ifdef CONFIG_IEEE80211R static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_ie_parse *kde) { struct wpa_ie_data ie; struct rsn_mdie *mdie; if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 || ie.num_pmkid != 1 || ie.pmkid == NULL) { wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in " "FT 4-way handshake message 2/4"); return -1; } os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant", sm->sup_pmk_r1_name, PMKID_LEN); if (!kde->mdie || !kde->ftie) { wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake " "message 2/4", kde->mdie ? "FTIE" : "MDIE"); return -1; } mdie = (struct rsn_mdie *) (kde->mdie + 2); if (kde->mdie[1] < sizeof(struct rsn_mdie) || os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: MDIE mismatch"); return -1; } if (sm->assoc_resp_ftie && (kde->ftie[1] != sm->assoc_resp_ftie[1] || os_memcmp(kde->ftie, sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]) != 0)) { wpa_printf(MSG_DEBUG, "FT: FTIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4", kde->ftie, kde->ftie_len); wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp", sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]); return -1; } return 0; } #endif /* CONFIG_IEEE80211R */ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int group) { /* Supplicant reported a Michael MIC error */ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Error Request " "(STA detected Michael MIC failure (group=%d))", group); if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "ignore Michael MIC failure report since " "group cipher is not TKIP"); } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "ignore Michael MIC failure report since " "pairwise cipher is not TKIP"); } else { if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0) return 1; /* STA entry was removed */ sm->dot11RSNAStatsTKIPRemoteMICFailures++; wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; } /* * Error report is not a request for a new key handshake, but since * Authenticator may do it, let's change the keys now anyway. */ wpa_request_new_ptk(sm); return 0; } static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, size_t data_len) { struct wpa_ptk PTK; int ok = 0; const u8 *pmk = NULL; for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, pmk); if (pmk == NULL) break; } else pmk = sm->PMK; wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK); if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) == 0) { ok = 1; break; } if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) break; } if (!ok) { wpa_printf(MSG_DEBUG, "WPA: Earlier SNonce did not result in matching MIC"); return -1; } wpa_printf(MSG_DEBUG, "WPA: Earlier SNonce resulted in matching MIC"); sm->alt_snonce_valid = 0; os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN); os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); sm->PTK_valid = TRUE; return 0; } void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; struct wpa_eapol_key_192 *key192; u16 key_info, key_data_length; enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, SMK_M1, SMK_M3, SMK_ERROR } msg; char *msgtxt; struct wpa_eapol_ie_parse kde; int ft; const u8 *eapol_key_ie, *key_data; size_t eapol_key_ie_len, keyhdrlen, mic_len; if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; mic_len = wpa_mic_len(sm->wpa_key_mgmt); keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); if (data_len < sizeof(*hdr) + keyhdrlen) return; hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); key192 = (struct wpa_eapol_key_192 *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); if (mic_len == 24) { key_data = (const u8 *) (key192 + 1); key_data_length = WPA_GET_BE16(key192->key_data_length); } else { key_data = (const u8 *) (key + 1); key_data_length = WPA_GET_BE16(key->key_data_length); } wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR " key_info=0x%x type=%u key_data_length=%u", MAC2STR(sm->addr), key_info, key->type, key_data_length); if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) { wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " "key_data overflow (%d > %lu)", key_data_length, (unsigned long) (data_len - sizeof(*hdr) - keyhdrlen)); return; } if (sm->wpa == WPA_VERSION_WPA2) { if (key->type == EAPOL_KEY_TYPE_WPA) { /* * Some deployed station implementations seem to send * msg 4/4 with incorrect type value in WPA2 mode. */ wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key " "with unexpected WPA type in RSN mode"); } else if (key->type != EAPOL_KEY_TYPE_RSN) { wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " "unexpected type %d in RSN mode", key->type); return; } } else { if (key->type != EAPOL_KEY_TYPE_WPA) { wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " "unexpected type %d in WPA mode", key->type); return; } } wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { if (key_info & WPA_KEY_INFO_ERROR) { msg = SMK_ERROR; msgtxt = "SMK Error"; } else { msg = SMK_M1; msgtxt = "SMK M1"; } } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { msg = SMK_M3; msgtxt = "SMK M3"; } else if (key_info & WPA_KEY_INFO_REQUEST) { msg = REQUEST; msgtxt = "Request"; } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { msg = GROUP_2; msgtxt = "2/2 Group"; } else if (key_data_length == 0) { msg = PAIRWISE_4; msgtxt = "4/4 Pairwise"; } else { msg = PAIRWISE_2; msgtxt = "2/4 Pairwise"; } /* TODO: key_info type validation for PeerKey */ if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || msg == GROUP_2) { u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; if (sm->pairwise == WPA_CIPHER_CCMP || sm->pairwise == WPA_CIPHER_GCMP) { if (wpa_use_aes_cmac(sm) && sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN && !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "advertised support for " "AES-128-CMAC, but did not " "use it"); return; } if (!wpa_use_aes_cmac(sm) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use HMAC-SHA1-AES " "with CCMP/GCMP"); return; } } if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases"); return; } } if (key_info & WPA_KEY_INFO_REQUEST) { if (sm->req_replay_counter_used && os_memcmp(key->replay_counter, sm->req_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "received EAPOL-Key request with " "replayed counter"); return; } } if (!(key_info & WPA_KEY_INFO_REQUEST) && !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) { int i; if (msg == PAIRWISE_2 && wpa_replay_counter_valid(sm->prev_key_replay, key->replay_counter) && sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) { /* * Some supplicant implementations (e.g., Windows XP * WZC) update SNonce for each EAPOL-Key 2/4. This * breaks the workaround on accepting any of the * pending requests, so allow the SNonce to be updated * even if we have already sent out EAPOL-Key 3/4. */ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "Process SNonce update from STA " "based on retransmitted EAPOL-Key " "1/4"); sm->update_snonce = 1; os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN); sm->alt_snonce_valid = TRUE; os_memcpy(sm->alt_replay_counter, sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); goto continue_processing; } if (msg == PAIRWISE_4 && sm->alt_snonce_valid && sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && os_memcmp(key->replay_counter, sm->alt_replay_counter, WPA_REPLAY_COUNTER_LEN) == 0) { /* * Supplicant may still be using the old SNonce since * there was two EAPOL-Key 2/4 messages and they had * different SNonce values. */ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4"); goto continue_processing; } if (msg == PAIRWISE_2 && wpa_replay_counter_valid(sm->prev_key_replay, key->replay_counter) && sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "ignore retransmitted EAPOL-Key %s - " "SNonce did not change", msgtxt); } else { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "received EAPOL-Key %s with " "unexpected replay counter", msgtxt); } for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { if (!sm->key_replay[i].valid) break; wpa_hexdump(MSG_DEBUG, "pending replay counter", sm->key_replay[i].counter, WPA_REPLAY_COUNTER_LEN); } wpa_hexdump(MSG_DEBUG, "received replay counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); return; } continue_processing: switch (msg) { case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING && (!sm->update_snonce || sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/4 in " "invalid state (%d) - dropped", sm->wpa_ptk_state); return; } random_add_randomness(key->key_nonce, WPA_NONCE_LEN); if (sm->group->reject_4way_hs_for_entropy) { /* * The system did not have enough entropy to generate * strong random numbers. Reject the first 4-way * handshake(s) and collect some entropy based on the * information from it. Once enough entropy is * available, the next atempt will trigger GMK/Key * Counter update and the station will be allowed to * continue. */ wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to " "collect more entropy for random number " "generation"); random_mark_pool_ready(); wpa_sta_disconnect(wpa_auth, sm->addr); return; } if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/4 with " "invalid Key Data contents"); return; } if (kde.rsn_ie) { eapol_key_ie = kde.rsn_ie; eapol_key_ie_len = kde.rsn_ie_len; } else if (kde.osen) { eapol_key_ie = kde.osen; eapol_key_ie_len = kde.osen_len; } else { eapol_key_ie = kde.wpa_ie; eapol_key_ie_len = kde.wpa_ie_len; } ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt); if (sm->wpa_ie == NULL || wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len, eapol_key_ie, eapol_key_ie_len)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "WPA IE from (Re)AssocReq did not " "match with msg 2/4"); if (sm->wpa_ie) { wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", sm->wpa_ie, sm->wpa_ie_len); } wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", eapol_key_ie, eapol_key_ie_len); /* MLME-DEAUTHENTICATE.request */ wpa_sta_disconnect(wpa_auth, sm->addr); return; } #ifdef CONFIG_IEEE80211R if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { wpa_sta_disconnect(wpa_auth, sm->addr); return; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P if (kde.ip_addr_req && kde.ip_addr_req[0] && wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { int idx; wpa_printf(MSG_DEBUG, "P2P: IP address requested in " "EAPOL-Key exchange"); idx = bitfield_get_first_zero(wpa_auth->ip_pool); if (idx >= 0) { u32 start = WPA_GET_BE32(wpa_auth->conf. ip_addr_start); bitfield_set(wpa_auth->ip_pool, idx); WPA_PUT_BE32(sm->ip_addr, start + idx); wpa_printf(MSG_DEBUG, "P2P: Assigned IP " "address %u.%u.%u.%u to " MACSTR, sm->ip_addr[0], sm->ip_addr[1], sm->ip_addr[2], sm->ip_addr[3], MAC2STR(sm->addr)); } } #endif /* CONFIG_P2P */ break; case PAIRWISE_4: if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || !sm->PTK_valid) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 4/4 in " "invalid state (%d) - dropped", sm->wpa_ptk_state); return; } break; case GROUP_2: if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING || !sm->PTK_valid) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/2 in " "invalid state (%d) - dropped", sm->wpa_ptk_group_state); return; } break; #ifdef CONFIG_PEERKEY case SMK_M1: case SMK_M3: case SMK_ERROR: if (!wpa_auth->conf.peerkey) { wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " "PeerKey use disabled - ignoring message"); return; } if (!sm->PTK_valid) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg SMK in " "invalid state - dropped"); return; } break; #else /* CONFIG_PEERKEY */ case SMK_M1: case SMK_M3: case SMK_ERROR: return; /* STSL disabled - ignore SMK messages */ #endif /* CONFIG_PEERKEY */ case REQUEST: break; } wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "received EAPOL-Key frame (%s)", msgtxt); if (key_info & WPA_KEY_INFO_ACK) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received invalid EAPOL-Key: Key Ack set"); return; } if (!(key_info & WPA_KEY_INFO_MIC)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received invalid EAPOL-Key: Key MIC not set"); return; } sm->MICVerified = FALSE; if (sm->PTK_valid && !sm->update_snonce) { if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data, data_len) && (msg != PAIRWISE_4 || !sm->alt_snonce_valid || wpa_try_alt_snonce(sm, data, data_len))) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); return; } sm->MICVerified = TRUE; eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); sm->pending_1_of_4_timeout = 0; } if (key_info & WPA_KEY_INFO_REQUEST) { if (sm->MICVerified) { sm->req_replay_counter_used = 1; os_memcpy(sm->req_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); } else { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key request with " "invalid MIC"); return; } /* * TODO: should decrypt key data field if encryption was used; * even though MAC address KDE is not normally encrypted, * supplicant is allowed to encrypt it. */ if (msg == SMK_ERROR) { #ifdef CONFIG_PEERKEY wpa_smk_error(wpa_auth, sm, key_data, key_data_length); #endif /* CONFIG_PEERKEY */ return; } else if (key_info & WPA_KEY_INFO_ERROR) { if (wpa_receive_error_report( wpa_auth, sm, !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) return; /* STA entry was removed */ } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for new " "4-Way Handshake"); wpa_request_new_ptk(sm); #ifdef CONFIG_PEERKEY } else if (msg == SMK_M1) { wpa_smk_m1(wpa_auth, sm, key, key_data, key_data_length); #endif /* CONFIG_PEERKEY */ } else if (key_data_length > 0 && wpa_parse_kde_ies(key_data, key_data_length, &kde) == 0 && kde.mac_addr) { } else { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for GTK " "rekeying"); eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); wpa_rekey_gtk(wpa_auth, NULL); } } else { /* Do not allow the same key replay counter to be reused. */ wpa_replay_counter_mark_invalid(sm->key_replay, key->replay_counter); if (msg == PAIRWISE_2) { /* * Maintain a copy of the pending EAPOL-Key frames in * case the EAPOL-Key frame was retransmitted. This is * needed to allow EAPOL-Key msg 2/4 reply to another * pending msg 1/4 to update the SNonce to work around * unexpected supplicant behavior. */ os_memcpy(sm->prev_key_replay, sm->key_replay, sizeof(sm->key_replay)); } else { os_memset(sm->prev_key_replay, 0, sizeof(sm->prev_key_replay)); } /* * Make sure old valid counters are not accepted anymore and * do not get copied again. */ wpa_replay_counter_mark_invalid(sm->key_replay, NULL); } #ifdef CONFIG_PEERKEY if (msg == SMK_M3) { wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length); return; } #endif /* CONFIG_PEERKEY */ os_free(sm->last_rx_eapol_key); sm->last_rx_eapol_key = os_malloc(data_len); if (sm->last_rx_eapol_key == NULL) return; os_memcpy(sm->last_rx_eapol_key, data, data_len); sm->last_rx_eapol_key_len = data_len; sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE); sm->EAPOLKeyReceived = TRUE; sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); wpa_sm_step(sm); } static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, const u8 *gnonce, u8 *gtk, size_t gtk_len) { u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16]; u8 *pos; int ret = 0; /* GTK = PRF-X(GMK, "Group key expansion", * AA || GNonce || Time || random data) * The example described in the IEEE 802.11 standard uses only AA and * GNonce as inputs here. Add some more entropy since this derivation * is done only at the Authenticator and as such, does not need to be * exactly same. */ os_memcpy(data, addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); pos = data + ETH_ALEN + WPA_NONCE_LEN; wpa_get_ntp_timestamp(pos); pos += 8; if (random_get_bytes(pos, 16) < 0) ret = -1; #ifdef CONFIG_IEEE80211W sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len); #else /* CONFIG_IEEE80211W */ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len) < 0) ret = -1; #endif /* CONFIG_IEEE80211W */ return ret; } static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_state_machine *sm = timeout_ctx; sm->pending_1_of_4_timeout = 0; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); sm->TimeoutEvt = TRUE; wpa_sm_step(sm); } void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int key_info, const u8 *key_rsc, const u8 *nonce, const u8 *kde, size_t kde_len, int keyidx, int encr, int force_version) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; struct wpa_eapol_key_192 *key192; size_t len, mic_len, keyhdrlen; int alg; int key_data_len, pad_len = 0; u8 *buf, *pos; int version, pairwise; int i; u8 *key_data; mic_len = wpa_mic_len(sm->wpa_key_mgmt); keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); len = sizeof(struct ieee802_1x_hdr) + keyhdrlen; if (force_version) version = force_version; else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AKM_DEFINED; else if (wpa_use_aes_cmac(sm)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise != WPA_CIPHER_TKIP) version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d " "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d " "encr=%d)", version, (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0, (key_info & WPA_KEY_INFO_MIC) ? 1 : 0, (key_info & WPA_KEY_INFO_ACK) ? 1 : 0, (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0, pairwise, (unsigned long) kde_len, keyidx, encr); key_data_len = kde_len; if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { pad_len = key_data_len % 8; if (pad_len) pad_len = 8 - pad_len; key_data_len += pad_len + 8; } len += key_data_len; hdr = os_zalloc(len); if (hdr == NULL) return; hdr->version = wpa_auth->conf.eapol_version; hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; hdr->length = host_to_be16(len - sizeof(*hdr)); key = (struct wpa_eapol_key *) (hdr + 1); key192 = (struct wpa_eapol_key_192 *) (hdr + 1); key_data = ((u8 *) (hdr + 1)) + keyhdrlen; key->type = sm->wpa == WPA_VERSION_WPA2 ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info |= version; if (encr && sm->wpa == WPA_VERSION_WPA2) key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; if (sm->wpa != WPA_VERSION_WPA2) key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; WPA_PUT_BE16(key->key_info, key_info); alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); if (key_info & WPA_KEY_INFO_SMK_MESSAGE) WPA_PUT_BE16(key->key_length, 0); /* FIX: STSL: what to use as key_replay_counter? */ for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) { sm->key_replay[i].valid = sm->key_replay[i - 1].valid; os_memcpy(sm->key_replay[i].counter, sm->key_replay[i - 1].counter, WPA_REPLAY_COUNTER_LEN); } inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); os_memcpy(key->replay_counter, sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); sm->key_replay[0].valid = TRUE; if (nonce) os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); if (key_rsc) os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); if (kde && !encr) { os_memcpy(key_data, kde, kde_len); if (mic_len == 24) WPA_PUT_BE16(key192->key_data_length, kde_len); else WPA_PUT_BE16(key->key_data_length, kde_len); } else if (encr && kde) { buf = os_zalloc(key_data_len); if (buf == NULL) { os_free(hdr); return; } pos = buf; os_memcpy(pos, kde, kde_len); pos += kde_len; if (pad_len) *pos++ = 0xdd; wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", buf, key_data_len); if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, (key_data_len - 8) / 8, buf, key_data)) { os_free(hdr); os_free(buf); return; } if (mic_len == 24) WPA_PUT_BE16(key192->key_data_length, key_data_len); else WPA_PUT_BE16(key->key_data_length, key_data_len); #ifndef CONFIG_NO_RC4 } else if (sm->PTK.kek_len == 16) { u8 ek[32]; os_memcpy(key->key_iv, sm->group->Counter + WPA_NONCE_LEN - 16, 16); inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len); os_memcpy(key_data, buf, key_data_len); rc4_skip(ek, 32, 256, key_data, key_data_len); if (mic_len == 24) WPA_PUT_BE16(key192->key_data_length, key_data_len); else WPA_PUT_BE16(key->key_data_length, key_data_len); #endif /* CONFIG_NO_RC4 */ } else { os_free(hdr); os_free(buf); return; } os_free(buf); } if (key_info & WPA_KEY_INFO_MIC) { u8 *key_mic; if (!sm->PTK_valid) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "PTK not valid when sending EAPOL-Key " "frame"); os_free(hdr); return; } key_mic = key192->key_mic; /* same offset for key and key192 */ wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, sm->wpa_key_mgmt, version, (u8 *) hdr, len, key_mic); #ifdef CONFIG_TESTING_OPTIONS if (!pairwise && wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 && drand48() < wpa_auth->conf.corrupt_gtk_rekey_mic_probability) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "Corrupting group EAPOL-Key Key MIC"); key_mic[0]++; } #endif /* CONFIG_TESTING_OPTIONS */ } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, 1); wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len, sm->pairwise_set); os_free(hdr); } static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int key_info, const u8 *key_rsc, const u8 *nonce, const u8 *kde, size_t kde_len, int keyidx, int encr) { int timeout_ms; int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; int ctr; if (sm == NULL) return; __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len, keyidx, encr, 0); ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; if (ctr == 1 && wpa_auth->conf.tx_status) timeout_ms = pairwise ? eapol_key_timeout_first : eapol_key_timeout_first_group; else timeout_ms = eapol_key_timeout_subseq; if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) sm->pending_1_of_4_timeout = 1; wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " "counter %d)", timeout_ms, ctr); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; struct wpa_eapol_key_192 *key192; u16 key_info; int ret = 0; u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; size_t mic_len = wpa_mic_len(akmp); if (data_len < sizeof(*hdr) + sizeof(*key)) return -1; hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); key192 = (struct wpa_eapol_key_192 *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); os_memcpy(mic, key192->key_mic, mic_len); os_memset(key192->key_mic, 0, mic_len); if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp, key_info & WPA_KEY_INFO_TYPE_MASK, data, data_len, key192->key_mic) || os_memcmp_const(mic, key192->key_mic, mic_len) != 0) ret = -1; os_memcpy(key192->key_mic, mic, mic_len); return ret; } void wpa_remove_ptk(struct wpa_state_machine *sm) { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0); sm->pairwise_set = FALSE; eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); } int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) { int remove_ptk = 1; if (sm == NULL) return -1; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "event %d notification", event); switch (event) { case WPA_AUTH: #ifdef CONFIG_MESH /* PTKs are derived through AMPE */ if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) { /* not mesh */ break; } return 0; #endif /* CONFIG_MESH */ case WPA_ASSOC: break; case WPA_DEAUTH: case WPA_DISASSOC: sm->DeauthenticationRequest = TRUE; break; case WPA_REAUTH: case WPA_REAUTH_EAPOL: if (!sm->started) { /* * When using WPS, we may end up here if the STA * manages to re-associate without the previous STA * entry getting removed. Consequently, we need to make * sure that the WPA state machines gets initialized * properly at this point. */ wpa_printf(MSG_DEBUG, "WPA state machine had not been " "started - initialize now"); sm->started = 1; sm->Init = TRUE; if (wpa_sm_step(sm) == 1) return 1; /* should not really happen */ sm->Init = FALSE; sm->AuthenticationRequest = TRUE; break; } if (sm->GUpdateStationKeys) { /* * Reauthentication cancels the pending group key * update for this STA. */ sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->PtkGroupInit = TRUE; } sm->ReAuthenticationRequest = TRUE; break; case WPA_ASSOC_FT: #ifdef CONFIG_IEEE80211R wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration " "after association"); wpa_ft_install_ptk(sm); /* Using FT protocol, not WPA auth state machine */ sm->ft_completed = 1; return 0; #else /* CONFIG_IEEE80211R */ break; #endif /* CONFIG_IEEE80211R */ } #ifdef CONFIG_IEEE80211R sm->ft_completed = 0; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (sm->mgmt_frame_prot && event == WPA_AUTH) remove_ptk = 0; #endif /* CONFIG_IEEE80211W */ if (remove_ptk) { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); if (event != WPA_REAUTH_EAPOL) wpa_remove_ptk(sm); } if (sm->in_step_loop) { /* * wpa_sm_step() is already running - avoid recursive call to * it by making the existing loop process the new update. */ sm->changed = TRUE; return 0; } return wpa_sm_step(sm); } SM_STATE(WPA_PTK, INITIALIZE) { SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); if (sm->Init) { /* Init flag is not cleared here, so avoid busy * loop by claiming nothing changed. */ sm->changed = FALSE; } sm->keycount = 0; if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = FALSE; if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and * Local AA > Remote AA)) */) { sm->Pair = TRUE; } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0); wpa_remove_ptk(sm); wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); sm->TimeoutCtr = 0; if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 0); } } SM_STATE(WPA_PTK, DISCONNECT) { SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); sm->Disconnect = FALSE; wpa_sta_disconnect(sm->wpa_auth, sm->addr); } SM_STATE(WPA_PTK, DISCONNECTED) { SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); sm->DeauthenticationRequest = FALSE; } SM_STATE(WPA_PTK, AUTHENTICATION) { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); os_memset(&sm->PTK, 0, sizeof(sm->PTK)); sm->PTK_valid = FALSE; wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto, 1); wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1); sm->AuthenticationRequest = FALSE; } static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { if (group->first_sta_seen) return; /* * System has run bit further than at the time hostapd was started * potentially very early during boot up. This provides better chances * of collecting more randomness on embedded systems. Re-initialize the * GMK and Counter here to improve their strength if there was not * enough entropy available immediately after system startup. */ wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first " "station"); if (random_pool_ready() != 1) { wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " "to proceed - reject first 4-way handshake"); group->reject_4way_hs_for_entropy = TRUE; } else { group->first_sta_seen = TRUE; group->reject_4way_hs_for_entropy = FALSE; } if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 || wpa_gtk_update(wpa_auth, group) < 0 || wpa_group_config_group_keys(wpa_auth, group) < 0) { wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed"); group->first_sta_seen = FALSE; group->reject_4way_hs_for_entropy = TRUE; } } SM_STATE(WPA_PTK, AUTHENTICATION2) { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); wpa_group_ensure_init(sm->wpa_auth, sm->group); sm->ReAuthenticationRequest = FALSE; /* * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat * ambiguous. The Authenticator state machine uses a counter that is * incremented by one for each 4-way handshake. However, the security * analysis of 4-way handshake points out that unpredictable nonces * help in preventing precomputation attacks. Instead of the state * machine definition, use an unpredictable nonce value here to provide * stronger protection against potential precomputation attacks. */ if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_ERROR, "WPA: Failed to get random data for " "ANonce."); sm->Disconnect = TRUE; return; } wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce, WPA_NONCE_LEN); /* IEEE 802.11i does not clear TimeoutCtr here, but this is more * logical place than INITIALIZE since AUTHENTICATION2 can be * re-entered on ReAuthenticationRequest without going through * INITIALIZE. */ sm->TimeoutCtr = 0; } +static int wpa_auth_sm_ptk_update(struct wpa_state_machine *sm) +{ + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_ERROR, + "WPA: Failed to get random data for ANonce"); + sm->Disconnect = TRUE; + return -1; + } + wpa_hexdump(MSG_DEBUG, "WPA: Assign new ANonce", sm->ANonce, + WPA_NONCE_LEN); + sm->TimeoutCtr = 0; + return 0; +} + + SM_STATE(WPA_PTK, INITPMK) { u8 msk[2 * PMK_LEN]; size_t len = 2 * PMK_LEN; SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); #ifdef CONFIG_IEEE80211R sm->xxkey_len = 0; #endif /* CONFIG_IEEE80211R */ if (sm->pmksa) { wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " "(len=%lu)", (unsigned long) len); os_memcpy(sm->PMK, msk, PMK_LEN); #ifdef CONFIG_IEEE80211R if (len >= 2 * PMK_LEN) { os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); sm->xxkey_len = PMK_LEN; } #endif /* CONFIG_IEEE80211R */ } else { wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p", sm->wpa_auth->cb.get_msk); sm->Disconnect = TRUE; return; } os_memset(msk, 0, sizeof(msk)); sm->req_replay_counter_used = 0; /* IEEE 802.11i does not set keyRun to FALSE, but not doing this * will break reauthentication since EAPOL state machines may not be * get into AUTHENTICATING state that clears keyRun before WPA state * machine enters AUTHENTICATION2 state and goes immediately to INITPMK * state and takes PMK from the previously used AAA Key. This will * eventually fail in 4-Way Handshake because Supplicant uses PMK * derived from the new AAA Key. Setting keyRun = FALSE here seems to * be good workaround for this issue. */ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); } SM_STATE(WPA_PTK, INITPSK) { const u8 *psk; SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); if (psk) { os_memcpy(sm->PMK, psk, PMK_LEN); #ifdef CONFIG_IEEE80211R os_memcpy(sm->xxkey, psk, PMK_LEN); sm->xxkey_len = PMK_LEN; #endif /* CONFIG_IEEE80211R */ } sm->req_replay_counter_used = 0; } SM_STATE(WPA_PTK, PTKSTART) { u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; size_t pmkid_len = 0; SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); sm->PTKRequest = FALSE; sm->TimeoutEvt = FALSE; sm->alt_snonce_valid = FALSE; sm->TimeoutCtr++; if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; } wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/4 msg of 4-Way Handshake"); /* * TODO: Could add PMKID even with WPA2-PSK, but only if there is only * one possible PSK for this STA. */ if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) { pmkid = buf; pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); if (sm->pmksa) { os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->pmksa->pmkid, PMKID_LEN); } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) { /* No KCK available to derive PMKID */ pmkid = NULL; } else { /* * Calculate PMKID since no PMKSA cache entry was * available with pre-calculated PMKID. */ rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); } } wpa_send_eapol(sm->wpa_auth, sm, WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, sm->ANonce, pmkid, pmkid_len, 0, 0); } static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, struct wpa_ptk *ptk) { #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk); #endif /* CONFIG_IEEE80211R */ return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, ptk, sm->wpa_key_mgmt, sm->pairwise); } SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { struct wpa_ptk PTK; int ok = 0, psk_found = 0; const u8 *pmk = NULL; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; sm->update_snonce = FALSE; /* WPA with IEEE 802.1X: use the derived PMK from EAP * WPA-PSK: iterate through possible PSKs and select the one matching * the packet */ for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, pmk); if (pmk == NULL) break; psk_found = 1; } else pmk = sm->PMK; wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK); if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { ok = 1; break; } if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) break; } if (!ok) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "invalid MIC in msg 2/4 of 4-Way Handshake"); if (psk_found) wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr); return; } #ifdef CONFIG_IEEE80211R if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { /* * Verify that PMKR1Name from EAPOL-Key message 2/4 matches * with the value we derived. */ if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "PMKR1Name mismatch in FT 4-way " "handshake"); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from " "Supplicant", sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); return; } } #endif /* CONFIG_IEEE80211R */ sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { /* PSK may have changed from the previous choice, so update * state machine data based on whatever PSK was selected here. */ os_memcpy(sm->PMK, pmk, PMK_LEN); } sm->MICVerified = TRUE; os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); sm->PTK_valid = TRUE; } SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) { SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); sm->TimeoutCtr = 0; } #ifdef CONFIG_IEEE80211W static int ieee80211w_kde_len(struct wpa_state_machine *sm) { if (sm->mgmt_frame_prot) { size_t len; len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len; } return 0; } static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) { struct wpa_igtk_kde igtk; struct wpa_group *gsm = sm->group; u8 rsc[WPA_KEY_RSC_LEN]; size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); if (!sm->mgmt_frame_prot) return pos; igtk.keyid[0] = gsm->GN_igtk; igtk.keyid[1] = 0; if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0) os_memset(igtk.pn, 0, sizeof(igtk.pn)); else os_memcpy(igtk.pn, rsc, sizeof(igtk.pn)); os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len); if (sm->wpa_auth->conf.disable_gtk) { /* * Provide unique random IGTK to each STA to prevent use of * IGTK in the BSS. */ if (random_get_bytes(igtk.igtk, len) < 0) return pos; } pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len, NULL, 0); return pos; } #else /* CONFIG_IEEE80211W */ static int ieee80211w_kde_len(struct wpa_state_machine *sm) { return 0; } static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) { return pos; } #endif /* CONFIG_IEEE80211W */ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; size_t gtk_len, kde_len; struct wpa_group *gsm = sm->group; u8 *wpa_ie; int wpa_ie_len, secure, keyidx, encr = 0; SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); sm->TimeoutEvt = FALSE; sm->TimeoutCtr++; if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; } /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], GTK[GN], IGTK, [FTIE], [TIE * 2]) */ os_memset(rsc, 0, WPA_KEY_RSC_LEN); wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */ wpa_ie = sm->wpa_auth->wpa_ie; wpa_ie_len = sm->wpa_auth->wpa_ie_len; if (sm->wpa == WPA_VERSION_WPA && (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { /* WPA-only STA, remove RSN IE and possible MDIE */ wpa_ie = wpa_ie + wpa_ie[1] + 2; if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN) wpa_ie = wpa_ie + wpa_ie[1] + 2; wpa_ie_len = wpa_ie[1] + 2; } wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 3/4 msg of 4-Way Handshake"); if (sm->wpa == WPA_VERSION_WPA2) { /* WPA2 send GTK in the 4-way handshake */ secure = 1; gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; if (sm->wpa_auth->conf.disable_gtk) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. */ if (random_get_bytes(dummy_gtk, gtk_len) < 0) return; gtk = dummy_gtk; } keyidx = gsm->GN; _rsc = rsc; encr = 1; } else { /* WPA does not include GTK in msg 3/4 */ secure = 0; gtk = NULL; gtk_len = 0; keyidx = 0; _rsc = NULL; if (sm->rx_eapol_key_secure) { /* * It looks like Windows 7 supplicant tries to use * Secure bit in msg 2/4 after having reported Michael * MIC failure and it then rejects the 4-way handshake * if msg 3/4 does not set Secure bit. Work around this * by setting the Secure bit here even in the case of * WPA if the supplicant used it first. */ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "STA used Secure bit in WPA msg 2/4 - " "set Secure for 3/4 as workaround"); secure = 1; } } kde_len = wpa_ie_len + ieee80211w_kde_len(sm); if (gtk) kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */ kde_len += 300; /* FTIE + 2 * TIE */ } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4; #endif /* CONFIG_P2P */ kde = os_malloc(kde_len); if (kde == NULL) return; pos = kde; os_memcpy(pos, wpa_ie, wpa_ie_len); pos += wpa_ie_len; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name); if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert " "PMKR1Name into RSN IE in EAPOL-Key data"); os_free(kde); return; } pos += res; } #endif /* CONFIG_IEEE80211R */ if (gtk) { u8 hdr[2]; hdr[0] = keyidx & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gtk_len); } pos = ieee80211w_kde_add(sm, pos); #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res; struct wpa_auth_config *conf; conf = &sm->wpa_auth->conf; res = wpa_write_ftie(conf, conf->r0_key_holder, conf->r0_key_holder_len, NULL, NULL, pos, kde + kde_len - pos, NULL, 0); if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " "into EAPOL-Key Key Data"); os_free(kde); return; } pos += res; /* TIE[ReassociationDeadline] (TU) */ *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE; WPA_PUT_LE32(pos, conf->reassociation_deadline); pos += 4; /* TIE[KeyLifetime] (seconds) */ *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60); pos += 4; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) { u8 addr[3 * 4]; os_memcpy(addr, sm->ip_addr, 4); os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4); os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4); pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC, addr, sizeof(addr), NULL, 0); } #endif /* CONFIG_P2P */ wpa_send_eapol(sm->wpa_auth, sm, (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_KEY_TYPE, _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); os_free(kde); } SM_STATE(WPA_PTK, PTKINITDONE) { SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); sm->EAPOLKeyReceived = FALSE; if (sm->Pair) { enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise); int klen = wpa_cipher_key_len(sm->pairwise); if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, sm->PTK.tk, klen)) { wpa_sta_disconnect(sm->wpa_auth, sm->addr); return; } /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; if (sm->wpa_auth->conf.wpa_ptk_rekey) { eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); eloop_register_timeout(sm->wpa_auth->conf. wpa_ptk_rekey, 0, wpa_rekey_ptk, sm->wpa_auth, sm); } if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 1); } } if (0 /* IBSS == TRUE */) { sm->keycount++; if (sm->keycount == 2) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 1); } } else { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 1); } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0); wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1); if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = TRUE; else sm->has_GTK = TRUE; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, "pairwise key handshake completed (%s)", sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); #ifdef CONFIG_IEEE80211R wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); #endif /* CONFIG_IEEE80211R */ } SM_STEP(WPA_PTK) { struct wpa_authenticator *wpa_auth = sm->wpa_auth; if (sm->Init) SM_ENTER(WPA_PTK, INITIALIZE); else if (sm->Disconnect /* || FIX: dot11RSNAConfigSALifetime timeout */) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "WPA_PTK: sm->Disconnect"); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->DeauthenticationRequest) SM_ENTER(WPA_PTK, DISCONNECTED); else if (sm->AuthenticationRequest) SM_ENTER(WPA_PTK, AUTHENTICATION); else if (sm->ReAuthenticationRequest) SM_ENTER(WPA_PTK, AUTHENTICATION2); - else if (sm->PTKRequest) - SM_ENTER(WPA_PTK, PTKSTART); - else switch (sm->wpa_ptk_state) { + else if (sm->PTKRequest) { + if (wpa_auth_sm_ptk_update(sm) < 0) + SM_ENTER(WPA_PTK, DISCONNECTED); + else + SM_ENTER(WPA_PTK, PTKSTART); + } else switch (sm->wpa_ptk_state) { case WPA_PTK_INITIALIZE: break; case WPA_PTK_DISCONNECT: SM_ENTER(WPA_PTK, DISCONNECTED); break; case WPA_PTK_DISCONNECTED: SM_ENTER(WPA_PTK, INITIALIZE); break; case WPA_PTK_AUTHENTICATION: SM_ENTER(WPA_PTK, AUTHENTICATION2); break; case WPA_PTK_AUTHENTICATION2: if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && wpa_auth_get_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun) > 0) SM_ENTER(WPA_PTK, INITPMK); else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) /* FIX: && 802.1X::keyRun */) SM_ENTER(WPA_PTK, INITPSK); break; case WPA_PTK_INITPMK: if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable) > 0) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "INITPMK - keyAvailable = false"); SM_ENTER(WPA_PTK, DISCONNECT); } break; case WPA_PTK_INITPSK: if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL)) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "no PSK configured for the STA"); wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } break; case WPA_PTK_PTKSTART: if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "PTKSTART: Retry limit %d reached", dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); break; case WPA_PTK_PTKCALCNEGOTIATING: if (sm->MICVerified) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2); else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); break; case WPA_PTK_PTKCALCNEGOTIATING2: SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); break; case WPA_PTK_PTKINITNEGOTIATING: if (sm->update_snonce) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "PTKINITNEGOTIATING: Retry limit %d " "reached", dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); break; case WPA_PTK_PTKINITDONE: break; } } SM_STATE(WPA_PTK_GROUP, IDLE) { SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group); if (sm->Init) { /* Init flag is not cleared here, so avoid busy * loop by claiming nothing changed. */ sm->changed = FALSE; } sm->GTimeoutCtr = 0; } SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN]; struct wpa_group *gsm = sm->group; const u8 *kde; u8 *kde_buf = NULL, *pos, hdr[2]; size_t kde_len; u8 *gtk, dummy_gtk[32]; SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); sm->GTimeoutCtr++; if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; } if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = FALSE; sm->TimeoutEvt = FALSE; /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ os_memset(rsc, 0, WPA_KEY_RSC_LEN); if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/2 msg of Group Key Handshake"); gtk = gsm->GTK[gsm->GN - 1]; if (sm->wpa_auth->conf.disable_gtk) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. */ if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0) return; gtk = dummy_gtk; } if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + ieee80211w_kde_len(sm); kde_buf = os_malloc(kde_len); if (kde_buf == NULL) return; kde = pos = kde_buf; hdr[0] = gsm->GN & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); kde_len = pos - kde; } else { kde = gtk; kde_len = gsm->GTK_len; } wpa_send_eapol(sm->wpa_auth, sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK | (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1); os_free(kde_buf); } SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) { SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); sm->EAPOLKeyReceived = FALSE; if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->GTimeoutCtr = 0; /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, "group key handshake completed (%s)", sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); sm->has_GTK = TRUE; } SM_STATE(WPA_PTK_GROUP, KEYERROR) { SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->Disconnect = TRUE; } SM_STEP(WPA_PTK_GROUP) { if (sm->Init || sm->PtkGroupInit) { SM_ENTER(WPA_PTK_GROUP, IDLE); sm->PtkGroupInit = FALSE; } else switch (sm->wpa_ptk_group_state) { case WPA_PTK_GROUP_IDLE: if (sm->GUpdateStationKeys || (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); break; case WPA_PTK_GROUP_REKEYNEGOTIATING: if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && !sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); else if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) SM_ENTER(WPA_PTK_GROUP, KEYERROR); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); break; case WPA_PTK_GROUP_KEYERROR: SM_ENTER(WPA_PTK_GROUP, IDLE); break; case WPA_PTK_GROUP_REKEYESTABLISHED: SM_ENTER(WPA_PTK_GROUP, IDLE); break; } } static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { int ret = 0; os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); inc_byte_array(group->Counter, WPA_NONCE_LEN); if (wpa_gmk_to_gtk(group->GMK, "Group key expansion", wpa_auth->addr, group->GNonce, group->GTK[group->GN - 1], group->GTK_len) < 0) ret = -1; wpa_hexdump_key(MSG_DEBUG, "GTK", group->GTK[group->GN - 1], group->GTK_len); #ifdef CONFIG_IEEE80211W if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { size_t len; len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); inc_byte_array(group->Counter, WPA_NONCE_LEN); if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion", wpa_auth->addr, group->GNonce, group->IGTK[group->GN_igtk - 4], len) < 0) ret = -1; wpa_hexdump_key(MSG_DEBUG, "IGTK", group->IGTK[group->GN_igtk - 4], len); } #endif /* CONFIG_IEEE80211W */ return ret; } static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "GTK_INIT (VLAN-ID %d)", group->vlan_id); group->changed = FALSE; /* GInit is not cleared here; avoid loop */ group->wpa_group_state = WPA_GROUP_GTK_INIT; /* GTK[0..N] = 0 */ os_memset(group->GTK, 0, sizeof(group->GTK)); group->GN = 1; group->GM = 2; #ifdef CONFIG_IEEE80211W group->GN_igtk = 4; group->GM_igtk = 5; #endif /* CONFIG_IEEE80211W */ /* GTK[GN] = CalcGTK() */ wpa_gtk_update(wpa_auth, group); } static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { if (ctx != NULL && ctx != sm->group) return 0; if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Not in PTKINITDONE; skip Group Key update"); sm->GUpdateStationKeys = FALSE; return 0; } if (sm->GUpdateStationKeys) { /* * This should not really happen, so add a debug log entry. * Since we clear the GKeyDoneStations before the loop, the * station needs to be counted here anyway. */ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "GUpdateStationKeys was already set when " "marking station for GTK rekeying"); } /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */ if (sm->is_wnmsleep) return 0; sm->group->GKeyDoneStations++; sm->GUpdateStationKeys = TRUE; wpa_sm_step(sm); return 0; } #ifdef CONFIG_WNM /* update GTK when exiting WNM-Sleep Mode */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) { if (sm == NULL || sm->is_wnmsleep) return; wpa_group_update_sta(sm, NULL); } void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) { if (sm) sm->is_wnmsleep = !!flag; } int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos) { struct wpa_group *gsm = sm->group; u8 *start = pos; /* * GTK subelement: * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | * Key[5..32] */ *pos++ = WNM_SLEEP_SUBELEM_GTK; *pos++ = 11 + gsm->GTK_len; /* Key ID in B0-B1 of Key Info */ WPA_PUT_LE16(pos, gsm->GN & 0x03); pos += 2; *pos++ = gsm->GTK_len; if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0) return 0; pos += 8; os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len); pos += gsm->GTK_len; wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit", gsm->GN); wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit", gsm->GTK[gsm->GN - 1], gsm->GTK_len); return pos - start; } #ifdef CONFIG_IEEE80211W int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) { struct wpa_group *gsm = sm->group; u8 *start = pos; size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); /* * IGTK subelement: * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] */ *pos++ = WNM_SLEEP_SUBELEM_IGTK; *pos++ = 2 + 6 + len; WPA_PUT_LE16(pos, gsm->GN_igtk); pos += 2; if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0) return 0; pos += 6; os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len); pos += len; wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit", gsm->GN_igtk); wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit", gsm->IGTK[gsm->GN_igtk - 4], len); return pos - start; } #endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_WNM */ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { int tmp; wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "SETKEYS (VLAN-ID %d)", group->vlan_id); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYS; group->GTKReKey = FALSE; tmp = group->GM; group->GM = group->GN; group->GN = tmp; #ifdef CONFIG_IEEE80211W tmp = group->GM_igtk; group->GM_igtk = group->GN_igtk; group->GN_igtk = tmp; #endif /* CONFIG_IEEE80211W */ /* "GKeyDoneStations = GNoStations" is done in more robust way by * counting the STAs that are marked with GUpdateStationKeys instead of * including all STAs that could be in not-yet-completed state. */ wpa_gtk_update(wpa_auth, group); if (group->GKeyDoneStations) { wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected " "GKeyDoneStations=%d when starting new GTK rekey", group->GKeyDoneStations); group->GKeyDoneStations = 0; } wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group); wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", group->GKeyDoneStations); } static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { int ret = 0; if (wpa_auth_set_key(wpa_auth, group->vlan_id, wpa_cipher_to_alg(wpa_auth->conf.wpa_group), broadcast_ether_addr, group->GN, group->GTK[group->GN - 1], group->GTK_len) < 0) ret = -1; #ifdef CONFIG_IEEE80211W if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { enum wpa_alg alg; size_t len; alg = wpa_cipher_to_alg(wpa_auth->conf.group_mgmt_cipher); len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); if (ret == 0 && wpa_auth_set_key(wpa_auth, group->vlan_id, alg, broadcast_ether_addr, group->GN_igtk, group->IGTK[group->GN_igtk - 4], len) < 0) ret = -1; } #endif /* CONFIG_IEEE80211W */ return ret; } static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx) { if (sm->group == ctx) { wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR " for discconnection due to fatal failure", MAC2STR(sm->addr)); sm->Disconnect = TRUE; } return 0; } static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state FATAL_FAILURE"); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_FATAL_FAILURE; wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group); } static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYSDONE; if (wpa_group_config_group_keys(wpa_auth, group) < 0) { wpa_group_fatal_failure(wpa_auth, group); return -1; } return 0; } static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { if (group->GInit) { wpa_group_gtk_init(wpa_auth, group); } else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) { /* Do not allow group operations */ } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && group->GTKAuthenticator) { wpa_group_setkeysdone(wpa_auth, group); } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE && group->GTKReKey) { wpa_group_setkeys(wpa_auth, group); } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) { if (group->GKeyDoneStations == 0) wpa_group_setkeysdone(wpa_auth, group); else if (group->GTKReKey) wpa_group_setkeys(wpa_auth, group); } } static int wpa_sm_step(struct wpa_state_machine *sm) { if (sm == NULL) return 0; if (sm->in_step_loop) { /* This should not happen, but if it does, make sure we do not * end up freeing the state machine too early by exiting the * recursive call. */ wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively"); return 0; } sm->in_step_loop = 1; do { if (sm->pending_deinit) break; sm->changed = FALSE; sm->wpa_auth->group->changed = FALSE; SM_STEP_RUN(WPA_PTK); if (sm->pending_deinit) break; SM_STEP_RUN(WPA_PTK_GROUP); if (sm->pending_deinit) break; wpa_group_sm_step(sm->wpa_auth, sm->group); } while (sm->changed || sm->wpa_auth->group->changed); sm->in_step_loop = 0; if (sm->pending_deinit) { wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state " "machine deinit for " MACSTR, MAC2STR(sm->addr)); wpa_free_sta_sm(sm); return 1; } return 0; } static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) { struct wpa_state_machine *sm = eloop_ctx; wpa_sm_step(sm); } void wpa_auth_sm_notify(struct wpa_state_machine *sm) { if (sm == NULL) return; eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); } void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) { int tmp, i; struct wpa_group *group; if (wpa_auth == NULL) return; group = wpa_auth->group; for (i = 0; i < 2; i++) { tmp = group->GM; group->GM = group->GN; group->GN = tmp; #ifdef CONFIG_IEEE80211W tmp = group->GM_igtk; group->GM_igtk = group->GN_igtk; group->GN_igtk = tmp; #endif /* CONFIG_IEEE80211W */ wpa_gtk_update(wpa_auth, group); wpa_group_config_group_keys(wpa_auth, group); } } static const char * wpa_bool_txt(int val) { return val ? "TRUE" : "FALSE"; } #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) { int len = 0, ret; char pmkid_txt[PMKID_LEN * 2 + 1]; #ifdef CONFIG_RSN_PREAUTH const int preauth = 1; #else /* CONFIG_RSN_PREAUTH */ const int preauth = 0; #endif /* CONFIG_RSN_PREAUTH */ if (wpa_auth == NULL) return len; ret = os_snprintf(buf + len, buflen - len, "dot11RSNAOptionImplemented=TRUE\n" "dot11RSNAPreauthenticationImplemented=%s\n" "dot11RSNAEnabled=%s\n" "dot11RSNAPreauthenticationEnabled=%s\n", wpa_bool_txt(preauth), wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), wpa_bool_txt(wpa_auth->conf.rsn_preauth)); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN); ret = os_snprintf( buf + len, buflen - len, "dot11RSNAConfigVersion=%u\n" "dot11RSNAConfigPairwiseKeysSupported=9999\n" /* FIX: dot11RSNAConfigGroupCipher */ /* FIX: dot11RSNAConfigGroupRekeyMethod */ /* FIX: dot11RSNAConfigGroupRekeyTime */ /* FIX: dot11RSNAConfigGroupRekeyPackets */ "dot11RSNAConfigGroupRekeyStrict=%u\n" "dot11RSNAConfigGroupUpdateCount=%u\n" "dot11RSNAConfigPairwiseUpdateCount=%u\n" "dot11RSNAConfigGroupCipherSize=%u\n" "dot11RSNAConfigPMKLifetime=%u\n" "dot11RSNAConfigPMKReauthThreshold=%u\n" "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" "dot11RSNAConfigSATimeout=%u\n" "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" "dot11RSNAPMKIDUsed=%s\n" "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" "dot11RSNATKIPCounterMeasuresInvoked=%u\n" "dot11RSNA4WayHandshakeFailures=%u\n" "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", RSN_VERSION, !!wpa_auth->conf.wpa_strict_rekey, dot11RSNAConfigGroupUpdateCount, dot11RSNAConfigPairwiseUpdateCount, wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, dot11RSNAConfigPMKLifetime, dot11RSNAConfigPMKReauthThreshold, dot11RSNAConfigSATimeout, RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected), RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected), RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected), pmkid_txt, RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested), RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested), RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, wpa_auth->dot11RSNA4WayHandshakeFailures); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; /* TODO: dot11RSNAConfigPairwiseCiphersTable */ /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ /* Private MIB */ ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", wpa_auth->group->wpa_group_state); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; return len; } int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) { int len = 0, ret; u32 pairwise = 0; if (sm == NULL) return 0; /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ /* dot11RSNAStatsEntry */ pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ? WPA_PROTO_RSN : WPA_PROTO_WPA, sm->pairwise); if (pairwise == 0) return 0; ret = os_snprintf( buf + len, buflen - len, /* TODO: dot11RSNAStatsIndex */ "dot11RSNAStatsSTAAddress=" MACSTR "\n" "dot11RSNAStatsVersion=1\n" "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" /* TODO: dot11RSNAStatsTKIPICVErrors */ "dot11RSNAStatsTKIPLocalMICFailures=%u\n" "dot11RSNAStatsTKIPRemoteMICFailures=%u\n" /* TODO: dot11RSNAStatsCCMPReplays */ /* TODO: dot11RSNAStatsCCMPDecryptErrors */ /* TODO: dot11RSNAStatsTKIPReplays */, MAC2STR(sm->addr), RSN_SUITE_ARG(pairwise), sm->dot11RSNAStatsTKIPLocalMICFailures, sm->dot11RSNAStatsTKIPRemoteMICFailures); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; /* Private MIB */ ret = os_snprintf(buf + len, buflen - len, "hostapdWPAPTKState=%d\n" "hostapdWPAPTKGroupState=%d\n", sm->wpa_ptk_state, sm->wpa_ptk_group_state); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; return len; } void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth) { if (wpa_auth) wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; } int wpa_auth_pairwise_set(struct wpa_state_machine *sm) { return sm && sm->pairwise_set; } int wpa_auth_get_pairwise(struct wpa_state_machine *sm) { return sm->pairwise; } int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) { if (sm == NULL) return -1; return sm->wpa_key_mgmt; } int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm) { if (sm == NULL) return 0; return sm->wpa; +} + + +int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm) +{ + if (!sm || !wpa_key_mgmt_ft(sm->wpa_key_mgmt)) + return 0; + return sm->tk_already_set; } int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry) { if (sm == NULL || sm->pmksa != entry) return -1; sm->pmksa = NULL; return 0; } struct rsn_pmksa_cache_entry * wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm) { return sm ? sm->pmksa : NULL; } void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm) { if (sm) sm->dot11RSNAStatsTKIPLocalMICFailures++; } const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) { if (wpa_auth == NULL) return NULL; *len = wpa_auth->wpa_ie_len; return wpa_auth->wpa_ie; } int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, int session_timeout, struct eapol_state_machine *eapol) { if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || sm->wpa_auth->conf.disable_pmksa_caching) return -1; if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, sm->PTK.kck, sm->PTK.kck_len, sm->wpa_auth->addr, sm->addr, session_timeout, eapol, sm->wpa_key_mgmt)) return 0; return -1; } int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, const u8 *pmk, size_t len, const u8 *sta_addr, int session_timeout, struct eapol_state_machine *eapol) { if (wpa_auth == NULL) return -1; if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL, 0, wpa_auth->addr, sta_addr, session_timeout, eapol, WPA_KEY_MGMT_IEEE8021X)) return 0; return -1; } int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *pmk) { if (wpa_auth->conf.disable_pmksa_caching) return -1; if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, NULL, 0, wpa_auth->addr, addr, 0, NULL, WPA_KEY_MGMT_SAE)) return 0; return -1; } void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { struct rsn_pmksa_cache_entry *pmksa; if (wpa_auth == NULL || wpa_auth->pmksa == NULL) return; pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); if (pmksa) { wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for " MACSTR " based on request", MAC2STR(sta_addr)); pmksa_cache_free_entry(wpa_auth->pmksa, pmksa); } } /* * Remove and free the group from wpa_authenticator. This is triggered by a * callback to make sure nobody is currently iterating the group list while it * gets modified. */ static void wpa_group_free(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { struct wpa_group *prev = wpa_auth->group; wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d", group->vlan_id); while (prev) { if (prev->next == group) { /* This never frees the special first group as needed */ prev->next = group->next; os_free(group); break; } prev = prev->next; } } /* Increase the reference counter for group */ static void wpa_group_get(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { /* Skip the special first group */ if (wpa_auth->group == group) return; group->references++; } /* Decrease the reference counter and maybe free the group */ static void wpa_group_put(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { /* Skip the special first group */ if (wpa_auth->group == group) return; group->references--; if (group->references) return; wpa_group_free(wpa_auth, group); } /* * Add a group that has its references counter set to zero. Caller needs to * call wpa_group_get() on the return value to mark the entry in use. */ static struct wpa_group * wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) { struct wpa_group *group; if (wpa_auth == NULL || wpa_auth->group == NULL) return NULL; wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", vlan_id); group = wpa_group_init(wpa_auth, vlan_id, 0); if (group == NULL) return NULL; group->next = wpa_auth->group->next; wpa_auth->group->next = group; return group; } int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) { struct wpa_group *group; if (sm == NULL || sm->wpa_auth == NULL) return 0; group = sm->wpa_auth->group; while (group) { if (group->vlan_id == vlan_id) break; group = group->next; } if (group == NULL) { group = wpa_auth_add_group(sm->wpa_auth, vlan_id); if (group == NULL) return -1; } if (sm->group == group) return 0; if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) return -1; wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); wpa_group_get(sm->wpa_auth, group); wpa_group_put(sm->wpa_auth, sm->group); sm->group = group; return 0; } void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack) { if (wpa_auth == NULL || sm == NULL) return; wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR " ack=%d", MAC2STR(sm->addr), ack); if (sm->pending_1_of_4_timeout && ack) { /* * Some deployed supplicant implementations update their SNonce * for each EAPOL-Key 2/4 message even within the same 4-way * handshake and then fail to use the first SNonce when * deriving the PTK. This results in unsuccessful 4-way * handshake whenever the relatively short initial timeout is * reached and EAPOL-Key 1/4 is retransmitted. Try to work * around this by increasing the timeout now that we know that * the station has received the frame. */ int timeout_ms = eapol_key_timeout_subseq; wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 " "timeout by %u ms because of acknowledged frame", timeout_ms); eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } } int wpa_auth_uses_sae(struct wpa_state_machine *sm) { if (sm == NULL) return 0; return wpa_key_mgmt_sae(sm->wpa_key_mgmt); } int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm) { if (sm == NULL) return 0; return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE; } #ifdef CONFIG_P2P int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr) { if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0) return -1; os_memcpy(addr, sm->ip_addr, 4); return 0; } #endif /* CONFIG_P2P */ int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth, struct radius_das_attrs *attr) { return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr); } void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth) { struct wpa_group *group; if (!wpa_auth) return; for (group = wpa_auth->group; group; group = group->next) wpa_group_config_group_keys(wpa_auth, group); } Index: releng/11.1/contrib/wpa/src/ap/wpa_auth.h =================================================================== --- releng/11.1/contrib/wpa/src/ap/wpa_auth.h (revision 324698) +++ releng/11.1/contrib/wpa/src/ap/wpa_auth.h (revision 324699) @@ -1,328 +1,329 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_AUTH_H #define WPA_AUTH_H #include "common/defs.h" #include "common/eapol_common.h" #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" #define MAX_OWN_IE_OVERRIDE 256 #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ /* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition */ struct ft_rrb_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */ le16 action_length; /* little endian length of action_frame */ u8 ap_address[ETH_ALEN]; /* * Followed by action_length bytes of FT Action frame (from Category * field to the end of Action Frame body. */ } STRUCT_PACKED; #define RSN_REMOTE_FRAME_TYPE_FT_RRB 1 #define FT_PACKET_REQUEST 0 #define FT_PACKET_RESPONSE 1 /* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ #define FT_PACKET_R0KH_R1KH_PULL 200 #define FT_PACKET_R0KH_R1KH_RESP 201 #define FT_PACKET_R0KH_R1KH_PUSH 202 #define FT_R0KH_R1KH_PULL_DATA_LEN 44 #define FT_R0KH_R1KH_RESP_DATA_LEN 76 #define FT_R0KH_R1KH_PUSH_DATA_LEN 88 #define FT_R0KH_R1KH_PULL_NONCE_LEN 16 struct ft_r0kh_r1kh_pull_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */ le16 data_length; /* little endian length of data (44) */ u8 ap_address[ETH_ALEN]; u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 r1kh_id[FT_R1KH_ID_LEN]; u8 s1kh_id[ETH_ALEN]; u8 pad[4]; /* 8-octet boundary for AES key wrap */ u8 key_wrap_extra[8]; } STRUCT_PACKED; struct ft_r0kh_r1kh_resp_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ le16 data_length; /* little endian length of data (76) */ u8 ap_address[ETH_ALEN]; u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */ u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ u8 s1kh_id[ETH_ALEN]; /* copied from pull */ u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; le16 pairwise; u8 pad[2]; /* 8-octet boundary for AES key wrap */ u8 key_wrap_extra[8]; } STRUCT_PACKED; struct ft_r0kh_r1kh_push_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ le16 data_length; /* little endian length of data (88) */ u8 ap_address[ETH_ALEN]; /* Encrypted with AES key-wrap */ u8 timestamp[4]; /* current time in seconds since unix epoch, little * endian */ u8 r1kh_id[FT_R1KH_ID_LEN]; u8 s1kh_id[ETH_ALEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; le16 pairwise; u8 pad[6]; /* 8-octet boundary for AES key wrap */ u8 key_wrap_extra[8]; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ /* per STA state machine data */ struct wpa_authenticator; struct wpa_state_machine; struct rsn_pmksa_cache_entry; struct eapol_state_machine; struct ft_remote_r0kh { struct ft_remote_r0kh *next; u8 addr[ETH_ALEN]; u8 id[FT_R0KH_ID_MAX_LEN]; size_t id_len; u8 key[16]; }; struct ft_remote_r1kh { struct ft_remote_r1kh *next; u8 addr[ETH_ALEN]; u8 id[FT_R1KH_ID_LEN]; u8 key[16]; }; struct wpa_auth_config { int wpa; int wpa_key_mgmt; int wpa_pairwise; int wpa_group; int wpa_group_rekey; int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; int rsn_pairwise; int rsn_preauth; int eapol_version; int peerkey; int wmm_enabled; int wmm_uapsd; int disable_pmksa_caching; int okc; int tx_status; #ifdef CONFIG_IEEE80211W enum mfp_options ieee80211w; int group_mgmt_cipher; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; size_t r0_key_holder_len; u8 r1_key_holder[FT_R1KH_ID_LEN]; u32 r0_key_lifetime; u32 reassociation_deadline; struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; int ft_over_ds; #endif /* CONFIG_IEEE80211R */ int disable_gtk; int ap_mlme; #ifdef CONFIG_TESTING_OPTIONS double corrupt_gtk_rekey_mic_probability; u8 own_ie_override[MAX_OWN_IE_OVERRIDE]; size_t own_ie_override_len; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P u8 ip_addr_go[4]; u8 ip_addr_mask[4]; u8 ip_addr_start[4]; u8 ip_addr_end[4]; #endif /* CONFIG_P2P */ }; typedef enum { LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING } logger_level; typedef enum { WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized, WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable, WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx } wpa_eapol_variable; struct wpa_auth_callbacks { void *ctx; void (*logger)(void *ctx, const u8 *addr, logger_level level, const char *txt); void (*disconnect)(void *ctx, const u8 *addr, u16 reason); int (*mic_failure_report)(void *ctx, const u8 *addr); void (*psk_failure_report)(void *ctx, const u8 *addr); void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk); int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len); int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq); int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data, size_t data_len, int encrypt); int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx); int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx); int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data, size_t data_len); #ifdef CONFIG_IEEE80211R struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); int (*send_ft_action)(void *ctx, const u8 *dst, const u8 *data, size_t data_len); int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen); #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_MESH int (*start_ampe)(void *ctx, const u8 *sta_addr); #endif /* CONFIG_MESH */ }; struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, struct wpa_auth_callbacks *cb); int wpa_init_keys(struct wpa_authenticator *wpa_auth); void wpa_deinit(struct wpa_authenticator *wpa_auth); int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_auth_config *conf); enum { WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER, WPA_INVALID_MDIE, WPA_INVALID_PROTO }; int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *mdie, size_t mdie_len); int wpa_validate_osen(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *osen_ie, size_t osen_ie_len); int wpa_auth_uses_mfp(struct wpa_state_machine *sm); struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr); int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm); void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm); void wpa_auth_sta_deinit(struct wpa_state_machine *sm); void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len); enum wpa_event { WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, WPA_REAUTH_EAPOL, WPA_ASSOC_FT }; void wpa_remove_ptk(struct wpa_state_machine *sm); int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event); void wpa_auth_sm_notify(struct wpa_state_machine *sm); void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen); void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); int wpa_auth_pairwise_set(struct wpa_state_machine *sm); int wpa_auth_get_pairwise(struct wpa_state_machine *sm); int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); +int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm); int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache_entry * wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm); void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len); int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, const u8 *pmk, size_t len, const u8 *sta_addr, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *pmk); void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); #ifdef CONFIG_IEEE80211R u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg, const u8 *req_ies, size_t req_ies_len); void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, u16 auth_transaction, const u8 *ies, size_t ies_len, void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 resp, const u8 *ies, size_t ies_len), void *ctx); u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len); int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len); int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len); void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); #endif /* CONFIG_IEEE80211R */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos); int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); int wpa_auth_uses_sae(struct wpa_state_machine *sm); int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm); int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr); struct radius_das_attrs; int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth, struct radius_das_attrs *attr); void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth); #endif /* WPA_AUTH_H */ Index: releng/11.1/contrib/wpa/src/ap/wpa_auth_ft.c =================================================================== --- releng/11.1/contrib/wpa/src/ap/wpa_auth_ft.c (revision 324698) +++ releng/11.1/contrib/wpa/src/ap/wpa_auth_ft.c (revision 324699) @@ -1,1792 +1,1802 @@ /* * hostapd - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "crypto/aes_wrap.h" #include "crypto/random.h" #include "ap_config.h" #include "ieee802_11.h" #include "wmm.h" #include "wpa_auth.h" #include "wpa_auth_i.h" #ifdef CONFIG_IEEE80211R static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, const u8 *current_ap, const u8 *sta_addr, u16 status, const u8 *resp_ies, size_t resp_ies_len); static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { if (wpa_auth->cb.send_ether == NULL) return -1; wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst)); return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB, data, data_len); } static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { if (wpa_auth->cb.send_ft_action == NULL) return -1; return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst, data, data_len); } static struct wpa_state_machine * wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { if (wpa_auth->cb.add_sta == NULL) return NULL; return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr); } static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen) { if (wpa_auth->cb.add_tspec == NULL) { wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); return -1; } return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie, tspec_ielen); } int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) { u8 *pos = buf; u8 capab; if (len < 2 + sizeof(struct rsn_mdie)) return -1; *pos++ = WLAN_EID_MOBILITY_DOMAIN; *pos++ = MOBILITY_DOMAIN_ID_LEN + 1; os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); pos += MOBILITY_DOMAIN_ID_LEN; capab = 0; if (conf->ft_over_ds) capab |= RSN_FT_CAPAB_FT_OVER_DS; *pos++ = capab; return pos - buf; } int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len) { u8 *pos = buf, *ielen; struct rsn_ftie *hdr; if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + subelem_len) return -1; *pos++ = WLAN_EID_FAST_BSS_TRANSITION; ielen = pos++; hdr = (struct rsn_ftie *) pos; os_memset(hdr, 0, sizeof(*hdr)); pos += sizeof(*hdr); WPA_PUT_LE16(hdr->mic_control, 0); if (anonce) os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); if (snonce) os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); /* Optional Parameters */ *pos++ = FTIE_SUBELEM_R1KH_ID; *pos++ = FT_R1KH_ID_LEN; os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN); pos += FT_R1KH_ID_LEN; if (r0kh_id) { *pos++ = FTIE_SUBELEM_R0KH_ID; *pos++ = r0kh_id_len; os_memcpy(pos, r0kh_id, r0kh_id_len); pos += r0kh_id_len; } if (subelem) { os_memcpy(pos, subelem, subelem_len); pos += subelem_len; } *ielen = pos - buf - 2; return pos - buf; } struct wpa_ft_pmk_r0_sa { struct wpa_ft_pmk_r0_sa *next; u8 pmk_r0[PMK_LEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ int pmk_r1_pushed; }; struct wpa_ft_pmk_r1_sa { struct wpa_ft_pmk_r1_sa *next; u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ }; struct wpa_ft_pmk_cache { struct wpa_ft_pmk_r0_sa *pmk_r0; struct wpa_ft_pmk_r1_sa *pmk_r1; }; struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void) { struct wpa_ft_pmk_cache *cache; cache = os_zalloc(sizeof(*cache)); return cache; } void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache) { struct wpa_ft_pmk_r0_sa *r0, *r0prev; struct wpa_ft_pmk_r1_sa *r1, *r1prev; r0 = cache->pmk_r0; while (r0) { r0prev = r0; r0 = r0->next; os_memset(r0prev->pmk_r0, 0, PMK_LEN); os_free(r0prev); } r1 = cache->pmk_r1; while (r1) { r1prev = r1; r1 = r1->next; os_memset(r1prev->pmk_r1, 0, PMK_LEN); os_free(r1prev); } os_free(cache); } static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0, const u8 *pmk_r0_name, int pairwise) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; /* TODO: add expiration and limit on number of entries in cache */ r0 = os_zalloc(sizeof(*r0)); if (r0 == NULL) return -1; os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); os_memcpy(r0->spa, spa, ETH_ALEN); r0->pairwise = pairwise; r0->next = cache->pmk_r0; cache->pmk_r0 = r0; return 0; } static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0_name, u8 *pmk_r0, int *pairwise) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; r0 = cache->pmk_r0; while (r0) { if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && os_memcmp_const(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) == 0) { os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); if (pairwise) *pairwise = r0->pairwise; return 0; } r0 = r0->next; } return -1; } static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1, const u8 *pmk_r1_name, int pairwise) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r1_sa *r1; /* TODO: add expiration and limit on number of entries in cache */ r1 = os_zalloc(sizeof(*r1)); if (r1 == NULL) return -1; os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); os_memcpy(r1->spa, spa, ETH_ALEN); r1->pairwise = pairwise; r1->next = cache->pmk_r1; cache->pmk_r1 = r1; return 0; } static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1_name, u8 *pmk_r1, int *pairwise) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r1_sa *r1; r1 = cache->pmk_r1; while (r1) { if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && os_memcmp_const(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN) == 0) { os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); if (pairwise) *pairwise = r1->pairwise; return 0; } r1 = r1->next; } return -1; } static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, const u8 *pmk_r0_name) { struct ft_remote_r0kh *r0kh; struct ft_r0kh_r1kh_pull_frame frame, f; r0kh = sm->wpa_auth->conf.r0kh_list; while (r0kh) { if (r0kh->id_len == sm->r0kh_id_len && os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) == 0) break; r0kh = r0kh->next; } if (r0kh == NULL) { wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " "address " MACSTR, MAC2STR(r0kh->addr)); os_memset(&frame, 0, sizeof(frame)); frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN); /* aes_wrap() does not support inplace encryption, so use a temporary * buffer for the data. */ if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; } os_memcpy(sm->ft_pending_pull_nonce, f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN); os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN); os_memset(f.pad, 0, sizeof(f.pad)); if (aes_wrap(r0kh->key, sizeof(r0kh->key), (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, f.nonce, frame.nonce) < 0) return -1; wpabuf_free(sm->ft_pending_req_ies); sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); if (sm->ft_pending_req_ies == NULL) return -1; wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); return 0; } int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk) { u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *mdid = sm->wpa_auth->conf.mobility_domain; const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len; const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; const u8 *ssid = sm->wpa_auth->conf.ssid; size_t ssid_len = sm->wpa_auth->conf.ssid_len; if (sm->xxkey_len == 0) { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " "derivation"); return -1; } wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name, sm->pairwise); wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr, pmk_r1, sm->pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name, sm->pairwise); return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name, ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise); } static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, const u8 *addr, int idx, u8 *seq) { if (wpa_auth->cb.get_seqnum == NULL) return -1; return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); } static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) { u8 *subelem; struct wpa_group *gsm = sm->group; size_t subelem_len, pad_len; const u8 *key; size_t key_len; u8 keybuf[32]; key_len = gsm->GTK_len; if (key_len > sizeof(keybuf)) return NULL; /* * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less * than 16 bytes. */ pad_len = key_len % 8; if (pad_len) pad_len = 8 - pad_len; if (key_len + pad_len < 16) pad_len += 8; if (pad_len && key_len < sizeof(keybuf)) { os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); os_memset(keybuf + key_len, 0, pad_len); keybuf[key_len] = 0xdd; key_len += pad_len; key = keybuf; } else key = gsm->GTK[gsm->GN - 1]; /* * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | * Key[5..32]. */ subelem_len = 13 + key_len + 8; subelem = os_zalloc(subelem_len); if (subelem == NULL) return NULL; subelem[0] = FTIE_SUBELEM_GTK; subelem[1] = 11 + key_len + 8; /* Key ID in B0-B1 of Key Info */ WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); subelem[4] = gsm->GTK_len; wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5); if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key, subelem + 13)) { os_free(subelem); return NULL; } *len = subelem_len; return subelem; } #ifdef CONFIG_IEEE80211W static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) { u8 *subelem, *pos; struct wpa_group *gsm = sm->group; size_t subelem_len; /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] | * Key[16+8] */ subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8; subelem = os_zalloc(subelem_len); if (subelem == NULL) return NULL; pos = subelem; *pos++ = FTIE_SUBELEM_IGTK; *pos++ = subelem_len - 2; WPA_PUT_LE16(pos, gsm->GN_igtk); pos += 2; wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos); pos += 6; *pos++ = WPA_IGTK_LEN; if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8, gsm->IGTK[gsm->GN_igtk - 4], pos)) { os_free(subelem); return NULL; } *len = subelem_len; return subelem; } #endif /* CONFIG_IEEE80211W */ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, u8 *pos, u8 *end, u8 id, u8 descr_count, const u8 *ies, size_t ies_len) { struct ieee802_11_elems parse; struct rsn_rdie *rdie; wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d", id, descr_count); wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)", ies, ies_len); if (end - pos < (int) sizeof(*rdie)) { wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE"); return pos; } *pos++ = WLAN_EID_RIC_DATA; *pos++ = sizeof(*rdie); rdie = (struct rsn_rdie *) pos; rdie->id = id; rdie->descr_count = 0; rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS); pos += sizeof(*rdie); if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) == ParseFailed) { wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs"); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; } if (parse.wmm_tspec) { struct wmm_tspec_element *tspec; if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) { wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE " "(%d)", (int) parse.wmm_tspec_len); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; } if (end - pos < (int) sizeof(*tspec)) { wpa_printf(MSG_ERROR, "FT: Not enough room for " "response TSPEC"); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; } tspec = (struct wmm_tspec_element *) pos; os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); } #ifdef NEED_AP_MLME if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { int res; res = wmm_process_tspec((struct wmm_tspec_element *) pos); wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res); if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS) rdie->status_code = host_to_le16(WLAN_STATUS_INVALID_PARAMETERS); else if (res == WMM_ADDTS_STATUS_REFUSED) rdie->status_code = host_to_le16(WLAN_STATUS_REQUEST_DECLINED); else { /* TSPEC accepted; include updated TSPEC in response */ rdie->descr_count = 1; pos += sizeof(struct wmm_tspec_element); } return pos; } #endif /* NEED_AP_MLME */ if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) { int res; res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos, sizeof(struct wmm_tspec_element)); if (res >= 0) { if (res) rdie->status_code = host_to_le16(res); else { /* TSPEC accepted; include updated TSPEC in * response */ rdie->descr_count = 1; pos += sizeof(struct wmm_tspec_element); } return pos; } } wpa_printf(MSG_DEBUG, "FT: No supported resource requested"); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; } static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end, const u8 *ric, size_t ric_len) { const u8 *rpos, *start; const struct rsn_rdie *rdie; wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len); rpos = ric; while (rpos + sizeof(*rdie) < ric + ric_len) { if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) || rpos + 2 + rpos[1] > ric + ric_len) break; rdie = (const struct rsn_rdie *) (rpos + 2); rpos += 2 + rpos[1]; start = rpos; while (rpos + 2 <= ric + ric_len && rpos + 2 + rpos[1] <= ric + ric_len) { if (rpos[0] == WLAN_EID_RIC_DATA) break; rpos += 2 + rpos[1]; } pos = wpa_ft_process_rdie(sm, pos, end, rdie->id, rdie->descr_count, start, rpos - start); } return pos; } u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg, const u8 *req_ies, size_t req_ies_len) { u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL; size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0; int res; struct wpa_auth_config *conf; struct rsn_ftie *_ftie; struct wpa_ft_ies parse; u8 *ric_start; u8 *anonce, *snonce; if (sm == NULL) return pos; conf = &sm->wpa_auth->conf; if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return pos; end = pos + max_len; if (auth_alg == WLAN_AUTH_FT) { /* * RSN (only present if this is a Reassociation Response and * part of a fast BSS transition) */ res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); if (res < 0) return pos; rsnie = pos; rsnie_len = res; pos += res; } /* Mobility Domain Information */ res = wpa_write_mdie(conf, pos, end - pos); if (res < 0) return pos; mdie = pos; mdie_len = res; pos += res; /* Fast BSS Transition Information */ if (auth_alg == WLAN_AUTH_FT) { subelem = wpa_ft_gtk_subelem(sm, &subelem_len); r0kh_id = sm->r0kh_id; r0kh_id_len = sm->r0kh_id_len; anonce = sm->ANonce; snonce = sm->SNonce; #ifdef CONFIG_IEEE80211W if (sm->mgmt_frame_prot) { u8 *igtk; size_t igtk_len; u8 *nbuf; igtk = wpa_ft_igtk_subelem(sm, &igtk_len); if (igtk == NULL) { os_free(subelem); return pos; } nbuf = os_realloc(subelem, subelem_len + igtk_len); if (nbuf == NULL) { os_free(subelem); os_free(igtk); return pos; } subelem = nbuf; os_memcpy(subelem + subelem_len, igtk, igtk_len); subelem_len += igtk_len; os_free(igtk); } #endif /* CONFIG_IEEE80211W */ } else { r0kh_id = conf->r0_key_holder; r0kh_id_len = conf->r0_key_holder_len; anonce = NULL; snonce = NULL; } res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos, end - pos, subelem, subelem_len); os_free(subelem); if (res < 0) return pos; ftie = pos; ftie_len = res; pos += res; os_free(sm->assoc_resp_ftie); sm->assoc_resp_ftie = os_malloc(ftie_len); if (sm->assoc_resp_ftie) os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); _ftie = (struct rsn_ftie *) (ftie + 2); if (auth_alg == WLAN_AUTH_FT) _ftie->mic_control[1] = 3; /* Information element count */ ric_start = pos; if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { pos = wpa_ft_process_ric(sm, pos, end, parse.ric, parse.ric_len); if (auth_alg == WLAN_AUTH_FT) _ftie->mic_control[1] += ieee802_11_ie_count(ric_start, pos - ric_start); } if (ric_start == pos) ric_start = NULL; if (auth_alg == WLAN_AUTH_FT && wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, sm->wpa_auth->addr, 6, mdie, mdie_len, ftie, ftie_len, rsnie, rsnie_len, ric_start, ric_start ? pos - ric_start : 0, _ftie->mic) < 0) wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); return pos; } static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { if (wpa_auth->cb.set_key == NULL) return -1; return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, key, key_len); } void wpa_ft_install_ptk(struct wpa_state_machine *sm) { enum wpa_alg alg; int klen; /* MLME-SETKEYS.request(PTK) */ alg = wpa_cipher_to_alg(sm->pairwise); klen = wpa_cipher_key_len(sm->pairwise); if (!wpa_cipher_valid_pairwise(sm->pairwise)) { wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip " "PTK configuration", sm->pairwise); return; } + if (sm->tk_already_set) { + /* Must avoid TK reconfiguration to prevent clearing of TX/RX + * PN in the driver */ + wpa_printf(MSG_DEBUG, + "FT: Do not re-install same PTK to the driver"); + return; + } + /* FIX: add STA entry to kernel/driver here? The set_key will fail * most likely without this.. At the moment, STA entry is added only * after association has been completed. This function will be called * again after association to get the PTK configured, but that could be * optimized by adding the STA entry earlier. */ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, sm->PTK.tk, klen)) return; /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; + sm->tk_already_set = TRUE; } static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, u8 **resp_ies, size_t *resp_ies_len) { struct rsn_mdie *mdie; struct rsn_ftie *ftie; u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; struct wpa_auth_config *conf; struct wpa_ft_ies parse; size_t buflen; int ret; u8 *pos, *end; int pairwise; *resp_ies = NULL; *resp_ies_len = 0; sm->pmk_r1_name_valid = 0; conf = &sm->wpa_auth->conf; wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs", ies, ies_len); if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->wpa_auth->conf.mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); return WLAN_STATUS_INVALID_MDIE; } ftie = (struct rsn_ftie *) parse.ftie; if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); return WLAN_STATUS_INVALID_FTIE; } os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID"); return WLAN_STATUS_INVALID_FTIE; } wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID", parse.r0kh_id, parse.r0kh_id_len); os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len); sm->r0kh_id_len = parse.r0kh_id_len; if (parse.rsn_pmkid == NULL) { wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); return WLAN_STATUS_INVALID_PMKID; } wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name", parse.rsn_pmkid, WPA_PMK_NAME_LEN); wpa_derive_pmk_r1_name(parse.rsn_pmkid, sm->wpa_auth->conf.r1_key_holder, sm->addr, pmk_r1_name); wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1, &pairwise) < 0) { if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) { wpa_printf(MSG_DEBUG, "FT: Did not have matching " "PMK-R1 and unknown R0KH-ID"); return WLAN_STATUS_INVALID_PMKID; } return -1; /* Status pending */ } wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); sm->pmk_r1_name_valid = 1; os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "ANonce"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", sm->SNonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", sm->ANonce, WPA_NONCE_LEN); if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, pmk_r1_name, &sm->PTK, ptk_name, sm->wpa_key_mgmt, pairwise) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; sm->pairwise = pairwise; sm->PTK_valid = TRUE; + sm->tk_already_set = FALSE; wpa_ft_install_ptk(sm); buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 2 + FT_R1KH_ID_LEN + 200; *resp_ies = os_zalloc(buflen); if (*resp_ies == NULL) { return WLAN_STATUS_UNSPECIFIED_FAILURE; } pos = *resp_ies; end = *resp_ies + buflen; ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid); if (ret < 0) { os_free(*resp_ies); *resp_ies = NULL; return WLAN_STATUS_UNSPECIFIED_FAILURE; } pos += ret; ret = wpa_write_mdie(conf, pos, end - pos); if (ret < 0) { os_free(*resp_ies); *resp_ies = NULL; return WLAN_STATUS_UNSPECIFIED_FAILURE; } pos += ret; ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len, sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0); if (ret < 0) { os_free(*resp_ies); *resp_ies = NULL; return WLAN_STATUS_UNSPECIFIED_FAILURE; } pos += ret; *resp_ies_len = pos - *resp_ies; return WLAN_STATUS_SUCCESS; } void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, u16 auth_transaction, const u8 *ies, size_t ies_len, void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, const u8 *ies, size_t ies_len), void *ctx) { u16 status; u8 *resp_ies; size_t resp_ies_len; int res; if (sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but " "WPA SM not available"); return; } wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR " BSSID=" MACSTR " transaction=%d", MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction); sm->ft_pending_cb = cb; sm->ft_pending_cb_ctx = ctx; sm->ft_pending_auth_transaction = auth_transaction; res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, &resp_ies_len); if (res < 0) { wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available"); return; } status = res; wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR " auth_transaction=%d status=%d", MAC2STR(sm->addr), auth_transaction + 1, status); wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); cb(ctx, sm->addr, bssid, auth_transaction + 1, status, resp_ies, resp_ies_len); os_free(resp_ies); } u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len) { struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; size_t mic_len = 16; unsigned int count; if (sm == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len); if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (parse.rsn == NULL) { wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (parse.rsn_pmkid == NULL) { wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); return WLAN_STATUS_INVALID_PMKID; } if (os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match " "with the PMKR1Name derived from auth request"); return WLAN_STATUS_INVALID_PMKID; } mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->wpa_auth->conf.mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); return WLAN_STATUS_INVALID_MDIE; } ftie = (struct rsn_ftie *) parse.ftie; if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); return WLAN_STATUS_INVALID_FTIE; } if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", ftie->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->SNonce, WPA_NONCE_LEN); return -1; } if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", ftie->anonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", sm->ANonce, WPA_NONCE_LEN); return -1; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); return -1; } if (parse.r0kh_id_len != sm->r0kh_id_len || os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " "the current R0KH-ID"); wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); return -1; } if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " "ReassocReq"); wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE", parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID", sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); return -1; } if (parse.rsn_pmkid == NULL || os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); return -1; } count = 3; if (parse.ric) count += ieee802_11_ie_count(parse.ric, parse.ric_len); if (ftie->mic_control[1] != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", ftie->mic_control[1], count); return -1; } if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, sm->wpa_auth->addr, 5, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, parse.rsn - 2, parse.rsn_len + 2, parse.ric, parse.ric_len, mic) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR, MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr)); wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, mic_len); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len); wpa_hexdump(MSG_MSGDUMP, "FT: MDIE", parse.mdie - 2, parse.mdie_len + 2); wpa_hexdump(MSG_MSGDUMP, "FT: FTIE", parse.ftie - 2, parse.ftie_len + 2); wpa_hexdump(MSG_MSGDUMP, "FT: RSN", parse.rsn - 2, parse.rsn_len + 2); return WLAN_STATUS_INVALID_FTIE; } return WLAN_STATUS_SUCCESS; } int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) { const u8 *sta_addr, *target_ap; const u8 *ies; size_t ies_len; u8 action; struct ft_rrb_frame *frame; if (sm == NULL) return -1; /* * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] * FT Request action frame body[variable] */ if (len < 14) { wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame " "(len=%lu)", (unsigned long) len); return -1; } action = data[1]; sta_addr = data + 2; target_ap = data + 8; ies = data + 14; ies_len = len - 14; wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR " Target AP=" MACSTR " Action=%d)", MAC2STR(sta_addr), MAC2STR(target_ap), action); if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: " "STA=" MACSTR " STA-Address=" MACSTR, MAC2STR(sm->addr), MAC2STR(sta_addr)); return -1; } /* * Do some sanity checking on the target AP address (not own and not * broadcast. This could be extended to filter based on a list of known * APs in the MD (if such a list were configured). */ if ((target_ap[0] & 0x01) || os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action " "frame"); return -1; } wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len); /* RRB - Forward action frame to the target AP */ frame = os_malloc(sizeof(*frame) + len); if (frame == NULL) return -1; frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame->packet_type = FT_PACKET_REQUEST; frame->action_length = host_to_le16(len); os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN); os_memcpy(frame + 1, data, len); wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame, sizeof(*frame) + len); os_free(frame); return 0; } static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 resp, const u8 *ies, size_t ies_len) { struct wpa_state_machine *sm = ctx; wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR, MAC2STR(sm->addr)); wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr, WLAN_STATUS_SUCCESS, ies, ies_len); } static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, const u8 *current_ap, const u8 *sta_addr, const u8 *body, size_t len) { struct wpa_state_machine *sm; u16 status; u8 *resp_ies; size_t resp_ies_len; int res; sm = wpa_ft_add_sta(wpa_auth, sta_addr); if (sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on " "RRB Request"); return -1; } wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len); sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb; sm->ft_pending_cb_ctx = sm; os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN); res = wpa_ft_process_auth_req(sm, body, len, &resp_ies, &resp_ies_len); if (res < 0) { wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response"); return 0; } status = res; res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status, resp_ies, resp_ies_len); os_free(resp_ies); return res; } static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, const u8 *current_ap, const u8 *sta_addr, u16 status, const u8 *resp_ies, size_t resp_ies_len) { struct wpa_authenticator *wpa_auth = sm->wpa_auth; size_t rlen; struct ft_rrb_frame *frame; u8 *pos; wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR " CurrentAP=" MACSTR " status=%d", MAC2STR(sm->addr), MAC2STR(current_ap), status); wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); /* RRB - Forward action frame response to the Current AP */ /* * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] * Status_Code[2] FT Request action frame body[variable] */ rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len; frame = os_malloc(sizeof(*frame) + rlen); if (frame == NULL) return -1; frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame->packet_type = FT_PACKET_RESPONSE; frame->action_length = host_to_le16(rlen); os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN); pos = (u8 *) (frame + 1); *pos++ = WLAN_ACTION_FT; *pos++ = 2; /* Action: Response */ os_memcpy(pos, sta_addr, ETH_ALEN); pos += ETH_ALEN; os_memcpy(pos, wpa_auth->addr, ETH_ALEN); pos += ETH_ALEN; WPA_PUT_LE16(pos, status); pos += 2; if (resp_ies) os_memcpy(pos, resp_ies, resp_ies_len); wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame, sizeof(*frame) + rlen); os_free(frame); return 0; } static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { struct ft_r0kh_r1kh_pull_frame f; const u8 *crypt; u8 *plain; struct ft_remote_r1kh *r1kh; struct ft_r0kh_r1kh_resp_frame resp, r; u8 pmk_r0[PMK_LEN]; int pairwise; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); if (data_len < sizeof(f)) return -1; r1kh = wpa_auth->conf.r1kh_list; while (r1kh) { if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) break; r1kh = r1kh->next; } if (r1kh == NULL) { wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for " "PMK-R1 pull source address " MACSTR, MAC2STR(src_addr)); return -1; } crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); os_memset(&f, 0, sizeof(f)); plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r1kh->key, sizeof(r1kh->key), (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "request from " MACSTR, MAC2STR(src_addr)); return -1; } wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", f.nonce, sizeof(f.nonce)); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", f.pmk_r0_name, WPA_PMK_NAME_LEN); wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); os_memset(&resp, 0, sizeof(resp)); resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN); os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN); /* aes_wrap() does not support inplace encryption, so use a temporary * buffer for the data. */ os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0, &pairwise) < 0) { wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for " "PMK-R1 pull"); return -1; } wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, r.pmk_r1, r.pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, WPA_PMK_NAME_LEN); r.pairwise = host_to_le16(pairwise); os_memset(r.pad, 0, sizeof(r.pad)); if (aes_wrap(r1kh->key, sizeof(r1kh->key), (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, r.nonce, resp.nonce) < 0) { os_memset(pmk_r0, 0, PMK_LEN); return -1; } os_memset(pmk_r0, 0, PMK_LEN); wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); return 0; } static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx) { struct wpa_state_machine *sm = eloop_ctx; int res; u8 *resp_ies; size_t resp_ies_len; u16 status; res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies), wpabuf_len(sm->ft_pending_req_ies), &resp_ies, &resp_ies_len); wpabuf_free(sm->ft_pending_req_ies); sm->ft_pending_req_ies = NULL; if (res < 0) res = WLAN_STATUS_UNSPECIFIED_FAILURE; status = res; wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR " - status %u", MAC2STR(sm->addr), status); sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr, sm->ft_pending_auth_transaction + 1, status, resp_ies, resp_ies_len); os_free(resp_ies); } static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx) { struct ft_r0kh_r1kh_resp_frame *frame = ctx; if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0) return 0; if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce, FT_R0KH_R1KH_PULL_NONCE_LEN) != 0) return 0; if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) return 0; wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for " MACSTR " - process from timeout", MAC2STR(sm->addr)); eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL); return 1; } static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { struct ft_r0kh_r1kh_resp_frame f; const u8 *crypt; u8 *plain; struct ft_remote_r0kh *r0kh; int pairwise, res; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); if (data_len < sizeof(f)) return -1; r0kh = wpa_auth->conf.r0kh_list; while (r0kh) { if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) break; r0kh = r0kh->next; } if (r0kh == NULL) { wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " "PMK-R0 pull response source address " MACSTR, MAC2STR(src_addr)); return -1; } crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); os_memset(&f, 0, sizeof(f)); plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r0kh->key, sizeof(r0kh->key), (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "response from " MACSTR, MAC2STR(src_addr)); return -1; } if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " "matching R1KH-ID"); return -1; } pairwise = le_to_host16(f.pairwise); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", f.nonce, sizeof(f.nonce)); wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" MACSTR " pairwise=0x%x", MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", f.pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", f.pmk_r1_name, WPA_PMK_NAME_LEN); res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, pairwise); wpa_printf(MSG_DEBUG, "FT: Look for pending pull request"); wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f); os_memset(f.pmk_r1, 0, PMK_LEN); return res ? 0 : -1; } static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { struct ft_r0kh_r1kh_push_frame f; const u8 *crypt; u8 *plain; struct ft_remote_r0kh *r0kh; struct os_time now; os_time_t tsend; int pairwise; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); if (data_len < sizeof(f)) return -1; r0kh = wpa_auth->conf.r0kh_list; while (r0kh) { if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) break; r0kh = r0kh->next; } if (r0kh == NULL) { wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " "PMK-R0 push source address " MACSTR, MAC2STR(src_addr)); return -1; } crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp); os_memset(&f, 0, sizeof(f)); plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r0kh->key, sizeof(r0kh->key), (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " MACSTR, MAC2STR(src_addr)); return -1; } os_get_time(&now); tsend = WPA_GET_LE32(f.timestamp); if ((now.sec > tsend && now.sec - tsend > 60) || (now.sec < tsend && tsend - now.sec > 60)) { wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid " "timestamp: sender time %d own time %d\n", (int) tsend, (int) now.sec); return -1; } if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " "R1KH-ID (received " MACSTR " own " MACSTR ")", MAC2STR(f.r1kh_id), MAC2STR(wpa_auth->conf.r1_key_holder)); return -1; } pairwise = le_to_host16(f.pairwise); wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" MACSTR " pairwise=0x%x", MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1", f.pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name", f.pmk_r1_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, pairwise); os_memset(f.pmk_r1, 0, PMK_LEN); return 0; } int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { struct ft_rrb_frame *frame; u16 alen; const u8 *pos, *end, *start; u8 action; const u8 *sta_addr, *target_ap_addr; wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR, MAC2STR(src_addr)); if (data_len < sizeof(*frame)) { wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)", (unsigned long) data_len); return -1; } pos = data; frame = (struct ft_rrb_frame *) pos; pos += sizeof(*frame); alen = le_to_host16(frame->action_length); wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d " "action_length=%d ap_address=" MACSTR, frame->frame_type, frame->packet_type, alen, MAC2STR(frame->ap_address)); if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) { /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */ wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with " "unrecognized type %d", frame->frame_type); return -1; } if (alen > data_len - sizeof(*frame)) { wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action " "frame"); return -1; } if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL) return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len); if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP) return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len); if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH) return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len); wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen); if (alen < 1 + 1 + 2 * ETH_ALEN) { wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough " "room for Action Frame body); alen=%lu", (unsigned long) alen); return -1; } start = pos; end = pos + alen; if (*pos != WLAN_ACTION_FT) { wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category " "%d", *pos); return -1; } pos++; action = *pos++; sta_addr = pos; pos += ETH_ALEN; target_ap_addr = pos; pos += ETH_ALEN; wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr=" MACSTR " target_ap_addr=" MACSTR, action, MAC2STR(sta_addr), MAC2STR(target_ap_addr)); if (frame->packet_type == FT_PACKET_REQUEST) { wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request"); if (action != 1) { wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in " "RRB Request", action); return -1; } if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Target AP address in the " "RRB Request does not match with own " "address"); return -1; } if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address, sta_addr, pos, end - pos) < 0) return -1; } else if (frame->packet_type == FT_PACKET_RESPONSE) { u16 status_code; if (end - pos < 2) { wpa_printf(MSG_DEBUG, "FT: Not enough room for status " "code in RRB Response"); return -1; } status_code = WPA_GET_LE16(pos); pos += 2; wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response " "(status_code=%d)", status_code); if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0) return -1; } else { wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown " "packet_type %d", frame->packet_type); return -1; } if (end > pos) { wpa_hexdump(MSG_DEBUG, "FT: Ignore extra data in end", pos, end - pos); } return 0; } static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, struct wpa_ft_pmk_r0_sa *pmk_r0, struct ft_remote_r1kh *r1kh, const u8 *s1kh_id, int pairwise) { struct ft_r0kh_r1kh_push_frame frame, f; struct os_time now; const u8 *plain; u8 *crypt; os_memset(&frame, 0, sizeof(frame)); frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH; frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN); os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); /* aes_wrap() does not support inplace encryption, so use a temporary * buffer for the data. */ os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, s1kh_id, f.pmk_r1, f.pmk_r1_name); wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, WPA_PMK_NAME_LEN); os_get_time(&now); WPA_PUT_LE32(f.timestamp, now.sec); f.pairwise = host_to_le16(pairwise); os_memset(f.pad, 0, sizeof(f.pad)); plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp); crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp); if (aes_wrap(r1kh->key, sizeof(r1kh->key), (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, plain, crypt) < 0) return; wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); } void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) { struct wpa_ft_pmk_r0_sa *r0; struct ft_remote_r1kh *r1kh; if (!wpa_auth->conf.pmk_r1_push) return; r0 = wpa_auth->ft_pmk_cache->pmk_r0; while (r0) { if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) break; r0 = r0->next; } if (r0 == NULL || r0->pmk_r1_pushed) return; r0->pmk_r1_pushed = 1; wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs " "for STA " MACSTR, MAC2STR(addr)); r1kh = wpa_auth->conf.r1kh_list; while (r1kh) { wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise); r1kh = r1kh->next; } } #endif /* CONFIG_IEEE80211R */ Index: releng/11.1/contrib/wpa/src/ap/wpa_auth_i.h =================================================================== --- releng/11.1/contrib/wpa/src/ap/wpa_auth_i.h (revision 324698) +++ releng/11.1/contrib/wpa/src/ap/wpa_auth_i.h (revision 324699) @@ -1,259 +1,260 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_AUTH_I_H #define WPA_AUTH_I_H /* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ #define RSNA_MAX_EAPOL_RETRIES 4 struct wpa_group; struct wpa_stsl_negotiation { struct wpa_stsl_negotiation *next; u8 initiator[ETH_ALEN]; u8 peer[ETH_ALEN]; }; struct wpa_state_machine { struct wpa_authenticator *wpa_auth; struct wpa_group *group; u8 addr[ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; enum { WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2, WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART, WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2, WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE } wpa_ptk_state; enum { WPA_PTK_GROUP_IDLE = 0, WPA_PTK_GROUP_REKEYNEGOTIATING, WPA_PTK_GROUP_REKEYESTABLISHED, WPA_PTK_GROUP_KEYERROR } wpa_ptk_group_state; Boolean Init; Boolean DeauthenticationRequest; Boolean AuthenticationRequest; Boolean ReAuthenticationRequest; Boolean Disconnect; int TimeoutCtr; int GTimeoutCtr; Boolean TimeoutEvt; Boolean EAPOLKeyReceived; Boolean EAPOLKeyPairwise; Boolean EAPOLKeyRequest; Boolean MICVerified; Boolean GUpdateStationKeys; u8 ANonce[WPA_NONCE_LEN]; u8 SNonce[WPA_NONCE_LEN]; u8 alt_SNonce[WPA_NONCE_LEN]; u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 PMK[PMK_LEN]; struct wpa_ptk PTK; Boolean PTK_valid; Boolean pairwise_set; + Boolean tk_already_set; int keycount; Boolean Pair; struct wpa_key_replay_counter { u8 counter[WPA_REPLAY_COUNTER_LEN]; Boolean valid; } key_replay[RSNA_MAX_EAPOL_RETRIES], prev_key_replay[RSNA_MAX_EAPOL_RETRIES]; Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ Boolean PTKRequest; /* not in IEEE 802.11i state machine */ Boolean has_GTK; Boolean PtkGroupInit; /* init request for PTK Group state machine */ u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ size_t last_rx_eapol_key_len; unsigned int changed:1; unsigned int in_step_loop:1; unsigned int pending_deinit:1; unsigned int started:1; unsigned int mgmt_frame_prot:1; unsigned int rx_eapol_key_secure:1; unsigned int update_snonce:1; unsigned int alt_snonce_valid:1; #ifdef CONFIG_IEEE80211R unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; #endif /* CONFIG_IEEE80211R */ unsigned int is_wnmsleep:1; u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; u8 *wpa_ie; size_t wpa_ie_len; enum { WPA_VERSION_NO_WPA = 0 /* WPA not used */, WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */, WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */ } wpa; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ struct rsn_pmksa_cache_entry *pmksa; u32 dot11RSNAStatsTKIPLocalMICFailures; u32 dot11RSNAStatsTKIPRemoteMICFailures; #ifdef CONFIG_IEEE80211R u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ size_t xxkey_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth * Request */ u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */ size_t r0kh_id_len; u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key * message 2/4 */ u8 *assoc_resp_ftie; void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, const u8 *ies, size_t ies_len); void *ft_pending_cb_ctx; struct wpabuf *ft_pending_req_ies; u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; u8 ft_pending_auth_transaction; u8 ft_pending_current_ap[ETH_ALEN]; #endif /* CONFIG_IEEE80211R */ int pending_1_of_4_timeout; #ifdef CONFIG_P2P u8 ip_addr[4]; #endif /* CONFIG_P2P */ }; /* per group key state machine data */ struct wpa_group { struct wpa_group *next; int vlan_id; Boolean GInit; int GKeyDoneStations; Boolean GTKReKey; int GTK_len; int GN, GM; Boolean GTKAuthenticator; u8 Counter[WPA_NONCE_LEN]; enum { WPA_GROUP_GTK_INIT = 0, WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE, WPA_GROUP_FATAL_FAILURE } wpa_group_state; u8 GMK[WPA_GMK_LEN]; u8 GTK[2][WPA_GTK_MAX_LEN]; u8 GNonce[WPA_NONCE_LEN]; Boolean changed; Boolean first_sta_seen; Boolean reject_4way_hs_for_entropy; #ifdef CONFIG_IEEE80211W u8 IGTK[2][WPA_IGTK_MAX_LEN]; int GN_igtk, GM_igtk; #endif /* CONFIG_IEEE80211W */ /* Number of references except those in struct wpa_group->next */ unsigned int references; }; struct wpa_ft_pmk_cache; /* per authenticator data */ struct wpa_authenticator { struct wpa_group *group; unsigned int dot11RSNAStatsTKIPRemoteMICFailures; u32 dot11RSNAAuthenticationSuiteSelected; u32 dot11RSNAPairwiseCipherSelected; u32 dot11RSNAGroupCipherSelected; u8 dot11RSNAPMKIDUsed[PMKID_LEN]; u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */ u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */ u32 dot11RSNAGroupCipherRequested; /* FIX: update */ unsigned int dot11RSNATKIPCounterMeasuresInvoked; unsigned int dot11RSNA4WayHandshakeFailures; struct wpa_stsl_negotiation *stsl_negotiations; struct wpa_auth_config conf; struct wpa_auth_callbacks cb; u8 *wpa_ie; size_t wpa_ie_len; u8 addr[ETH_ALEN]; struct rsn_pmksa_cache *pmksa; struct wpa_ft_pmk_cache *ft_pmk_cache; #ifdef CONFIG_P2P struct bitfield *ip_pool; #endif /* CONFIG_P2P */ }; int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, const u8 *pmkid); void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *txt); void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *fmt, ...); void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int key_info, const u8 *key_rsc, const u8 *nonce, const u8 *kde, size_t kde_len, int keyidx, int encr, int force_version); int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx); int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx); #ifdef CONFIG_PEERKEY int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, struct wpa_stsl_negotiation *neg); void wpa_smk_error(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *key_data, size_t key_data_len); void wpa_smk_m1(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key, const u8 *key_data, size_t key_data_len); void wpa_smk_m3(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key, const u8 *key_data, size_t key_data_len); #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211R int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len); int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len); int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk); struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); void wpa_ft_install_ptk(struct wpa_state_machine *sm); #endif /* CONFIG_IEEE80211R */ #endif /* WPA_AUTH_I_H */ Index: releng/11.1/contrib/wpa/src/common/wpa_common.h =================================================================== --- releng/11.1/contrib/wpa/src/common/wpa_common.h (revision 324698) +++ releng/11.1/contrib/wpa/src/common/wpa_common.h (revision 324699) @@ -1,451 +1,463 @@ /* * WPA definitions shared between hostapd and wpa_supplicant * Copyright (c) 2002-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_COMMON_H #define WPA_COMMON_H /* IEEE 802.11i */ #define PMKID_LEN 16 #define PMK_LEN 32 #define WPA_REPLAY_COUNTER_LEN 8 #define WPA_NONCE_LEN 32 #define WPA_KEY_RSC_LEN 8 #define WPA_GMK_LEN 32 #define WPA_GTK_MAX_LEN 32 #define WPA_ALLOWED_PAIRWISE_CIPHERS \ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) #define WPA_ALLOWED_GROUP_CIPHERS \ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ WPA_CIPHER_GTK_NOT_USED) #define WPA_SELECTOR_LEN 4 #define WPA_VERSION 1 #define RSN_SELECTOR_LEN 4 #define RSN_VERSION 1 #define RSN_SELECTOR(a, b, c, d) \ ((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \ (u32) (d)) #define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) #define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2) #define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0) #define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) #define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2) #define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4) #define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2) #ifdef CONFIG_IEEE80211R #define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4) #endif /* CONFIG_IEEE80211R */ #define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8) #define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) #define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \ RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) #define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2) #if 0 #define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #endif #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4) #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) #define RSN_CIPHER_SUITE_GCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #define RSN_CIPHER_SUITE_CCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 10) #define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) #define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13) /* EAPOL-Key Key Data Encapsulation * GroupKey and PeerKey require encryption, otherwise, encryption is optional. */ #define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #if 0 #define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2) #endif #define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4) #ifdef CONFIG_PEERKEY #define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8) #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #endif /* CONFIG_IEEE80211W */ #define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10) #define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12) #define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4) #define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5) #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val)) #define RSN_SELECTOR_GET(a) WPA_GET_BE32((const u8 *) (a)) #define RSN_NUM_REPLAY_COUNTERS_1 0 #define RSN_NUM_REPLAY_COUNTERS_2 1 #define RSN_NUM_REPLAY_COUNTERS_4 2 #define RSN_NUM_REPLAY_COUNTERS_16 3 #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ #ifdef CONFIG_IEEE80211W #define WPA_IGTK_LEN 16 #define WPA_IGTK_MAX_LEN 32 #endif /* CONFIG_IEEE80211W */ /* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */ #define WPA_CAPABILITY_PREAUTH BIT(0) #define WPA_CAPABILITY_NO_PAIRWISE BIT(1) /* B2-B3: PTKSA Replay Counter */ /* B4-B5: GTKSA Replay Counter */ #define WPA_CAPABILITY_MFPR BIT(6) #define WPA_CAPABILITY_MFPC BIT(7) /* B8: Reserved */ #define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9) #define WPA_CAPABILITY_SPP_A_MSDU_CAPABLE BIT(10) #define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11) #define WPA_CAPABILITY_PBAC BIT(12) #define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13) /* B14-B15: Reserved */ /* IEEE 802.11r */ #define MOBILITY_DOMAIN_ID_LEN 2 #define FT_R0KH_ID_MAX_LEN 48 #define FT_R1KH_ID_LEN 6 #define WPA_PMK_NAME_LEN 16 /* IEEE 802.11, 8.5.2 EAPOL-Key frames */ #define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2))) #define WPA_KEY_INFO_TYPE_AKM_DEFINED 0 #define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0) #define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1) #define WPA_KEY_INFO_TYPE_AES_128_CMAC 3 #define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */ /* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */ #define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5)) #define WPA_KEY_INFO_KEY_INDEX_SHIFT 4 #define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */ #define WPA_KEY_INFO_TXRX BIT(6) /* group */ #define WPA_KEY_INFO_ACK BIT(7) #define WPA_KEY_INFO_MIC BIT(8) #define WPA_KEY_INFO_SECURE BIT(9) #define WPA_KEY_INFO_ERROR BIT(10) #define WPA_KEY_INFO_REQUEST BIT(11) #define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */ #define WPA_KEY_INFO_SMK_MESSAGE BIT(13) struct wpa_eapol_key { u8 type; /* Note: key_info, key_length, and key_data_length are unaligned */ u8 key_info[2]; /* big endian */ u8 key_length[2]; /* big endian */ u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 key_nonce[WPA_NONCE_LEN]; u8 key_iv[16]; u8 key_rsc[WPA_KEY_RSC_LEN]; u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ u8 key_mic[16]; u8 key_data_length[2]; /* big endian */ /* followed by key_data_length bytes of key_data */ } STRUCT_PACKED; struct wpa_eapol_key_192 { u8 type; /* Note: key_info, key_length, and key_data_length are unaligned */ u8 key_info[2]; /* big endian */ u8 key_length[2]; /* big endian */ u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 key_nonce[WPA_NONCE_LEN]; u8 key_iv[16]; u8 key_rsc[WPA_KEY_RSC_LEN]; u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ u8 key_mic[24]; u8 key_data_length[2]; /* big endian */ /* followed by key_data_length bytes of key_data */ } STRUCT_PACKED; #define WPA_EAPOL_KEY_MIC_MAX_LEN 24 #define WPA_KCK_MAX_LEN 24 #define WPA_KEK_MAX_LEN 32 #define WPA_TK_MAX_LEN 32 /** * struct wpa_ptk - WPA Pairwise Transient Key * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy */ struct wpa_ptk { u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */ u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */ u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */ size_t kck_len; size_t kek_len; size_t tk_len; + int installed; /* 1 if key has already been installed to driver */ }; +struct wpa_gtk { + u8 gtk[WPA_GTK_MAX_LEN]; + size_t gtk_len; +}; + +#ifdef CONFIG_IEEE80211W +struct wpa_igtk { + u8 igtk[WPA_IGTK_MAX_LEN]; + size_t igtk_len; +}; +#endif /* CONFIG_IEEE80211W */ /* WPA IE version 1 * 00-50-f2:1 (OUI:OUI type) * 0x01 0x00 (version; little endian) * (all following fields are optional:) * Group Suite Selector (4 octets) (default: TKIP) * Pairwise Suite Count (2 octets, little endian) (default: 1) * Pairwise Suite List (4 * n octets) (default: TKIP) * Authenticated Key Management Suite Count (2 octets, little endian) * (default: 1) * Authenticated Key Management Suite List (4 * n octets) * (default: unspec 802.1X) * WPA Capabilities (2 octets, little endian) (default: 0) */ struct wpa_ie_hdr { u8 elem_id; u8 len; u8 oui[4]; /* 24-bit OUI followed by 8-bit OUI type */ u8 version[2]; /* little endian */ } STRUCT_PACKED; /* 1/4: PMKID * 2/4: RSN IE * 3/4: one or two RSN IEs + GTK IE (encrypted) * 4/4: empty * 1/2: GTK IE (encrypted) * 2/2: empty */ /* RSN IE version 1 * 0x01 0x00 (version; little endian) * (all following fields are optional:) * Group Suite Selector (4 octets) (default: CCMP) * Pairwise Suite Count (2 octets, little endian) (default: 1) * Pairwise Suite List (4 * n octets) (default: CCMP) * Authenticated Key Management Suite Count (2 octets, little endian) * (default: 1) * Authenticated Key Management Suite List (4 * n octets) * (default: unspec 802.1X) * RSN Capabilities (2 octets, little endian) (default: 0) * PMKID Count (2 octets) (default: 0) * PMKID List (16 * n octets) * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC) */ struct rsn_ie_hdr { u8 elem_id; /* WLAN_EID_RSN */ u8 len; u8 version[2]; /* little endian */ } STRUCT_PACKED; #ifdef CONFIG_PEERKEY enum { STK_MUI_4WAY_STA_AP = 1, STK_MUI_4WAY_STAT_STA = 2, STK_MUI_GTK = 3, STK_MUI_SMK = 4 }; enum { STK_ERR_STA_NR = 1, STK_ERR_STA_NRSN = 2, STK_ERR_CPHR_NS = 3, STK_ERR_NO_STSL = 4 }; #endif /* CONFIG_PEERKEY */ struct rsn_error_kde { be16 mui; be16 error_type; } STRUCT_PACKED; #ifdef CONFIG_IEEE80211W #define WPA_IGTK_KDE_PREFIX_LEN (2 + 6) struct wpa_igtk_kde { u8 keyid[2]; u8 pn[6]; u8 igtk[WPA_IGTK_MAX_LEN]; } STRUCT_PACKED; #endif /* CONFIG_IEEE80211W */ struct rsn_mdie { u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 ft_capab; } STRUCT_PACKED; #define RSN_FT_CAPAB_FT_OVER_DS BIT(0) #define RSN_FT_CAPAB_FT_RESOURCE_REQ_SUPP BIT(1) struct rsn_ftie { u8 mic_control[2]; u8 mic[16]; u8 anonce[WPA_NONCE_LEN]; u8 snonce[WPA_NONCE_LEN]; /* followed by optional parameters */ } STRUCT_PACKED; #define FTIE_SUBELEM_R1KH_ID 1 #define FTIE_SUBELEM_GTK 2 #define FTIE_SUBELEM_R0KH_ID 3 #define FTIE_SUBELEM_IGTK 4 struct rsn_rdie { u8 id; u8 descr_count; le16 status_code; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, const u8 *buf, size_t len, u8 *mic); int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, struct wpa_ptk *ptk, int akmp, int cipher); #ifdef CONFIG_IEEE80211R int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, const u8 *ap_addr, u8 transaction_seqnum, const u8 *mdie, size_t mdie_len, const u8 *ftie, size_t ftie_len, const u8 *rsnie, size_t rsnie_len, const u8 *ric, size_t ric_len, u8 *mic); void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, const u8 *ssid, size_t ssid_len, const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name); void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, const u8 *s1kh_id, u8 *pmk_r1_name); void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, const u8 *r1kh_id, const u8 *s1kh_id, u8 *pmk_r1, u8 *pmk_r1_name); int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *pmk_r1_name, struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher); #endif /* CONFIG_IEEE80211R */ struct wpa_ie_data { int proto; int pairwise_cipher; int group_cipher; int key_mgmt; int capabilities; size_t num_pmkid; const u8 *pmkid; int mgmt_group_cipher; }; int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data); int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, u8 *pmkid, int use_sha256); #ifdef CONFIG_SUITEB int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, u8 *pmkid); #else /* CONFIG_SUITEB */ static inline int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, u8 *pmkid) { return -1; } #endif /* CONFIG_SUITEB */ #ifdef CONFIG_SUITEB192 int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, u8 *pmkid); #else /* CONFIG_SUITEB192 */ static inline int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, u8 *pmkid) { return -1; } #endif /* CONFIG_SUITEB192 */ const char * wpa_cipher_txt(int cipher); const char * wpa_key_mgmt_txt(int key_mgmt, int proto); u32 wpa_akm_to_suite(int akm); int wpa_compare_rsn_ie(int ft_initial_assoc, const u8 *ie1, size_t ie1len, const u8 *ie2, size_t ie2len); int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid); struct wpa_ft_ies { const u8 *mdie; size_t mdie_len; const u8 *ftie; size_t ftie_len; const u8 *r1kh_id; const u8 *gtk; size_t gtk_len; const u8 *r0kh_id; size_t r0kh_id_len; const u8 *rsn; size_t rsn_len; const u8 *rsn_pmkid; const u8 *tie; size_t tie_len; const u8 *igtk; size_t igtk_len; const u8 *ric; size_t ric_len; }; int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); int wpa_cipher_key_len(int cipher); int wpa_cipher_rsc_len(int cipher); int wpa_cipher_to_alg(int cipher); int wpa_cipher_valid_group(int cipher); int wpa_cipher_valid_pairwise(int cipher); int wpa_cipher_valid_mgmt_group(int cipher); u32 wpa_cipher_to_suite(int proto, int cipher); int rsn_cipher_put_suites(u8 *pos, int ciphers); int wpa_cipher_put_suites(u8 *pos, int ciphers); int wpa_pick_pairwise_cipher(int ciphers, int none_allowed); int wpa_pick_group_cipher(int ciphers); int wpa_parse_cipher(const char *value); int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim); int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise); unsigned int wpa_mic_len(int akmp); #endif /* WPA_COMMON_H */ Index: releng/11.1/contrib/wpa/src/rsn_supp/tdls.c =================================================================== --- releng/11.1/contrib/wpa/src/rsn_supp/tdls.c (revision 324698) +++ releng/11.1/contrib/wpa/src/rsn_supp/tdls.c (revision 324699) @@ -1,3008 +1,3050 @@ /* * wpa_supplicant - TDLS * Copyright (c) 2010-2011, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "utils/os.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "crypto/sha256.h" #include "crypto/crypto.h" #include "crypto/aes_wrap.h" #include "rsn_supp/wpa.h" #include "rsn_supp/wpa_ie.h" #include "rsn_supp/wpa_i.h" #include "drivers/driver.h" #include "l2_packet/l2_packet.h" #ifdef CONFIG_TDLS_TESTING #define TDLS_TESTING_LONG_FRAME BIT(0) #define TDLS_TESTING_ALT_RSN_IE BIT(1) #define TDLS_TESTING_DIFF_BSSID BIT(2) #define TDLS_TESTING_SHORT_LIFETIME BIT(3) #define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4) #define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5) #define TDLS_TESTING_LONG_LIFETIME BIT(6) #define TDLS_TESTING_CONCURRENT_INIT BIT(7) #define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8) #define TDLS_TESTING_DECLINE_RESP BIT(9) #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10) #define TDLS_TESTING_WRONG_MIC BIT(11) unsigned int tdls_testing = 0; #endif /* CONFIG_TDLS_TESTING */ #define TPK_LIFETIME 43200 /* 12 hours */ #define TPK_M1_RETRY_COUNT 3 #define TPK_M1_TIMEOUT 5000 /* in milliseconds */ #define TPK_M2_RETRY_COUNT 10 #define TPK_M2_TIMEOUT 500 /* in milliseconds */ #define TDLS_MIC_LEN 16 #define TDLS_TIMEOUT_LEN 4 struct wpa_tdls_ftie { u8 ie_type; /* FTIE */ u8 ie_len; u8 mic_ctrl[2]; u8 mic[TDLS_MIC_LEN]; u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */ u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */ /* followed by optional elements */ } STRUCT_PACKED; struct wpa_tdls_timeoutie { u8 ie_type; /* Timeout IE */ u8 ie_len; u8 interval_type; u8 value[TDLS_TIMEOUT_LEN]; } STRUCT_PACKED; struct wpa_tdls_lnkid { u8 ie_type; /* Link Identifier IE */ u8 ie_len; u8 bssid[ETH_ALEN]; u8 init_sta[ETH_ALEN]; u8 resp_sta[ETH_ALEN]; } STRUCT_PACKED; /* TDLS frame headers as per IEEE Std 802.11z-2010 */ struct wpa_tdls_frame { u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */ u8 category; /* Category */ u8 action; /* Action (enum tdls_frame_type) */ } STRUCT_PACKED; static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs); static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx); static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer); static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer); static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code); #define TDLS_MAX_IE_LEN 80 #define IEEE80211_MAX_SUPP_RATES 32 struct wpa_tdls_peer { struct wpa_tdls_peer *next; unsigned int reconfig_key:1; int initiator; /* whether this end was initiator for TDLS setup */ u8 addr[ETH_ALEN]; /* other end MAC address */ u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */ u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */ size_t rsnie_i_len; u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */ size_t rsnie_p_len; u32 lifetime; int cipher; /* Selected cipher (WPA_CIPHER_*) */ u8 dtoken; struct tpk { u8 kck[16]; /* TPK-KCK */ u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */ } tpk; int tpk_set; + int tk_set; /* TPK-TK configured to the driver */ int tpk_success; int tpk_in_progress; struct tpk_timer { u8 dest[ETH_ALEN]; int count; /* Retry Count */ int timer; /* Timeout in milliseconds */ u8 action_code; /* TDLS frame type */ u8 dialog_token; u16 status_code; u32 peer_capab; int buf_len; /* length of TPK message for retransmission */ u8 *buf; /* buffer for TPK message */ } sm_tmr; u16 capability; u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; size_t supp_rates_len; struct ieee80211_ht_capabilities *ht_capabilities; struct ieee80211_vht_capabilities *vht_capabilities; u8 qos_info; u16 aid; u8 *ext_capab; size_t ext_capab_len; u8 *supp_channels; size_t supp_channels_len; u8 *supp_oper_classes; size_t supp_oper_classes_len; u8 wmm_capable; /* channel switch currently enabled */ int chan_switch_enabled; }; static int wpa_tdls_get_privacy(struct wpa_sm *sm) { /* * Get info needed from supplicant to check if the current BSS supports * security. Other than OPEN mode, rest are considered secured * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake. */ return sm->pairwise_cipher != WPA_CIPHER_NONE; } static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len) { os_memcpy(pos, ie, ie_len); return pos + ie_len; } static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr, 0, 0, NULL, 0, NULL, 0) < 0) { wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from " "the driver"); return -1; } return 0; } static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { u8 key_len; u8 rsc[6]; enum wpa_alg alg; + if (peer->tk_set) { + /* + * This same TPK-TK has already been configured to the driver + * and this new configuration attempt (likely due to an + * unexpected retransmitted frame) would result in clearing + * the TX/RX sequence number which can break security, so must + * not allow that to happen. + */ + wpa_printf(MSG_INFO, "TDLS: TPK-TK for the peer " MACSTR + " has already been configured to the driver - do not reconfigure", + MAC2STR(peer->addr)); + return -1; + } + os_memset(rsc, 0, 6); switch (peer->cipher) { case WPA_CIPHER_CCMP: alg = WPA_ALG_CCMP; key_len = 16; break; case WPA_CIPHER_NONE: wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: " "NONE - do not use pairwise keys"); return -1; default: wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d", sm->pairwise_cipher); return -1; } + wpa_printf(MSG_DEBUG, "TDLS: Configure pairwise key for peer " MACSTR, + MAC2STR(peer->addr)); if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1, rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) { wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the " "driver"); return -1; } + peer->tk_set = 1; return 0; } static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, int initiator, const u8 *buf, size_t len) { return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token, status_code, peer_capab, initiator, buf, len); } static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, int initiator, const u8 *msg, size_t msg_len) { struct wpa_tdls_peer *peer; wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u " "dialog_token=%u status_code=%u peer_capab=%u initiator=%d " "msg_len=%u", MAC2STR(dest), action_code, dialog_token, status_code, peer_capab, initiator, (unsigned int) msg_len); if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token, status_code, peer_capab, initiator, msg, msg_len)) { wpa_printf(MSG_INFO, "TDLS: Failed to send message " "(action_code=%u)", action_code); return -1; } if (action_code == WLAN_TDLS_SETUP_CONFIRM || action_code == WLAN_TDLS_TEARDOWN || action_code == WLAN_TDLS_DISCOVERY_REQUEST || action_code == WLAN_TDLS_DISCOVERY_RESPONSE) return 0; /* No retries */ for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_INFO, "TDLS: No matching entry found for " "retry " MACSTR, MAC2STR(dest)); return 0; } eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); if (action_code == WLAN_TDLS_SETUP_RESPONSE) { peer->sm_tmr.count = TPK_M2_RETRY_COUNT; peer->sm_tmr.timer = TPK_M2_TIMEOUT; } else { peer->sm_tmr.count = TPK_M1_RETRY_COUNT; peer->sm_tmr.timer = TPK_M1_TIMEOUT; } /* Copy message to resend on timeout */ os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN); peer->sm_tmr.action_code = action_code; peer->sm_tmr.dialog_token = dialog_token; peer->sm_tmr.status_code = status_code; peer->sm_tmr.peer_capab = peer_capab; peer->sm_tmr.buf_len = msg_len; os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = os_malloc(msg_len); if (peer->sm_tmr.buf == NULL) return -1; os_memcpy(peer->sm_tmr.buf, msg, msg_len); wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered " "(action_code=%u)", action_code); eloop_register_timeout(peer->sm_tmr.timer / 1000, (peer->sm_tmr.timer % 1000) * 1000, wpa_tdls_tpk_retry_timeout, sm, peer); return 0; } static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer, u16 reason_code) { int ret; ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); /* disable the link after teardown was sent */ wpa_tdls_disable_peer_link(sm, peer); return ret; } static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; struct wpa_tdls_peer *peer = timeout_ctx; if (peer->sm_tmr.count) { peer->sm_tmr.count--; wpa_printf(MSG_INFO, "TDLS: Retrying sending of message " "(action_code=%u)", peer->sm_tmr.action_code); if (peer->sm_tmr.buf == NULL) { wpa_printf(MSG_INFO, "TDLS: No retry buffer available " "for action_code=%u", peer->sm_tmr.action_code); eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); return; } /* resend TPK Handshake Message to Peer */ if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest, peer->sm_tmr.action_code, peer->sm_tmr.dialog_token, peer->sm_tmr.status_code, peer->sm_tmr.peer_capab, peer->initiator, peer->sm_tmr.buf, peer->sm_tmr.buf_len)) { wpa_printf(MSG_INFO, "TDLS: Failed to retry " "transmission"); } eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); eloop_register_timeout(peer->sm_tmr.timer / 1000, (peer->sm_tmr.timer % 1000) * 1000, wpa_tdls_tpk_retry_timeout, sm, peer); } else { eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request"); wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm, struct wpa_tdls_peer *peer, u8 action_code) { if (action_code == peer->sm_tmr.action_code) { wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for " "action_code=%u", action_code); /* Cancel Timeout registered */ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); /* free all resources meant for retry */ os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = NULL; peer->sm_tmr.count = 0; peer->sm_tmr.timer = 0; peer->sm_tmr.buf_len = 0; peer->sm_tmr.action_code = 0xff; } else { wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout " "(Unknown action_code=%u)", action_code); } } static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer, const u8 *own_addr, const u8 *bssid) { u8 key_input[SHA256_MAC_LEN]; const u8 *nonce[2]; size_t len[2]; u8 data[3 * ETH_ALEN]; /* IEEE Std 802.11z-2010 8.5.9.1: * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce)) */ len[0] = WPA_NONCE_LEN; len[1] = WPA_NONCE_LEN; if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) { nonce[0] = peer->inonce; nonce[1] = peer->rnonce; } else { nonce[0] = peer->rnonce; nonce[1] = peer->inonce; } wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN); sha256_vector(2, nonce, len, key_input); wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input", key_input, SHA256_MAC_LEN); /* * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK", * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY) * TODO: is N_KEY really included in KDF Context and if so, in which * presentation format (little endian 16-bit?) is it used? It gets * added by the KDF anyway.. */ if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) { os_memcpy(data, own_addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN); } else { os_memcpy(data, peer->addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN); } os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN); wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data)); sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data), (u8 *) &peer->tpk, sizeof(peer->tpk)); wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK", peer->tpk.kck, sizeof(peer->tpk.kck)); wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK", peer->tpk.tk, sizeof(peer->tpk.tk)); peer->tpk_set = 1; } /** * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC * @kck: TPK-KCK * @lnkid: Pointer to the beginning of Link Identifier IE * @rsnie: Pointer to the beginning of RSN IE used for handshake * @timeoutie: Pointer to the beginning of Timeout IE used for handshake * @ftie: Pointer to the beginning of FT IE * @mic: Pointer for writing MIC * * Calculate MIC for TDLS frame. */ static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid, const u8 *rsnie, const u8 *timeoutie, const u8 *ftie, u8 *mic) { u8 *buf, *pos; struct wpa_tdls_ftie *_ftie; const struct wpa_tdls_lnkid *_lnkid; int ret; int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] + 2 + timeoutie[1] + 2 + ftie[1]; buf = os_zalloc(len); if (!buf) { wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation"); return -1; } pos = buf; _lnkid = (const struct wpa_tdls_lnkid *) lnkid; /* 1) TDLS initiator STA MAC address */ os_memcpy(pos, _lnkid->init_sta, ETH_ALEN); pos += ETH_ALEN; /* 2) TDLS responder STA MAC address */ os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN); pos += ETH_ALEN; /* 3) Transaction Sequence number */ *pos++ = trans_seq; /* 4) Link Identifier IE */ os_memcpy(pos, lnkid, 2 + lnkid[1]); pos += 2 + lnkid[1]; /* 5) RSN IE */ os_memcpy(pos, rsnie, 2 + rsnie[1]); pos += 2 + rsnie[1]; /* 6) Timeout Interval IE */ os_memcpy(pos, timeoutie, 2 + timeoutie[1]); pos += 2 + timeoutie[1]; /* 7) FTIE, with the MIC field of the FTIE set to 0 */ os_memcpy(pos, ftie, 2 + ftie[1]); _ftie = (struct wpa_tdls_ftie *) pos; os_memset(_ftie->mic, 0, TDLS_MIC_LEN); pos += 2 + ftie[1]; wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf); wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16); ret = omac1_aes_128(kck, buf, pos - buf, mic); os_free(buf); wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16); return ret; } /** * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame * @kck: TPK-KCK * @trans_seq: Transaction Sequence Number (4 - Teardown) * @rcode: Reason code for Teardown * @dtoken: Dialog Token used for that particular link * @lnkid: Pointer to the beginning of Link Identifier IE * @ftie: Pointer to the beginning of FT IE * @mic: Pointer for writing MIC * * Calculate MIC for TDLS frame. */ static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode, u8 dtoken, const u8 *lnkid, const u8 *ftie, u8 *mic) { u8 *buf, *pos; struct wpa_tdls_ftie *_ftie; int ret; int len; if (lnkid == NULL) return -1; len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) + sizeof(trans_seq) + 2 + ftie[1]; buf = os_zalloc(len); if (!buf) { wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation"); return -1; } pos = buf; /* 1) Link Identifier IE */ os_memcpy(pos, lnkid, 2 + lnkid[1]); pos += 2 + lnkid[1]; /* 2) Reason Code */ WPA_PUT_LE16(pos, rcode); pos += sizeof(rcode); /* 3) Dialog token */ *pos++ = dtoken; /* 4) Transaction Sequence number */ *pos++ = trans_seq; /* 7) FTIE, with the MIC field of the FTIE set to 0 */ os_memcpy(pos, ftie, 2 + ftie[1]); _ftie = (struct wpa_tdls_ftie *) pos; os_memset(_ftie->mic, 0, TDLS_MIC_LEN); pos += 2 + ftie[1]; wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf); wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16); ret = omac1_aes_128(kck, buf, pos - buf, mic); os_free(buf); wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16); return ret; } static int wpa_supplicant_verify_tdls_mic(u8 trans_seq, struct wpa_tdls_peer *peer, const u8 *lnkid, const u8 *timeoutie, const struct wpa_tdls_ftie *ftie) { u8 mic[16]; if (peer->tpk_set) { wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid, peer->rsnie_p, timeoutie, (u8 *) ftie, mic); if (os_memcmp_const(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - " "dropping packet"); wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC", ftie->mic, 16); wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC", mic, 16); return -1; } } else { wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, " "TPK not set - dropping packet"); return -1; } return 0; } static int wpa_supplicant_verify_tdls_mic_teardown( u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer, const u8 *lnkid, const struct wpa_tdls_ftie *ftie) { u8 mic[16]; if (peer->tpk_set) { wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode, dtoken, lnkid, (u8 *) ftie, mic); if (os_memcmp_const(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - " "dropping packet"); return -1; } } else { wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown " "MIC, TPK not set - dropping packet"); return -1; } return 0; } static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; struct wpa_tdls_peer *peer = timeout_ctx; /* * On TPK lifetime expiration, we have an option of either tearing down * the direct link or trying to re-initiate it. The selection of what * to do is not strictly speaking controlled by our role in the expired * link, but for now, use that to select whether to renew or tear down * the link. */ if (peer->initiator) { wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR " - try to renew", MAC2STR(peer->addr)); wpa_tdls_start(sm, peer->addr); } else { wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR " - tear down", MAC2STR(peer->addr)); wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { struct wpa_tdls_peer *cur, *prev; cur = sm->tdls; prev = NULL; while (cur && cur != peer) { prev = cur; cur = cur->next; } if (cur != peer) { wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR " to remove it from the list", MAC2STR(peer->addr)); return; } if (prev) prev->next = peer->next; else sm->tdls = peer->next; } static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR, MAC2STR(peer->addr)); eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); peer->reconfig_key = 0; peer->initiator = 0; peer->tpk_in_progress = 0; os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = NULL; os_free(peer->ht_capabilities); peer->ht_capabilities = NULL; os_free(peer->vht_capabilities); peer->vht_capabilities = NULL; os_free(peer->ext_capab); peer->ext_capab = NULL; os_free(peer->supp_channels); peer->supp_channels = NULL; os_free(peer->supp_oper_classes); peer->supp_oper_classes = NULL; peer->rsnie_i_len = peer->rsnie_p_len = 0; peer->cipher = 0; peer->qos_info = 0; peer->wmm_capable = 0; - peer->tpk_set = peer->tpk_success = 0; + peer->tk_set = peer->tpk_set = peer->tpk_success = 0; peer->chan_switch_enabled = 0; os_memset(&peer->tpk, 0, sizeof(peer->tpk)); os_memset(peer->inonce, 0, WPA_NONCE_LEN); os_memset(peer->rnonce, 0, WPA_NONCE_LEN); } static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { wpa_tdls_peer_clear(sm, peer); wpa_tdls_peer_remove_from_list(sm, peer); os_free(peer); } static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer, struct wpa_tdls_lnkid *lnkid) { lnkid->ie_type = WLAN_EID_LINK_ID; lnkid->ie_len = 3 * ETH_ALEN; os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN); if (peer->initiator) { os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN); os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN); } else { os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN); os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN); } } static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code) { struct wpa_tdls_peer *peer; struct wpa_tdls_ftie *ftie; struct wpa_tdls_lnkid lnkid; u8 dialog_token; u8 *rbuf, *pos; int ielen; if (sm->tdls_disabled || !sm->tdls_supported) return -1; /* Find the node and free from the list */ for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_INFO, "TDLS: No matching entry found for " "Teardown " MACSTR, MAC2STR(addr)); return 0; } /* Cancel active channel switch before teardown */ if (peer->chan_switch_enabled) { wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR " to base channel", MAC2STR(addr)); wpa_sm_tdls_disable_channel_switch(sm, peer->addr); } dialog_token = peer->dtoken; wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR, MAC2STR(addr)); ielen = 0; if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) { /* To add FTIE for Teardown request and compute MIC */ ielen += sizeof(*ftie); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) ielen += 170; #endif /* CONFIG_TDLS_TESTING */ } rbuf = os_zalloc(ielen + 1); if (rbuf == NULL) return -1; pos = rbuf; if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) goto skip_ies; ftie = (struct wpa_tdls_ftie *) pos; ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; /* Using the recent nonce which should be for CONFIRM frame */ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; pos = (u8 *) (ftie + 1); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " "FTIE"); ftie->ie_len += 170; *pos++ = 255; /* FTIE subelem */ *pos++ = 168; /* FTIE subelem length */ pos += 168; } #endif /* CONFIG_TDLS_TESTING */ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake", (u8 *) ftie, pos - (u8 *) ftie); /* compute MIC before sending */ wpa_tdls_linkid(sm, peer, &lnkid); wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code, dialog_token, (u8 *) &lnkid, (u8 *) ftie, ftie->mic); skip_ies: /* TODO: register for a Timeout handler, if Teardown is not received at * the other end, then try again another time */ /* request driver to send Teardown using this FTIE */ wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0, reason_code, 0, peer->initiator, rbuf, pos - rbuf); os_free(rbuf); return 0; } int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code) { struct wpa_tdls_peer *peer; if (sm->tdls_disabled || !sm->tdls_supported) return -1; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR " for link Teardown", MAC2STR(addr)); return -1; } if (!peer->tpk_success) { wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR " not connected - cannot Teardown link", MAC2STR(addr)); return -1; } return wpa_tdls_do_teardown(sm, peer, reason_code); } static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); wpa_tdls_peer_free(sm, peer); } void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (!peer || !peer->tpk_success) { wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR " not connected - cannot teardown unreachable link", MAC2STR(addr)); return; } if (wpa_tdls_is_external_setup(sm)) { /* * Get us on the base channel, disable the link, send a * teardown packet through the AP, and then reset link data. */ if (peer->chan_switch_enabled) wpa_sm_tdls_disable_channel_switch(sm, peer->addr); wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr); wpa_tdls_send_teardown(sm, addr, WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE); wpa_tdls_peer_free(sm, peer); } else { wpa_tdls_disable_peer_link(sm, peer); } } const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; if (sm->tdls_disabled || !sm->tdls_supported) return "disabled"; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (peer == NULL) return "peer does not exist"; if (!peer->tpk_success) return "peer not connected"; return "connected"; } static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_tdls_peer *peer = NULL; struct wpa_tdls_ftie *ftie; struct wpa_tdls_lnkid *lnkid; struct wpa_eapol_ie_parse kde; u16 reason_code; const u8 *pos; int ielen; /* Find the node and free from the list */ for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_INFO, "TDLS: No matching entry found for " "Teardown " MACSTR, MAC2STR(src_addr)); return 0; } pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; reason_code = WPA_GET_LE16(pos); pos += 2; wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR " (reason code %u)", MAC2STR(src_addr), reason_code); ielen = len - (pos - buf); /* start of IE in buf */ /* * Don't reject the message if failing to parse IEs. The IEs we need are * explicitly checked below. Some APs may add arbitrary padding to the * end of short TDLS frames and that would look like invalid IEs. */ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) wpa_printf(MSG_DEBUG, "TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround"); if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS " "Teardown"); return -1; } lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) goto skip_ftie; if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown"); return -1; } ftie = (struct wpa_tdls_ftie *) kde.ftie; /* Process MIC check to see if TDLS Teardown is right */ if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code, peer->dtoken, peer, (u8 *) lnkid, ftie) < 0) { wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS " "Teardown Request from " MACSTR, MAC2STR(src_addr)); return -1; } skip_ftie: /* * Request the driver to disable the direct link and clear associated * keys. */ wpa_tdls_disable_peer_link(sm, peer); return 0; } /** * wpa_tdls_send_error - To send suitable TDLS status response with * appropriate status code mentioning reason for error/failure. * @dst - MAC addr of Peer station * @tdls_action - TDLS frame type for which error code is sent * @initiator - was this end the initiator of the connection * @status - status code mentioning reason */ static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst, u8 tdls_action, u8 dialog_token, int initiator, u16 status) { wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR " (action=%u status=%u)", MAC2STR(dst), tdls_action, status); return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status, 0, initiator, NULL, 0); } static struct wpa_tdls_peer * wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing) { struct wpa_tdls_peer *peer; if (existing) *existing = 0; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) { if (existing) *existing = 1; return peer; /* re-use existing entry */ } } wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR, MAC2STR(addr)); peer = os_zalloc(sizeof(*peer)); if (peer == NULL) return NULL; os_memcpy(peer->addr, addr, ETH_ALEN); peer->next = sm->tdls; sm->tdls = peer; return peer; } static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { size_t buf_len; struct wpa_tdls_timeoutie timeoutie; u16 rsn_capab; struct wpa_tdls_ftie *ftie; u8 *rbuf, *pos, *count_pos; u16 count; struct rsn_ie_hdr *hdr; int status; if (!wpa_tdls_get_privacy(sm)) { wpa_printf(MSG_DEBUG, "TDLS: No security used on the link"); peer->rsnie_i_len = 0; goto skip_rsnie; } /* * TPK Handshake Message 1: * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I, * Timeout Interval IE)) */ /* Filling RSN IE */ hdr = (struct rsn_ie_hdr *) peer->rsnie_i; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); pos += RSN_SELECTOR_LEN; count_pos = pos; pos += 2; count = 0; /* * AES-CCMP is the default Encryption preferred for TDLS, so * RSN IE is filled only with CCMP CIPHER * Note: TKIP is not used to encrypt TDLS link. * * Regardless of the cipher used on the AP connection, select CCMP * here. */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; count++; WPA_PUT_LE16(count_pos, count); WPA_PUT_LE16(pos, 1); pos += 2; RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); pos += RSN_SELECTOR_LEN; rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) { wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for " "testing"); rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; } #endif /* CONFIG_TDLS_TESTING */ WPA_PUT_LE16(pos, rsn_capab); pos += 2; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) { /* Number of PMKIDs */ *pos++ = 0x00; *pos++ = 0x00; } #endif /* CONFIG_TDLS_TESTING */ hdr->len = (pos - peer->rsnie_i) - 2; peer->rsnie_i_len = pos - peer->rsnie_i; wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake", peer->rsnie_i, peer->rsnie_i_len); skip_rsnie: buf_len = 0; if (wpa_tdls_get_privacy(sm)) buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + sizeof(struct wpa_tdls_timeoutie); #ifdef CONFIG_TDLS_TESTING if (wpa_tdls_get_privacy(sm) && (tdls_testing & TDLS_TESTING_LONG_FRAME)) buf_len += 170; if (tdls_testing & TDLS_TESTING_DIFF_BSSID) buf_len += sizeof(struct wpa_tdls_lnkid); #endif /* CONFIG_TDLS_TESTING */ rbuf = os_zalloc(buf_len + 1); if (rbuf == NULL) { wpa_tdls_peer_free(sm, peer); return -1; } pos = rbuf; if (!wpa_tdls_get_privacy(sm)) goto skip_ies; /* Initiator RSN IE */ pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len); ftie = (struct wpa_tdls_ftie *) pos; ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; if (os_get_random(peer->inonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "TDLS: Failed to get random data for initiator Nonce"); os_free(rbuf); wpa_tdls_peer_free(sm, peer); return -1; } + peer->tk_set = 0; /* A new nonce results in a new TK */ wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake", peer->inonce, WPA_NONCE_LEN); os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1", (u8 *) ftie, sizeof(struct wpa_tdls_ftie)); pos = (u8 *) (ftie + 1); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " "FTIE"); ftie->ie_len += 170; *pos++ = 255; /* FTIE subelem */ *pos++ = 168; /* FTIE subelem length */ pos += 168; } #endif /* CONFIG_TDLS_TESTING */ /* Lifetime */ peer->lifetime = TPK_LIFETIME; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK " "lifetime"); peer->lifetime = 301; } if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK " "lifetime"); peer->lifetime = 0xffffffff; } #endif /* CONFIG_TDLS_TESTING */ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, sizeof(timeoutie), peer->lifetime); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime); skip_ies: #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_DIFF_BSSID) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in " "Link Identifier"); struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos; wpa_tdls_linkid(sm, peer, l); l->bssid[5] ^= 0x01; pos += sizeof(*l); } #endif /* CONFIG_TDLS_TESTING */ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK " "Handshake Message 1 (peer " MACSTR ")", MAC2STR(peer->addr)); status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0, 0, peer->initiator, rbuf, pos - rbuf); os_free(rbuf); return status; } static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, const unsigned char *src_addr, u8 dtoken, struct wpa_tdls_lnkid *lnkid, const struct wpa_tdls_peer *peer) { u8 *rbuf, *pos; size_t buf_len; u32 lifetime; struct wpa_tdls_timeoutie timeoutie; struct wpa_tdls_ftie *ftie; int status; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce), * Lifetime */ buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + sizeof(struct wpa_tdls_timeoutie); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) buf_len += 170; #endif /* CONFIG_TDLS_TESTING */ } rbuf = os_zalloc(buf_len + 1); if (rbuf == NULL) return -1; pos = rbuf; if (!wpa_tdls_get_privacy(sm)) goto skip_ies; /* Peer RSN IE */ pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len); ftie = (struct wpa_tdls_ftie *) pos; ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; /* TODO: ftie->mic_control to set 2-RESPONSE */ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2", (u8 *) ftie, sizeof(*ftie)); pos = (u8 *) (ftie + 1); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " "FTIE"); ftie->ie_len += 170; *pos++ = 255; /* FTIE subelem */ *pos++ = 168; /* FTIE subelem length */ pos += 168; } #endif /* CONFIG_TDLS_TESTING */ /* Lifetime */ lifetime = peer->lifetime; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK " "lifetime in response"); lifetime++; } #endif /* CONFIG_TDLS_TESTING */ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, sizeof(timeoutie), lifetime); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator", lifetime); /* compute MIC before sending */ wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p, (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_WRONG_MIC) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC"); ftie->mic[0] ^= 0x01; } #endif /* CONFIG_TDLS_TESTING */ skip_ies: status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, 0, peer->initiator, rbuf, pos - rbuf); os_free(rbuf); return status; } static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm, const unsigned char *src_addr, u8 dtoken, struct wpa_tdls_lnkid *lnkid, const struct wpa_tdls_peer *peer) { u8 *rbuf, *pos; size_t buf_len; struct wpa_tdls_ftie *ftie; struct wpa_tdls_timeoutie timeoutie; u32 lifetime; int status; u32 peer_capab = 0; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce), * Lifetime */ buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + sizeof(struct wpa_tdls_timeoutie); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) buf_len += 170; #endif /* CONFIG_TDLS_TESTING */ } rbuf = os_zalloc(buf_len + 1); if (rbuf == NULL) return -1; pos = rbuf; if (!wpa_tdls_get_privacy(sm)) goto skip_ies; /* Peer RSN IE */ pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len); ftie = (struct wpa_tdls_ftie *) pos; ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; /*TODO: ftie->mic_control to set 3-CONFIRM */ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; pos = (u8 *) (ftie + 1); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_LONG_FRAME) { wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " "FTIE"); ftie->ie_len += 170; *pos++ = 255; /* FTIE subelem */ *pos++ = 168; /* FTIE subelem length */ pos += 168; } #endif /* CONFIG_TDLS_TESTING */ /* Lifetime */ lifetime = peer->lifetime; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK " "lifetime in confirm"); lifetime++; } #endif /* CONFIG_TDLS_TESTING */ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, sizeof(timeoutie), lifetime); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime); /* compute MIC before sending */ wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p, (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_WRONG_MIC) { wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC"); ftie->mic[0] ^= 0x01; } #endif /* CONFIG_TDLS_TESTING */ skip_ies: if (peer->vht_capabilities) peer_capab |= TDLS_PEER_VHT; if (peer->ht_capabilities) peer_capab |= TDLS_PEER_HT; if (peer->wmm_capable) peer_capab |= TDLS_PEER_WMM; status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0, peer_capab, peer->initiator, rbuf, pos - rbuf); os_free(rbuf); return status; } static int wpa_tdls_send_discovery_response(struct wpa_sm *sm, struct wpa_tdls_peer *peer, u8 dialog_token) { size_t buf_len = 0; struct wpa_tdls_timeoutie timeoutie; u16 rsn_capab; u8 *rbuf, *pos, *count_pos; u16 count; struct rsn_ie_hdr *hdr; int status; wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response " "(peer " MACSTR ")", MAC2STR(peer->addr)); if (!wpa_tdls_get_privacy(sm)) goto skip_rsn_ies; /* Filling RSN IE */ hdr = (struct rsn_ie_hdr *) peer->rsnie_i; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); pos += RSN_SELECTOR_LEN; count_pos = pos; pos += 2; count = 0; /* * AES-CCMP is the default encryption preferred for TDLS, so * RSN IE is filled only with CCMP cipher suite. * Note: TKIP is not used to encrypt TDLS link. * * Regardless of the cipher used on the AP connection, select CCMP * here. */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; count++; WPA_PUT_LE16(count_pos, count); WPA_PUT_LE16(pos, 1); pos += 2; RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); pos += RSN_SELECTOR_LEN; rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; WPA_PUT_LE16(pos, rsn_capab); pos += 2; hdr->len = (pos - (u8 *) hdr) - 2; peer->rsnie_i_len = pos - peer->rsnie_i; wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response", (u8 *) hdr, hdr->len + 2); skip_rsn_ies: buf_len = 0; if (wpa_tdls_get_privacy(sm)) { /* Peer RSN IE, Lifetime */ buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_timeoutie); } rbuf = os_zalloc(buf_len + 1); if (rbuf == NULL) { wpa_tdls_peer_free(sm, peer); return -1; } pos = rbuf; if (!wpa_tdls_get_privacy(sm)) goto skip_ies; /* Initiator RSN IE */ pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len); /* Lifetime */ peer->lifetime = TPK_LIFETIME; pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, sizeof(timeoutie), peer->lifetime); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime); skip_ies: status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE, dialog_token, 0, 0, 0, rbuf, pos - rbuf); os_free(rbuf); return status; } static int wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr, const u8 *buf, size_t len) { struct wpa_eapol_ie_parse kde; const struct wpa_tdls_lnkid *lnkid; struct wpa_tdls_peer *peer; size_t min_req_len = sizeof(struct wpa_tdls_frame) + 1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid); u8 dialog_token; wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR, MAC2STR(addr)); if (len < min_req_len) { wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: " "%d", (int) len); return -1; } dialog_token = buf[sizeof(struct wpa_tdls_frame)]; /* * Some APs will tack on a weird IE to the end of a TDLS * discovery request packet. This needn't fail the response, * since the required IE are verified separately. */ if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1, len - (sizeof(struct wpa_tdls_frame) + 1), &kde) < 0) { wpa_printf(MSG_DEBUG, "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround"); } if (!kde.lnkid) { wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery " "Request"); return -1; } lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid; if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different " " BSS " MACSTR, MAC2STR(lnkid->bssid)); return -1; } peer = wpa_tdls_add_peer(sm, addr, NULL); if (peer == NULL) return -1; return wpa_tdls_send_discovery_response(sm, peer, dialog_token); } int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr) { if (sm->tdls_disabled || !sm->tdls_supported) return -1; wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer " MACSTR, MAC2STR(addr)); return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST, 1, 0, 0, 1, NULL, 0); } static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->supp_rates) { wpa_printf(MSG_DEBUG, "TDLS: No supported rates received"); return -1; } peer->supp_rates_len = merge_byte_arrays( peer->supp_rates, sizeof(peer->supp_rates), kde->supp_rates + 2, kde->supp_rates_len - 2, kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL, kde->ext_supp_rates_len - 2); return 0; } static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->ht_capabilities) { wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities " "received"); return 0; } if (!peer->ht_capabilities) { peer->ht_capabilities = os_zalloc(sizeof(struct ieee80211_ht_capabilities)); if (peer->ht_capabilities == NULL) return -1; } os_memcpy(peer->ht_capabilities, kde->ht_capabilities, sizeof(struct ieee80211_ht_capabilities)); wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities", (u8 *) peer->ht_capabilities, sizeof(struct ieee80211_ht_capabilities)); return 0; } static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->vht_capabilities) { wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities " "received"); return 0; } if (!peer->vht_capabilities) { peer->vht_capabilities = os_zalloc(sizeof(struct ieee80211_vht_capabilities)); if (peer->vht_capabilities == NULL) return -1; } os_memcpy(peer->vht_capabilities, kde->vht_capabilities, sizeof(struct ieee80211_vht_capabilities)); wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities", (u8 *) peer->vht_capabilities, sizeof(struct ieee80211_vht_capabilities)); return 0; } static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->ext_capab) { wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities " "received"); return 0; } if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) { /* Need to allocate buffer to fit the new information */ os_free(peer->ext_capab); peer->ext_capab = os_zalloc(kde->ext_capab_len - 2); if (peer->ext_capab == NULL) return -1; } peer->ext_capab_len = kde->ext_capab_len - 2; os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len); return 0; } static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { struct wmm_information_element *wmm; if (!kde->wmm) { wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received"); return 0; } if (kde->wmm_len < sizeof(struct wmm_information_element)) { wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received"); return -1; } wmm = (struct wmm_information_element *) kde->wmm; peer->qos_info = wmm->qos_info; peer->wmm_capable = 1; wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info); return 0; } static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->supp_channels) { wpa_printf(MSG_DEBUG, "TDLS: No supported channels received"); return 0; } if (!peer->supp_channels || peer->supp_channels_len < kde->supp_channels_len) { os_free(peer->supp_channels); peer->supp_channels = os_zalloc(kde->supp_channels_len); if (peer->supp_channels == NULL) return -1; } peer->supp_channels_len = kde->supp_channels_len; os_memcpy(peer->supp_channels, kde->supp_channels, peer->supp_channels_len); wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels", (u8 *) peer->supp_channels, peer->supp_channels_len); return 0; } static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { if (!kde->supp_oper_classes) { wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received"); return 0; } if (!peer->supp_oper_classes || peer->supp_oper_classes_len < kde->supp_oper_classes_len) { os_free(peer->supp_oper_classes); peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len); if (peer->supp_oper_classes == NULL) return -1; } peer->supp_oper_classes_len = kde->supp_oper_classes_len; os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes, peer->supp_oper_classes_len); wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes", (u8 *) peer->supp_oper_classes, peer->supp_oper_classes_len); return 0; } static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer, int add) { return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid, peer->capability, peer->supp_rates, peer->supp_rates_len, peer->ht_capabilities, peer->vht_capabilities, peer->qos_info, peer->wmm_capable, peer->ext_capab, peer->ext_capab_len, peer->supp_channels, peer->supp_channels_len, peer->supp_oper_classes, peer->supp_oper_classes_len); } +static int tdls_nonce_set(const u8 *nonce) +{ + int i; + + for (i = 0; i < WPA_NONCE_LEN; i++) { + if (nonce[i]) + return 1; + } + + return 0; +} + + static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_tdls_peer *peer; struct wpa_eapol_ie_parse kde; struct wpa_ie_data ie; int cipher; const u8 *cpos; struct wpa_tdls_ftie *ftie = NULL; struct wpa_tdls_timeoutie *timeoutie; struct wpa_tdls_lnkid *lnkid; u32 lifetime = 0; #if 0 struct rsn_ie_hdr *hdr; u8 *pos; u16 rsn_capab; u16 rsn_ver; #endif u8 dtoken; u16 ielen; u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE; int tdls_prohibited = sm->tdls_prohibited; int existing_peer = 0; if (len < 3 + 3) return -1; cpos = buf; cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; /* driver had already verified the frame format */ dtoken = *cpos++; /* dialog token */ wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken); peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer); if (peer == NULL) goto error; /* If found, use existing entry instead of adding a new one; * how to handle the case where both ends initiate at the * same time? */ if (existing_peer) { if (peer->tpk_success) { wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while " "direct link is enabled - tear down the " "old link first"); wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); wpa_tdls_peer_clear(sm, peer); } else if (peer->initiator) { /* * An entry is already present, so check if we already * sent a TDLS Setup Request. If so, compare MAC * addresses and let the STA with the lower MAC address * continue as the initiator. The other negotiation is * terminated. */ if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) { wpa_printf(MSG_DEBUG, "TDLS: Discard request " "from peer with higher address " MACSTR, MAC2STR(src_addr)); return -1; } else { wpa_printf(MSG_DEBUG, "TDLS: Accept request " "from peer with lower address " MACSTR " (terminate previously " "initiated negotiation", MAC2STR(src_addr)); wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); wpa_tdls_peer_clear(sm, peer); } } } /* capability information */ peer->capability = WPA_GET_LE16(cpos); cpos += 2; ielen = len - (cpos - buf); /* start of IE in buf */ /* * Don't reject the message if failing to parse IEs. The IEs we need are * explicitly checked below. Some APs may add arbitrary padding to the * end of short TDLS frames and that would look like invalid IEs. */ if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) wpa_printf(MSG_DEBUG, "TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround"); if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " "TPK M1"); goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1", kde.lnkid, kde.lnkid_len); lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS"); status = WLAN_STATUS_REQUEST_DECLINED; goto error; } wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR, MAC2STR(src_addr)); if (copy_supp_rates(&kde, peer) < 0) goto error; if (copy_peer_ht_capab(&kde, peer) < 0) goto error; if (copy_peer_vht_capab(&kde, peer) < 0) goto error; if (copy_peer_ext_capab(&kde, peer) < 0) goto error; if (copy_peer_supp_channels(&kde, peer) < 0) goto error; if (copy_peer_supp_oper_classes(&kde, peer) < 0) goto error; peer->qos_info = kde.qosinfo; /* Overwrite with the qos_info obtained in WMM IE */ if (copy_peer_wmm_capab(&kde, peer) < 0) goto error; peer->aid = kde.aid; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { peer = wpa_tdls_add_peer(sm, src_addr, NULL); if (peer == NULL) goto error; wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of " "TDLS setup - send own request"); peer->initiator = 1; wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0); wpa_tdls_send_tpk_m1(sm, peer); } if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) && tdls_prohibited) { wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition " "on TDLS"); tdls_prohibited = 0; } #endif /* CONFIG_TDLS_TESTING */ if (tdls_prohibited) { wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS"); status = WLAN_STATUS_REQUEST_DECLINED; goto error; } if (!wpa_tdls_get_privacy(sm)) { if (kde.rsn_ie) { wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while " "security is disabled"); status = WLAN_STATUS_SECURITY_DISABLED; goto error; } goto skip_rsn; } if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) || kde.rsn_ie == NULL) { wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1"); status = WLAN_STATUS_INVALID_PARAMETERS; goto error; } if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) { wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in " "TPK M1"); status = WLAN_STATUS_INVALID_RSNIE; goto error; } if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1"); status = WLAN_STATUS_INVALID_RSNIE; goto error; } cipher = ie.pairwise_cipher; if (cipher & WPA_CIPHER_CCMP) { wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link"); cipher = WPA_CIPHER_CCMP; } else { wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1"); status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; goto error; } if ((ie.capabilities & (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) != WPA_CAPABILITY_PEERKEY_ENABLED) { wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in " "TPK M1"); status = WLAN_STATUS_INVALID_RSN_IE_CAPAB; goto error; } /* Lifetime */ if (kde.key_lifetime == NULL) { wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1"); status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; goto error; } timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; lifetime = WPA_GET_LE32(timeoutie->value); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime); if (lifetime < 300) { wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime"); status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; goto error; } skip_rsn: #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) { /* * The request frame from us is going to win, so do not * replace information based on this request frame from * the peer. */ goto skip_rsn_check; } } #endif /* CONFIG_TDLS_TESTING */ peer->initiator = 0; /* Need to check */ peer->dtoken = dtoken; if (!wpa_tdls_get_privacy(sm)) { peer->rsnie_i_len = 0; peer->rsnie_p_len = 0; peer->cipher = WPA_CIPHER_NONE; goto skip_rsn_check; } ftie = (struct wpa_tdls_ftie *) kde.ftie; os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); peer->rsnie_i_len = kde.rsn_ie_len; peer->cipher = cipher; - if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0 || + !tdls_nonce_set(peer->inonce)) { /* * There is no point in updating the RNonce for every obtained * TPK M1 frame (e.g., retransmission due to timeout) with the * same INonce (SNonce in FTIE). However, if the TPK M1 is * retransmitted with a different INonce, update the RNonce * since this is for a new TDLS session. */ wpa_printf(MSG_DEBUG, "TDLS: New TPK M1 INonce - generate new RNonce"); os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->ctx, MSG_WARNING, "TDLS: Failed to get random data for responder nonce"); goto error; } + peer->tk_set = 0; /* A new nonce results in a new TK */ } #if 0 /* get version info from RSNIE received from Peer */ hdr = (struct rsn_ie_hdr *) kde.rsn_ie; rsn_ver = WPA_GET_LE16(hdr->version); /* use min(peer's version, out version) */ if (rsn_ver > RSN_VERSION) rsn_ver = RSN_VERSION; hdr = (struct rsn_ie_hdr *) peer->rsnie_p; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, rsn_ver); pos = (u8 *) (hdr + 1); RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); pos += RSN_SELECTOR_LEN; /* Include only the selected cipher in pairwise cipher suite */ WPA_PUT_LE16(pos, 1); pos += 2; if (cipher == WPA_CIPHER_CCMP) RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; WPA_PUT_LE16(pos, 1); pos += 2; RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); pos += RSN_SELECTOR_LEN; rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; WPA_PUT_LE16(pos, rsn_capab); pos += 2; hdr->len = (pos - peer->rsnie_p) - 2; peer->rsnie_p_len = pos - peer->rsnie_p; #endif /* temp fix: validation of RSNIE later */ os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len); peer->rsnie_p_len = peer->rsnie_i_len; wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake", peer->rsnie_p, peer->rsnie_p_len); peer->lifetime = lifetime; wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); skip_rsn_check: #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) goto skip_add_peer; #endif /* CONFIG_TDLS_TESTING */ /* add supported rates, capabilities, and qos_info to the TDLS peer */ if (wpa_tdls_addset_peer(sm, peer, 1) < 0) goto error; #ifdef CONFIG_TDLS_TESTING skip_add_peer: #endif /* CONFIG_TDLS_TESTING */ peer->tpk_in_progress = 1; wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2"); if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) { wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); goto error; } return 0; error: wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, status); if (peer) wpa_tdls_peer_free(sm, peer); return -1; } static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { peer->tpk_success = 1; peer->tpk_in_progress = 0; eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); if (wpa_tdls_get_privacy(sm)) { u32 lifetime = peer->lifetime; /* * Start the initiator process a bit earlier to avoid race * condition with the responder sending teardown request. */ if (lifetime > 3 && peer->initiator) lifetime -= 3; eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout, sm, peer); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) { wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK " "expiration"); eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); } #endif /* CONFIG_TDLS_TESTING */ } if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) { wpa_printf(MSG_INFO, "TDLS: Could not configure key to the " "driver"); return -1; } peer->reconfig_key = 0; return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); } static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_tdls_peer *peer; struct wpa_eapol_ie_parse kde; struct wpa_ie_data ie; int cipher; struct wpa_tdls_ftie *ftie; struct wpa_tdls_timeoutie *timeoutie; struct wpa_tdls_lnkid *lnkid; u32 lifetime; u8 dtoken; int ielen; u16 status; const u8 *pos; int ret = 0; wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 " "(Peer " MACSTR ")", MAC2STR(src_addr)); for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_INFO, "TDLS: No matching peer found for " "TPK M2: " MACSTR, MAC2STR(src_addr)); return -1; } if (!peer->initiator) { /* * This may happen if both devices try to initiate TDLS at the * same time and we accept the TPK M1 from the peer in * wpa_tdls_process_tpk_m1() and clear our previous state. */ wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so " "ignore TPK M2 from " MACSTR, MAC2STR(src_addr)); return -1; } + + if (peer->tpk_success) { + wpa_printf(MSG_INFO, "TDLS: Ignore incoming TPK M2 retry, from " + MACSTR " as TPK M3 was already sent", + MAC2STR(src_addr)); + return 0; + } + wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST); if (len < 3 + 2 + 1) { wpa_tdls_disable_peer_link(sm, peer); return -1; } pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; status = WPA_GET_LE16(pos); pos += 2 /* status code */; if (status != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u", status); wpa_tdls_disable_peer_link(sm, peer); return -1; } status = WLAN_STATUS_UNSPECIFIED_FAILURE; /* TODO: need to verify dialog token matches here or in kernel */ dtoken = *pos++; /* dialog token */ wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken); if (len < 3 + 2 + 1 + 2) { wpa_tdls_disable_peer_link(sm, peer); return -1; } /* capability information */ peer->capability = WPA_GET_LE16(pos); pos += 2; ielen = len - (pos - buf); /* start of IE in buf */ /* * Don't reject the message if failing to parse IEs. The IEs we need are * explicitly checked below. Some APs may add arbitrary padding to the * end of short TDLS frames and that would look like invalid IEs. */ if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) wpa_printf(MSG_DEBUG, "TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround"); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_DECLINE_RESP) { wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response"); status = WLAN_STATUS_REQUEST_DECLINED; goto error; } #endif /* CONFIG_TDLS_TESTING */ if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " "TPK M2"); goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2", kde.lnkid, kde.lnkid_len); lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS"); status = WLAN_STATUS_NOT_IN_SAME_BSS; goto error; } if (copy_supp_rates(&kde, peer) < 0) goto error; if (copy_peer_ht_capab(&kde, peer) < 0) goto error; if (copy_peer_vht_capab(&kde, peer) < 0) goto error; if (copy_peer_ext_capab(&kde, peer) < 0) goto error; if (copy_peer_supp_channels(&kde, peer) < 0) goto error; if (copy_peer_supp_oper_classes(&kde, peer) < 0) goto error; peer->qos_info = kde.qosinfo; /* Overwrite with the qos_info obtained in WMM IE */ if (copy_peer_wmm_capab(&kde, peer) < 0) goto error; peer->aid = kde.aid; if (!wpa_tdls_get_privacy(sm)) { peer->rsnie_p_len = 0; peer->cipher = WPA_CIPHER_NONE; goto skip_rsn; } if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) || kde.rsn_ie == NULL) { wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2"); status = WLAN_STATUS_INVALID_PARAMETERS; goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", kde.rsn_ie, kde.rsn_ie_len); if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) { wpa_printf(MSG_INFO, "TDLS: Too long Responder RSN IE in TPK M2"); status = WLAN_STATUS_INVALID_RSNIE; goto error; } /* * FIX: bitwise comparison of RSN IE is not the correct way of * validation this. It can be different, but certain fields must * match. Since we list only a single pairwise cipher in TPK M1, the * memcmp is likely to work in most cases, though. */ if (kde.rsn_ie_len != peer->rsnie_i_len || os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) { wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does " "not match with RSN IE used in TPK M1"); wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1", peer->rsnie_i, peer->rsnie_i_len); wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", kde.rsn_ie, kde.rsn_ie_len); status = WLAN_STATUS_INVALID_RSNIE; goto error; } if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2"); status = WLAN_STATUS_INVALID_RSNIE; goto error; } cipher = ie.pairwise_cipher; if (cipher == WPA_CIPHER_CCMP) { wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link"); cipher = WPA_CIPHER_CCMP; } else { wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2"); status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2", kde.ftie, sizeof(*ftie)); ftie = (struct wpa_tdls_ftie *) kde.ftie; if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does " "not match with FTIE SNonce used in TPK M1"); /* Silently discard the frame */ return -1; } /* Responder Nonce and RSN IE */ os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN); os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len); peer->rsnie_p_len = kde.rsn_ie_len; peer->cipher = cipher; /* Lifetime */ if (kde.key_lifetime == NULL) { wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2"); status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; goto error; } timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; lifetime = WPA_GET_LE32(timeoutie->value); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2", lifetime); if (lifetime != peer->lifetime) { wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " "TPK M2 (expected %u)", lifetime, peer->lifetime); status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; goto error; } wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); /* Process MIC check to see if TPK M2 is right */ if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid, (u8 *) timeoutie, ftie) < 0) { /* Discard the frame */ wpa_tdls_del_key(sm, peer); wpa_tdls_disable_peer_link(sm, peer); return -1; } if (wpa_tdls_set_key(sm, peer) < 0) { /* * Some drivers may not be able to config the key prior to full * STA entry having been configured. */ wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " "STA entry is complete"); peer->reconfig_key = 1; } skip_rsn: peer->dtoken = dtoken; /* add supported rates, capabilities, and qos_info to the TDLS peer */ if (wpa_tdls_addset_peer(sm, peer, 0) < 0) goto error; wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " "TPK Handshake Message 3"); if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) goto error; if (!peer->tpk_success) { /* * Enable Link only when tpk_success is 0, signifying that this * processing of TPK M2 frame is not because of a retransmission * during TDLS setup handshake. */ ret = wpa_tdls_enable_link(sm, peer); if (ret < 0) { wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); wpa_tdls_do_teardown( sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } return ret; error: wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1, status); wpa_tdls_disable_peer_link(sm, peer); return -1; } static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_tdls_peer *peer; struct wpa_eapol_ie_parse kde; struct wpa_tdls_ftie *ftie; struct wpa_tdls_timeoutie *timeoutie; struct wpa_tdls_lnkid *lnkid; int ielen; u16 status; const u8 *pos; u32 lifetime; int ret = 0; wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 " "(Peer " MACSTR ")", MAC2STR(src_addr)); for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) break; } if (peer == NULL) { wpa_printf(MSG_INFO, "TDLS: No matching peer found for " "TPK M3: " MACSTR, MAC2STR(src_addr)); return -1; } wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE); if (len < 3 + 3) goto error; pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; status = WPA_GET_LE16(pos); if (status != 0) { wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u", status); goto error; } pos += 2 /* status code */ + 1 /* dialog token */; ielen = len - (pos - buf); /* start of IE in buf */ /* * Don't reject the message if failing to parse IEs. The IEs we need are * explicitly checked below. Some APs piggy-back broken IEs to the end * of a TDLS Confirm packet, which will fail the link if we don't ignore * this error. */ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { wpa_printf(MSG_DEBUG, "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround"); } if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3"); goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3", (u8 *) kde.lnkid, kde.lnkid_len); lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS"); goto error; } if (!wpa_tdls_get_privacy(sm)) goto skip_rsn; if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3"); goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3", kde.ftie, sizeof(*ftie)); ftie = (struct wpa_tdls_ftie *) kde.ftie; if (kde.rsn_ie == NULL) { wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3"); goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3", kde.rsn_ie, kde.rsn_ie_len); if (kde.rsn_ie_len != peer->rsnie_p_len || os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) { wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match " "with the one sent in TPK M2"); goto error; } if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does " "not match with FTIE ANonce used in TPK M2"); goto error; } if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not " "match with FTIE SNonce used in TPK M1"); goto error; } if (kde.key_lifetime == NULL) { wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3"); goto error; } timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3", (u8 *) timeoutie, sizeof(*timeoutie)); lifetime = WPA_GET_LE32(timeoutie->value); wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3", lifetime); if (lifetime != peer->lifetime) { wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " "TPK M3 (expected %u)", lifetime, peer->lifetime); goto error; } if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid, (u8 *) timeoutie, ftie) < 0) { wpa_tdls_del_key(sm, peer); goto error; } if (wpa_tdls_set_key(sm, peer) < 0) { /* * Some drivers may not be able to config the key prior to full * STA entry having been configured. */ wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " "STA entry is complete"); peer->reconfig_key = 1; } skip_rsn: /* add supported rates, capabilities, and qos_info to the TDLS peer */ if (wpa_tdls_addset_peer(sm, peer, 0) < 0) goto error; if (!peer->tpk_success) { /* * Enable Link only when tpk_success is 0, signifying that this * processing of TPK M3 frame is not because of a retransmission * during TDLS setup handshake. */ ret = wpa_tdls_enable_link(sm, peer); if (ret < 0) { wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); goto error; } } return ret; error: wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); return -1; } static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs) { struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie; os_memset(lifetime, 0, ie_len); lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL; lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2; lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME; WPA_PUT_LE32(lifetime->value, tsecs); os_memcpy(pos, ie, ie_len); return pos + ie_len; } /** * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1) * @sm: Pointer to WPA state machine data from wpa_sm_init() * @peer: MAC address of the peer STA * Returns: 0 on success, or -1 on failure * * Send TPK Handshake Message 1 info to driver to start TDLS * handshake with the peer. */ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; int tdls_prohibited = sm->tdls_prohibited; if (sm->tdls_disabled || !sm->tdls_supported) return -1; #ifdef CONFIG_TDLS_TESTING if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) && tdls_prohibited) { wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition " "on TDLS"); tdls_prohibited = 0; } #endif /* CONFIG_TDLS_TESTING */ if (tdls_prohibited) { wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - " "reject request to start setup"); return -1; } peer = wpa_tdls_add_peer(sm, addr, NULL); if (peer == NULL) return -1; if (peer->tpk_in_progress) { wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer"); return 0; } peer->initiator = 1; /* add the peer to the driver as a "setup in progress" peer */ if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0)) { wpa_tdls_disable_peer_link(sm, peer); return -1; } peer->tpk_in_progress = 1; if (wpa_tdls_send_tpk_m1(sm, peer) < 0) { wpa_tdls_disable_peer_link(sm, peer); return -1; } return 0; } void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; if (sm->tdls_disabled || !sm->tdls_supported) return; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (peer == NULL || !peer->tpk_success) return; if (sm->tdls_external_setup) { /* * Disable previous link to allow renegotiation to be completed * on AP path. */ wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } /** * wpa_supplicant_rx_tdls - Receive TDLS data frame * * This function is called to receive TDLS (ethertype = 0x890d) data frames. */ static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_sm *sm = ctx; struct wpa_tdls_frame *tf; wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation", buf, len); if (sm->tdls_disabled || !sm->tdls_supported) { wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled " "or unsupported by driver"); return; } if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message"); return; } if (len < sizeof(*tf)) { wpa_printf(MSG_INFO, "TDLS: Drop too short frame"); return; } /* Check to make sure its a valid encapsulated TDLS frame */ tf = (struct wpa_tdls_frame *) buf; if (tf->payloadtype != 2 /* TDLS_RFTYPE */ || tf->category != WLAN_ACTION_TDLS) { wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u " "category=%u action=%u", tf->payloadtype, tf->category, tf->action); return; } switch (tf->action) { case WLAN_TDLS_SETUP_REQUEST: wpa_tdls_process_tpk_m1(sm, src_addr, buf, len); break; case WLAN_TDLS_SETUP_RESPONSE: wpa_tdls_process_tpk_m2(sm, src_addr, buf, len); break; case WLAN_TDLS_SETUP_CONFIRM: wpa_tdls_process_tpk_m3(sm, src_addr, buf, len); break; case WLAN_TDLS_TEARDOWN: wpa_tdls_recv_teardown(sm, src_addr, buf, len); break; case WLAN_TDLS_DISCOVERY_REQUEST: wpa_tdls_process_discovery_request(sm, src_addr, buf, len); break; default: /* Kernel code will process remaining frames */ wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u", tf->action); break; } } /** * wpa_tdls_init - Initialize driver interface parameters for TDLS * @wpa_s: Pointer to wpa_supplicant data * Returns: 0 on success, -1 on failure * * This function is called to initialize driver interface parameters for TDLS. * wpa_drv_init() must have been called before this function to initialize the * driver interface. */ int wpa_tdls_init(struct wpa_sm *sm) { if (sm == NULL) return -1; sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname : sm->ifname, sm->own_addr, ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls, sm, 0); if (sm->l2_tdls == NULL) { wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet " "connection"); return -1; } /* * Drivers that support TDLS but don't implement the get_capa callback * are assumed to perform everything internally */ if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported, &sm->tdls_external_setup, &sm->tdls_chan_switch) < 0) { sm->tdls_supported = 1; sm->tdls_external_setup = 0; } wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by " "driver", sm->tdls_supported ? "" : " not"); wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup", sm->tdls_external_setup ? "external" : "internal"); wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching", sm->tdls_chan_switch ? "supports" : "does not support"); return 0; } void wpa_tdls_teardown_peers(struct wpa_sm *sm) { struct wpa_tdls_peer *peer, *tmp; if (!sm) return; peer = sm->tdls; wpa_printf(MSG_DEBUG, "TDLS: Tear down peers"); while (peer) { tmp = peer->next; wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR, MAC2STR(peer->addr)); if (sm->tdls_external_setup) wpa_tdls_do_teardown(sm, peer, WLAN_REASON_DEAUTH_LEAVING); else wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); peer = tmp; } } static void wpa_tdls_remove_peers(struct wpa_sm *sm) { struct wpa_tdls_peer *peer, *tmp; peer = sm->tdls; while (peer) { int res; tmp = peer->next; res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)", MAC2STR(peer->addr), res); wpa_tdls_peer_free(sm, peer); peer = tmp; } } /** * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS * * This function is called to recover driver interface parameters for TDLS * and frees resources allocated for it. */ void wpa_tdls_deinit(struct wpa_sm *sm) { if (sm == NULL) return; if (sm->l2_tdls) l2_packet_deinit(sm->l2_tdls); sm->l2_tdls = NULL; wpa_tdls_remove_peers(sm); } void wpa_tdls_assoc(struct wpa_sm *sm) { wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association"); wpa_tdls_remove_peers(sm); } void wpa_tdls_disassoc(struct wpa_sm *sm) { wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation"); wpa_tdls_remove_peers(sm); } static int wpa_tdls_prohibited(struct ieee802_11_elems *elems) { /* bit 38 - TDLS Prohibited */ return !!(elems->ext_capab[2 + 4] & 0x40); } static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems) { /* bit 39 - TDLS Channel Switch Prohibited */ return !!(elems->ext_capab[2 + 4] & 0x80); } void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { struct ieee802_11_elems elems; sm->tdls_prohibited = 0; sm->tdls_chan_switch_prohibited = 0; if (ies == NULL || ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) return; sm->tdls_prohibited = wpa_tdls_prohibited(&elems); wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS", sm->tdls_prohibited ? "prohibited" : "allowed"); sm->tdls_chan_switch_prohibited = wpa_tdls_chan_switch_prohibited(&elems); wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS", sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed"); } void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { struct ieee802_11_elems elems; if (ies == NULL || ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) return; if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) { wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on " "(Re)Association Response IEs"); sm->tdls_prohibited = 1; } if (!sm->tdls_chan_switch_prohibited && wpa_tdls_chan_switch_prohibited(&elems)) { wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs"); sm->tdls_chan_switch_prohibited = 1; } } void wpa_tdls_enable(struct wpa_sm *sm, int enabled) { wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled"); sm->tdls_disabled = !enabled; } int wpa_tdls_is_external_setup(struct wpa_sm *sm) { return sm->tdls_external_setup; } int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr, u8 oper_class, struct hostapd_freq_params *freq_params) { struct wpa_tdls_peer *peer; int ret; if (sm->tdls_disabled || !sm->tdls_supported) return -1; if (!sm->tdls_chan_switch) { wpa_printf(MSG_DEBUG, "TDLS: Channel switching not supported by the driver"); return -1; } if (sm->tdls_chan_switch_prohibited) { wpa_printf(MSG_DEBUG, "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel"); return -1; } for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (peer == NULL || !peer->tpk_success) { wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR " not found for channel switching", MAC2STR(addr)); return -1; } if (peer->chan_switch_enabled) { wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR " already has channel switching enabled", MAC2STR(addr)); return 0; } ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr, oper_class, freq_params); if (!ret) peer->chan_switch_enabled = 1; return ret; } int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; if (sm->tdls_disabled || !sm->tdls_supported) return -1; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (!peer || !peer->chan_switch_enabled) { wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for " MACSTR, MAC2STR(addr)); return -1; } /* ignore the return value */ wpa_sm_tdls_disable_channel_switch(sm, peer->addr); peer->chan_switch_enabled = 0; return 0; } Index: releng/11.1/contrib/wpa/src/rsn_supp/wpa.c =================================================================== --- releng/11.1/contrib/wpa/src/rsn_supp/wpa.c (revision 324698) +++ releng/11.1/contrib/wpa/src/rsn_supp/wpa.c (revision 324699) @@ -1,2977 +1,3038 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing * Copyright (c) 2003-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" #include "eloop.h" #include "preauth.h" #include "pmksa_cache.h" #include "wpa_i.h" #include "wpa_ie.h" #include "peerkey.h" /** * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message * @sm: Pointer to WPA state machine data from wpa_sm_init() * @kck: Key Confirmation Key (KCK, part of PTK) * @kck_len: KCK length in octets * @ver: Version field from Key Info * @dest: Destination address for the frame * @proto: Ethertype (usually ETH_P_EAPOL) * @msg: EAPOL-Key message * @msg_len: Length of message * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written */ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic) { size_t mic_len = wpa_mic_len(sm->key_mgmt); if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { /* * Association event was not yet received; try to fetch * BSSID from the driver. */ if (wpa_sm_get_bssid(sm, sm->bssid) < 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Failed to read BSSID for " "EAPOL-Key destination address"); } else { dest = sm->bssid; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Use BSSID (" MACSTR ") as the destination for EAPOL-Key", MAC2STR(dest)); } } if (key_mic && wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len, key_mic)) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC", ver, sm->key_mgmt); goto out; } wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len); wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len); wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); wpa_sm_ether_send(sm, dest, proto, msg, msg_len); eapol_sm_notify_tx_eapol_key(sm->eapol); out: os_free(msg); } /** * wpa_sm_key_request - Send EAPOL-Key Request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @error: Indicate whether this is an Michael MIC error report * @pairwise: 1 = error report for pairwise packet, 0 = for group packet * * Send an EAPOL-Key Request to the current authenticator. This function is * used to request rekeying and it is usually called when a local Michael MIC * failure is detected. */ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; struct wpa_eapol_key_192 *reply192; int key_info, ver; u8 bssid[ETH_ALEN], *rbuf, *key_mic; if (sm->key_mgmt == WPA_KEY_MGMT_OSEN || wpa_key_mgmt_suite_b(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AKM_DEFINED; else if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise_cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; if (wpa_sm_get_bssid(sm, bssid) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "Failed to read BSSID for EAPOL-Key request"); return; } mic_len = wpa_mic_len(sm->key_mgmt); hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return; reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = WPA_KEY_INFO_REQUEST | ver; if (sm->ptk_set) key_info |= WPA_KEY_INFO_MIC; if (error) key_info |= WPA_KEY_INFO_ERROR; if (pairwise) key_info |= WPA_KEY_INFO_KEY_TYPE; WPA_PUT_BE16(reply->key_info, key_info); WPA_PUT_BE16(reply->key_length, 0); os_memcpy(reply->replay_counter, sm->request_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); if (mic_len == 24) WPA_PUT_BE16(reply192->key_data_length, 0); else WPA_PUT_BE16(reply->key_data_length, 0); if (!(key_info & WPA_KEY_INFO_MIC)) key_mic = NULL; else key_mic = reply192->key_mic; /* same offset in reply */ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d " "pairwise=%d ptk_set=%d len=%lu)", error, pairwise, sm->ptk_set, (unsigned long) rlen); wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid, ETH_P_EAPOL, rbuf, rlen, key_mic); } static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm) { #ifdef CONFIG_IEEE80211R if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len)) wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Cannot set low order 256 bits of MSK for key management offload"); } else { #endif /* CONFIG_IEEE80211R */ if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len)) wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Cannot set PMK for key management offload"); #ifdef CONFIG_IEEE80211R } #endif /* CONFIG_IEEE80211R */ } static int wpa_supplicant_get_pmk(struct wpa_sm *sm, const unsigned char *src_addr, const u8 *pmkid) { int abort_cached = 0; if (pmkid && !sm->cur_pmksa) { /* When using drivers that generate RSN IE, wpa_supplicant may * not have enough time to get the association information * event before receiving this 1/4 message, so try to find a * matching PMKSA cache entry here. */ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL); if (sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: found matching PMKID from PMKSA cache"); } else { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no matching PMKID found"); abort_cached = 1; } } if (pmkid && sm->cur_pmksa && os_memcmp_const(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) { wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN); wpa_sm_set_pmk_from_pmksa(sm); wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache", sm->pmk, sm->pmk_len); eapol_sm_notify_cached(sm->eapol); #ifdef CONFIG_IEEE80211R sm->xxkey_len = 0; #endif /* CONFIG_IEEE80211R */ } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { int res, pmk_len; pmk_len = PMK_LEN; res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); if (res) { /* * EAP-LEAP is an exception from other EAP methods: it * uses only 16-byte PMK. */ res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); pmk_len = 16; } else { #ifdef CONFIG_IEEE80211R u8 buf[2 * PMK_LEN]; if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) { os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN); sm->xxkey_len = PMK_LEN; os_memset(buf, 0, sizeof(buf)); } #endif /* CONFIG_IEEE80211R */ } if (res == 0) { struct rsn_pmksa_cache_entry *sa = NULL; wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; wpa_supplicant_key_mgmt_set_pmk(sm); if (sm->proto == WPA_PROTO_RSN && !wpa_key_mgmt_suite_b(sm->key_mgmt) && !wpa_key_mgmt_ft(sm->key_mgmt)) { sa = pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, NULL, 0, src_addr, sm->own_addr, sm->network_ctx, sm->key_mgmt); } if (!sm->cur_pmksa && pmkid && pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: the new PMK matches with the " "PMKID"); abort_cached = 0; } else if (sa && !sm->cur_pmksa && pmkid) { /* * It looks like the authentication server * derived mismatching MSK. This should not * really happen, but bugs happen.. There is not * much we can do here without knowing what * exactly caused the server to misbehave. */ wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); return -1; } if (!sm->cur_pmksa) sm->cur_pmksa = sa; } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " "EAPOL state machines - key handshake " "aborted"); if (sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Cancelled PMKSA caching " "attempt"); sm->cur_pmksa = NULL; abort_cached = 1; } else if (!abort_cached) { return -1; } } } if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && !wpa_key_mgmt_suite_b(sm->key_mgmt) && !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN) { /* Send EAPOL-Start to trigger full EAP authentication. */ u8 *buf; size_t buflen; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no PMKSA entry found - trigger " "full EAP authentication"); buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START, NULL, 0, &buflen, NULL); if (buf) { wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL, buf, buflen); os_free(buf); return -2; } return -1; } return 0; } /** * wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake * @sm: Pointer to WPA state machine data from wpa_sm_init() * @dst: Destination address for the frame * @key: Pointer to the EAPOL-Key frame header * @ver: Version bits from EAPOL-Key Key Info * @nonce: Nonce value for the EAPOL-Key frame * @wpa_ie: WPA/RSN IE * @wpa_ie_len: Length of the WPA/RSN IE * @ptk: PTK to use for keyed hash and encryption * Returns: 0 on success, -1 on failure */ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, int ver, const u8 *nonce, const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ptk *ptk) { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; struct wpa_eapol_key_192 *reply192; u8 *rbuf, *key_mic; u8 *rsn_ie_buf = NULL; if (wpa_ie == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - " "cannot generate msg 2/4"); return -1; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { int res; /* * Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and * FTIE from (Re)Association Response. */ rsn_ie_buf = os_malloc(wpa_ie_len + 2 + 2 + PMKID_LEN + sm->assoc_resp_ies_len); if (rsn_ie_buf == NULL) return -1; os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len); res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len, sm->pmk_r1_name); if (res < 0) { os_free(rsn_ie_buf); return -1; } wpa_ie_len += res; if (sm->assoc_resp_ies) { os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies, sm->assoc_resp_ies_len); wpa_ie_len += sm->assoc_resp_ies_len; } wpa_ie = rsn_ie_buf; } #endif /* CONFIG_IEEE80211R */ wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len); mic_len = wpa_mic_len(sm->key_mgmt); hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen + wpa_ie_len, &rlen, (void *) &reply); if (rbuf == NULL) { os_free(rsn_ie_buf); return -1; } reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; WPA_PUT_BE16(reply->key_info, ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter, WPA_REPLAY_COUNTER_LEN); key_mic = reply192->key_mic; /* same offset for reply and reply192 */ if (mic_len == 24) { WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len); os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len); } else { WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); os_memcpy(reply + 1, wpa_ie, wpa_ie_len); } os_free(rsn_ie_buf); os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, rbuf, rlen, key_mic); return 0; } static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) return wpa_derive_ptk_ft(sm, src_addr, key, ptk); #endif /* CONFIG_IEEE80211R */ return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, ptk, sm->key_mgmt, sm->pairwise_cipher); } static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, u16 ver, const u8 *key_data, size_t key_data_len) { struct wpa_eapol_ie_parse ie; struct wpa_ptk *ptk; int res; u8 *kde, *kde_buf = NULL; size_t kde_len; if (wpa_sm_get_network_ctx(sm) == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info " "found (msg 1 of 4)"); return; } wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way " "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); os_memset(&ie, 0, sizeof(ie)); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { /* RSN: msg 1/4 should contain PMKID for the selected PMK */ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len); if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) goto failed; if (ie.pmkid) { wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " "Authenticator", ie.pmkid, PMKID_LEN); } } res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); if (res == -2) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to " "msg 1/4 - requesting full EAP authentication"); return; } if (res) goto failed; if (sm->renew_snonce) { if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for SNonce"); goto failed; } sm->renew_snonce = 0; wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", sm->snonce, WPA_NONCE_LEN); } /* Calculate PTK which will be stored as a temporary PTK until it has * been verified when processing message 3/4. */ ptk = &sm->tptk; wpa_derive_ptk(sm, src_addr, key, ptk); if (sm->pairwise_cipher == WPA_CIPHER_TKIP) { u8 buf[8]; /* Supplicant: swap tx/rx Mic keys */ os_memcpy(buf, &ptk->tk[16], 8); os_memcpy(&ptk->tk[16], &ptk->tk[24], 8); os_memcpy(&ptk->tk[24], buf, 8); os_memset(buf, 0, sizeof(buf)); } sm->tptk_set = 1; kde = sm->assoc_wpa_ie; kde_len = sm->assoc_wpa_ie_len; #ifdef CONFIG_P2P if (sm->p2p) { kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1); if (kde_buf) { u8 *pos; wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE " "into EAPOL-Key 2/4"); os_memcpy(kde_buf, kde, kde_len); kde = kde_buf; pos = kde + kde_len; *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = RSN_SELECTOR_LEN + 1; RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ); pos += RSN_SELECTOR_LEN; *pos++ = 0x01; kde_len = pos - kde; } } #endif /* CONFIG_P2P */ if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, kde, kde_len, ptk)) goto failed; os_free(kde_buf); os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); return; failed: os_free(kde_buf); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; rsn_preauth_candidate_process(sm); } static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, const u8 *addr, int secure) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Key negotiation completed with " MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr), wpa_cipher_txt(sm->pairwise_cipher), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); if (secure) { wpa_sm_mlme_setprotection( sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); eapol_sm_notify_portValid(sm->eapol, TRUE); if (wpa_key_mgmt_wpa_psk(sm->key_mgmt)) eapol_sm_notify_eap_success(sm->eapol, TRUE); /* * Start preauthentication after a short wait to avoid a * possible race condition between the data receive and key * configuration after the 4-Way Handshake. This increases the * likelihood of the first preauth EAPOL-Start frame getting to * the target AP. */ eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL); } if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Authenticator accepted " "opportunistic PMKSA entry - marking it valid"); sm->cur_pmksa->opportunistic = 0; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* Prepare for the next transition */ wpa_ft_prepare_auth_request(sm, NULL); } #endif /* CONFIG_IEEE80211R */ } static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying"); wpa_sm_key_request(sm, 0, 1); } static int wpa_supplicant_install_ptk(struct wpa_sm *sm, const struct wpa_eapol_key *key) { int keylen, rsclen; enum wpa_alg alg; const u8 *key_rsc; u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + if (sm->ptk.installed) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Do not re-install same PTK to the driver"); + return 0; + } + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Installing PTK to the driver"); if (sm->pairwise_cipher == WPA_CIPHER_NONE) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher " "Suite: NONE - do not use pairwise keys"); return 0; } if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported pairwise cipher %d", sm->pairwise_cipher); return -1; } alg = wpa_cipher_to_alg(sm->pairwise_cipher); keylen = wpa_cipher_key_len(sm->pairwise_cipher); rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { key_rsc = null_rsc; } else { key_rsc = key->key_rsc; wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen); } if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen, sm->ptk.tk, keylen) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to set PTK to the " "driver (alg=%d keylen=%d bssid=" MACSTR ")", alg, keylen, MAC2STR(sm->bssid)); return -1; } /* TK is not needed anymore in supplicant */ os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); + sm->ptk.installed = 1; if (sm->wpa_ptk_rekey) { eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk, sm, NULL); } return 0; } static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm, int group_cipher, int keylen, int maxkeylen, int *key_rsc_len, enum wpa_alg *alg) { int klen; *alg = wpa_cipher_to_alg(group_cipher); if (*alg == WPA_ALG_NONE) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported Group Cipher %d", group_cipher); return -1; } *key_rsc_len = wpa_cipher_rsc_len(group_cipher); klen = wpa_cipher_key_len(group_cipher); if (keylen != klen || maxkeylen < klen) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported %s Group Cipher key length %d (%d)", wpa_cipher_txt(group_cipher), keylen, maxkeylen); return -1; } return 0; } struct wpa_gtk_data { enum wpa_alg alg; int tx, key_rsc_len, keyidx; u8 gtk[32]; int gtk_len; }; static int wpa_supplicant_install_gtk(struct wpa_sm *sm, const struct wpa_gtk_data *gd, - const u8 *key_rsc) + const u8 *key_rsc, int wnm_sleep) { const u8 *_gtk = gd->gtk; u8 gtk_buf[32]; + /* Detect possible key reinstallation */ + if ((sm->gtk.gtk_len == (size_t) gd->gtk_len && + os_memcmp(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len) == 0) || + (sm->gtk_wnm_sleep.gtk_len == (size_t) gd->gtk_len && + os_memcmp(sm->gtk_wnm_sleep.gtk, gd->gtk, + sm->gtk_wnm_sleep.gtk_len) == 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Not reinstalling already in-use GTK to the driver (keyidx=%d tx=%d len=%d)", + gd->keyidx, gd->tx, gd->gtk_len); + return 0; + } + wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)", gd->keyidx, gd->tx, gd->gtk_len); wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len); if (sm->group_cipher == WPA_CIPHER_TKIP) { /* Swap Tx/Rx keys for Michael MIC */ os_memcpy(gtk_buf, gd->gtk, 16); os_memcpy(gtk_buf + 16, gd->gtk + 24, 8); os_memcpy(gtk_buf + 24, gd->gtk + 16, 8); _gtk = gtk_buf; } if (sm->pairwise_cipher == WPA_CIPHER_NONE) { if (wpa_sm_set_key(sm, gd->alg, NULL, gd->keyidx, 1, key_rsc, gd->key_rsc_len, _gtk, gd->gtk_len) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to set GTK to the driver " "(Group only)"); os_memset(gtk_buf, 0, sizeof(gtk_buf)); return -1; } } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr, gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, _gtk, gd->gtk_len) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to set GTK to " "the driver (alg=%d keylen=%d keyidx=%d)", gd->alg, gd->gtk_len, gd->keyidx); os_memset(gtk_buf, 0, sizeof(gtk_buf)); return -1; } os_memset(gtk_buf, 0, sizeof(gtk_buf)); + if (wnm_sleep) { + sm->gtk_wnm_sleep.gtk_len = gd->gtk_len; + os_memcpy(sm->gtk_wnm_sleep.gtk, gd->gtk, + sm->gtk_wnm_sleep.gtk_len); + } else { + sm->gtk.gtk_len = gd->gtk_len; + os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len); + } + return 0; } static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, int tx) { if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) { /* Ignore Tx bit for GTK if a pairwise key is used. One AP * seemed to set this bit (incorrectly, since Tx is only when * doing Group Key only APs) and without this workaround, the * data connection does not work because wpa_supplicant * configured non-zero keyidx to be used for unicast. */ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Tx bit set for GTK, but pairwise " "keys are used - ignore Tx bit"); return 0; } return tx; } static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const struct wpa_eapol_key *key, const u8 *gtk, size_t gtk_len, int key_info) { struct wpa_gtk_data gd; /* * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x * GTK KDE format: * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7] * Reserved [bits 0-7] * GTK */ os_memset(&gd, 0, sizeof(gd)); wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake", gtk, gtk_len); if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk)) return -1; gd.keyidx = gtk[0] & 0x3; gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(gtk[0] & BIT(2))); gtk += 2; gtk_len -= 2; os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, gtk_len, &gd.key_rsc_len, &gd.alg) || - wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) { + wpa_supplicant_install_gtk(sm, &gd, key->key_rsc, 0))) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Failed to install GTK"); os_memset(&gd, 0, sizeof(gd)); return -1; } os_memset(&gd, 0, sizeof(gd)); wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); return 0; } +#ifdef CONFIG_IEEE80211W +static int wpa_supplicant_install_igtk(struct wpa_sm *sm, + const struct wpa_igtk_kde *igtk, + int wnm_sleep) +{ + size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher); + u16 keyidx = WPA_GET_LE16(igtk->keyid); + + /* Detect possible key reinstallation */ + if ((sm->igtk.igtk_len == len && + os_memcmp(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len) == 0) || + (sm->igtk_wnm_sleep.igtk_len == len && + os_memcmp(sm->igtk_wnm_sleep.igtk, igtk->igtk, + sm->igtk_wnm_sleep.igtk_len) == 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Not reinstalling already in-use IGTK to the driver (keyidx=%d)", + keyidx); + return 0; + } + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: IGTK keyid %d pn %02x%02x%02x%02x%02x%02x", + keyidx, MAC2STR(igtk->pn)); + wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len); + if (keyidx > 4095) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KeyID %d", keyidx); + return -1; + } + if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, + keyidx, 0, igtk->pn, sizeof(igtk->pn), + igtk->igtk, len) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to configure IGTK to the driver"); + return -1; + } + + if (wnm_sleep) { + sm->igtk_wnm_sleep.igtk_len = len; + os_memcpy(sm->igtk_wnm_sleep.igtk, igtk->igtk, + sm->igtk_wnm_sleep.igtk_len); + } else { + sm->igtk.igtk_len = len; + os_memcpy(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len); + } + + return 0; +} +#endif /* CONFIG_IEEE80211W */ + + static int ieee80211w_set_keys(struct wpa_sm *sm, struct wpa_eapol_ie_parse *ie) { #ifdef CONFIG_IEEE80211W if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher)) return 0; if (ie->igtk) { size_t len; const struct wpa_igtk_kde *igtk; - u16 keyidx; + len = wpa_cipher_key_len(sm->mgmt_group_cipher); if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len) return -1; + igtk = (const struct wpa_igtk_kde *) ie->igtk; - keyidx = WPA_GET_LE16(igtk->keyid); - wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d " - "pn %02x%02x%02x%02x%02x%02x", - keyidx, MAC2STR(igtk->pn)); - wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", - igtk->igtk, len); - if (keyidx > 4095) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Invalid IGTK KeyID %d", keyidx); + if (wpa_supplicant_install_igtk(sm, igtk, 0) < 0) return -1; - } - if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), - broadcast_ether_addr, - keyidx, 0, igtk->pn, sizeof(igtk->pn), - igtk->igtk, len) < 0) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Failed to configure IGTK to the driver"); - return -1; - } } return 0; #else /* CONFIG_IEEE80211W */ return 0; #endif /* CONFIG_IEEE80211W */ } static void wpa_report_ie_mismatch(struct wpa_sm *sm, const char *reason, const u8 *src_addr, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *rsn_ie, size_t rsn_ie_len) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")", reason, MAC2STR(src_addr)); if (sm->ap_wpa_ie) { wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp", sm->ap_wpa_ie, sm->ap_wpa_ie_len); } if (wpa_ie) { if (!sm->ap_wpa_ie) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No WPA IE in Beacon/ProbeResp"); } wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg", wpa_ie, wpa_ie_len); } if (sm->ap_rsn_ie) { wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp", sm->ap_rsn_ie, sm->ap_rsn_ie_len); } if (rsn_ie) { if (!sm->ap_rsn_ie) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No RSN IE in Beacon/ProbeResp"); } wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg", rsn_ie, rsn_ie_len); } wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); } #ifdef CONFIG_IEEE80211R static int ft_validate_mdie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie, const u8 *assoc_resp_mdie) { struct rsn_mdie *mdie; mdie = (struct rsn_mdie *) (ie->mdie + 2); if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did " "not match with the current mobility domain"); return -1; } if (assoc_resp_mdie && (assoc_resp_mdie[1] != ie->mdie[1] || os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4", ie->mdie, 2 + ie->mdie[1]); wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response", assoc_resp_mdie, 2 + assoc_resp_mdie[1]); return -1; } return 0; } static int ft_validate_ftie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie, const u8 *assoc_resp_ftie) { if (ie->ftie == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No FTIE in EAPOL-Key msg 3/4"); return -1; } if (assoc_resp_ftie == NULL) return 0; if (assoc_resp_ftie[1] != ie->ftie[1] || os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4", ie->ftie, 2 + ie->ftie[1]); wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response", assoc_resp_ftie, 2 + assoc_resp_ftie[1]); return -1; } return 0; } static int ft_validate_rsnie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie) { struct wpa_ie_data rsn; if (!ie->rsn_ie) return 0; /* * Verify that PMKR1Name from EAPOL-Key message 3/4 * matches with the value we derived. */ if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 || rsn.num_pmkid != 1 || rsn.pmkid == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in " "FT 4-way handshake message 3/4"); return -1; } if (os_memcmp_const(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: PMKR1Name mismatch in " "FT 4-way handshake message 3/4"); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator", rsn.pmkid, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); return -1; } return 0; } static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie) { const u8 *pos, *end, *mdie = NULL, *ftie = NULL; if (sm->assoc_resp_ies) { pos = sm->assoc_resp_ies; end = pos + sm->assoc_resp_ies_len; while (pos + 2 < end) { if (pos + 2 + pos[1] > end) break; switch (*pos) { case WLAN_EID_MOBILITY_DOMAIN: mdie = pos; break; case WLAN_EID_FAST_BSS_TRANSITION: ftie = pos; break; } pos += 2 + pos[1]; } } if (ft_validate_mdie(sm, src_addr, ie, mdie) < 0 || ft_validate_ftie(sm, src_addr, ie, ftie) < 0 || ft_validate_rsnie(sm, src_addr, ie) < 0) return -1; return 0; } #endif /* CONFIG_IEEE80211R */ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie) { if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. " "Trying to get from scan results"); if (wpa_sm_get_beacon_ie(sm) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Could not find AP from " "the scan results"); } else { wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Found the current AP from " "updated scan results"); } } if (ie->wpa_ie == NULL && ie->rsn_ie == NULL && (sm->ap_wpa_ie || sm->ap_rsn_ie)) { wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " "with IE in Beacon/ProbeResp (no IE?)", src_addr, ie->wpa_ie, ie->wpa_ie_len, ie->rsn_ie, ie->rsn_ie_len); return -1; } if ((ie->wpa_ie && sm->ap_wpa_ie && (ie->wpa_ie_len != sm->ap_wpa_ie_len || os_memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) || (ie->rsn_ie && sm->ap_rsn_ie && wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), sm->ap_rsn_ie, sm->ap_rsn_ie_len, ie->rsn_ie, ie->rsn_ie_len))) { wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " "with IE in Beacon/ProbeResp", src_addr, ie->wpa_ie, ie->wpa_ie_len, ie->rsn_ie, ie->rsn_ie_len); return -1; } if (sm->proto == WPA_PROTO_WPA && ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) { wpa_report_ie_mismatch(sm, "Possible downgrade attack " "detected - RSN was enabled and RSN IE " "was in msg 3/4, but not in " "Beacon/ProbeResp", src_addr, ie->wpa_ie, ie->wpa_ie_len, ie->rsn_ie, ie->rsn_ie_len); return -1; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt) && wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0) return -1; #endif /* CONFIG_IEEE80211R */ return 0; } /** * wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake * @sm: Pointer to WPA state machine data from wpa_sm_init() * @dst: Destination address for the frame * @key: Pointer to the EAPOL-Key frame header * @ver: Version bits from EAPOL-Key Key Info * @key_info: Key Info * @ptk: PTK to use for keyed hash and encryption * Returns: 0 on success, -1 on failure */ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, u16 ver, u16 key_info, struct wpa_ptk *ptk) { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; struct wpa_eapol_key_192 *reply192; u8 *rbuf, *key_mic; mic_len = wpa_mic_len(sm->key_mgmt); hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return -1; reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_SECURE; key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC; WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); key_mic = reply192->key_mic; /* same offset for reply and reply192 */ if (mic_len == 24) WPA_PUT_BE16(reply192->key_data_length, 0); else WPA_PUT_BE16(reply->key_data_length, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, rbuf, rlen, key_mic); return 0; } static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_eapol_key *key, u16 ver, const u8 *key_data, size_t key_data_len) { u16 key_info, keylen; struct wpa_eapol_ie_parse ie; wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way " "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver); key_info = WPA_GET_BE16(key->key_info); wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len); if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) goto failed; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: GTK IE in unencrypted key data"); goto failed; } #ifdef CONFIG_IEEE80211W if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: IGTK KDE in unencrypted key data"); goto failed; } if (ie.igtk && wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) && ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN + (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid IGTK KDE length %lu", (unsigned long) ie.igtk_len); goto failed; } #endif /* CONFIG_IEEE80211W */ if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) goto failed; if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: ANonce from message 1 of 4-Way Handshake " "differs from 3 of 4-Way Handshake - drop packet (src=" MACSTR ")", MAC2STR(sm->bssid)); goto failed; } keylen = WPA_GET_BE16(key->key_length); if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid %s key length %d (src=" MACSTR ")", wpa_cipher_txt(sm->pairwise_cipher), keylen, MAC2STR(sm->bssid)); goto failed; } #ifdef CONFIG_P2P if (ie.ip_addr_alloc) { os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4); wpa_hexdump(MSG_DEBUG, "P2P: IP address info", sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr)); } #endif /* CONFIG_P2P */ if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, &sm->ptk)) { goto failed; } /* SNonce was successfully used in msg 3/4, so mark it to be renewed * for the next 4-Way Handshake. If msg 3 is received again, the old * SNonce will still be used to avoid changing PTK. */ sm->renew_snonce = 1; if (key_info & WPA_KEY_INFO_INSTALL) { if (wpa_supplicant_install_ptk(sm, key)) goto failed; } if (key_info & WPA_KEY_INFO_SECURE) { wpa_sm_mlme_setprotection( sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); eapol_sm_notify_portValid(sm->eapol, TRUE); } wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) { wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); } else if (ie.gtk && wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Failed to configure GTK"); goto failed; } if (ieee80211w_set_keys(sm, &ie) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Failed to configure IGTK"); goto failed; } if (ie.gtk) wpa_sm_set_rekey_offload(sm); if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) { struct rsn_pmksa_cache_entry *sa; sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, sm->ptk.kck, sm->ptk.kck_len, sm->bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt); if (!sm->cur_pmksa) sm->cur_pmksa = sa; } sm->msg_3_of_4_ok = 1; return; failed: wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, const u8 *keydata, size_t keydatalen, u16 key_info, struct wpa_gtk_data *gd) { int maxkeylen; struct wpa_eapol_ie_parse ie; wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0) return -1; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: GTK IE in unencrypted key data"); return -1; } if (ie.gtk == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2"); return -1; } maxkeylen = gd->gtk_len = ie.gtk_len - 2; if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake", ie.gtk, ie.gtk_len); gd->keyidx = ie.gtk[0] & 0x3; gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(ie.gtk[0] & BIT(2))); if (ie.gtk_len - 2 > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Too long GTK in GTK IE (len=%lu)", (unsigned long) ie.gtk_len - 2); return -1; } os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2); if (ieee80211w_set_keys(sm, &ie) < 0) wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Failed to configure IGTK"); return 0; } static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, const struct wpa_eapol_key *key, const u8 *key_data, size_t key_data_len, u16 key_info, u16 ver, struct wpa_gtk_data *gd) { size_t maxkeylen; u16 gtk_len; gtk_len = WPA_GET_BE16(key->key_length); maxkeylen = key_data_len; if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen < 8) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Too short maxkeylen (%lu)", (unsigned long) maxkeylen); return -1; } maxkeylen -= 8; } if (gtk_len > maxkeylen || wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; gd->gtk_len = gtk_len; gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> WPA_KEY_INFO_KEY_INDEX_SHIFT; if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { #ifdef CONFIG_NO_RC4 wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: RC4 not supported in the build"); return -1; #else /* CONFIG_NO_RC4 */ u8 ek[32]; if (key_data_len > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: RC4 key data too long (%lu)", (unsigned long) key_data_len); return -1; } os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); os_memcpy(gd->gtk, key_data, key_data_len); if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) { os_memset(ek, 0, sizeof(ek)); wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: RC4 failed"); return -1; } os_memset(ek, 0, sizeof(ek)); #endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %lu", (unsigned long) maxkeylen); return -1; } if (maxkeylen > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES-WRAP key data " "too long (keydatalen=%lu maxkeylen=%lu)", (unsigned long) key_data_len, (unsigned long) maxkeylen); return -1; } if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8, key_data, gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES unwrap failed - could not decrypt " "GTK"); return -1; } } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported key_info type %d", ver); return -1; } gd->tx = wpa_supplicant_gtk_tx_bit_workaround( sm, !!(key_info & WPA_KEY_INFO_TXRX)); return 0; } static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, const struct wpa_eapol_key *key, int ver, u16 key_info) { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; struct wpa_eapol_key_192 *reply192; u8 *rbuf, *key_mic; mic_len = wpa_mic_len(sm->key_mgmt); hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return -1; reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_KEY_INDEX_MASK; key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); key_mic = reply192->key_mic; /* same offset for reply and reply192 */ if (mic_len == 24) WPA_PUT_BE16(reply192->key_data_length, 0); else WPA_PUT_BE16(reply->key_data_length, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic); return 0; } static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, const u8 *key_data, size_t key_data_len, u16 ver) { u16 key_info; int rekey, ret; struct wpa_gtk_data gd; if (!sm->msg_3_of_4_ok) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group Key Handshake started prior to completion of 4-way handshake"); goto failed; } os_memset(&gd, 0, sizeof(gd)); rekey = wpa_sm_get_state(sm) == WPA_COMPLETED; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key " "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); key_info = WPA_GET_BE16(key->key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data, key_data_len, key_info, &gd); } else { ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data, key_data_len, key_info, ver, &gd); } wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (ret) goto failed; - if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || + if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc, 0) || wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) goto failed; os_memset(&gd, 0, sizeof(gd)); if (rekey) { wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Group rekeying " "completed with " MACSTR " [GTK=%s]", MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); } else { wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); } wpa_sm_set_rekey_offload(sm); return; failed: os_memset(&gd, 0, sizeof(gd)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_eapol_key_192 *key, u16 ver, const u8 *buf, size_t len) { u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; int ok = 0; size_t mic_len = wpa_mic_len(sm->key_mgmt); os_memcpy(mic, key->key_mic, mic_len); if (sm->tptk_set) { os_memset(key->key_mic, 0, mic_len); wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt, ver, buf, len, key->key_mic); if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " "when using TPTK - ignoring TPTK"); } else { ok = 1; sm->tptk_set = 0; sm->ptk_set = 1; os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); } } if (!ok && sm->ptk_set) { os_memset(key->key_mic, 0, mic_len); wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt, ver, buf, len, key->key_mic); if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC - " "dropping packet"); return -1; } ok = 1; } if (!ok) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC - " "dropping packet"); return -1; } os_memcpy(sm->rx_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); sm->rx_replay_counter_set = 1; return 0; } /* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, struct wpa_eapol_key *key, u16 ver, u8 *key_data, size_t *key_data_len) { wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", key_data, *key_data_len); if (!sm->ptk_set) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: PTK not available, cannot decrypt EAPOL-Key Key " "Data"); return -1; } /* Decrypt key data here so that this operation does not need * to be implemented separately for each message type. */ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { #ifdef CONFIG_NO_RC4 wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: RC4 not supported in the build"); return -1; #else /* CONFIG_NO_RC4 */ u8 ek[32]; os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) { os_memset(ek, 0, sizeof(ek)); wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: RC4 failed"); return -1; } os_memset(ek, 0, sizeof(ek)); #endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || sm->key_mgmt == WPA_KEY_MGMT_OSEN || wpa_key_mgmt_suite_b(sm->key_mgmt)) { u8 *buf; if (*key_data_len < 8 || *key_data_len % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %u", (unsigned int) *key_data_len); return -1; } *key_data_len -= 8; /* AES-WRAP adds 8 bytes */ buf = os_malloc(*key_data_len); if (buf == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No memory for AES-UNWRAP buffer"); return -1; } if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8, key_data, buf)) { os_free(buf); wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES unwrap failed - " "could not decrypt EAPOL-Key key data"); return -1; } os_memcpy(key_data, buf, *key_data_len); os_free(buf); WPA_PUT_BE16(key->key_data_length, *key_data_len); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported key_info type %d", ver); return -1; } wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data", key_data, *key_data_len); return 0; } /** * wpa_sm_aborted_cached - Notify WPA that PMKSA caching was aborted * @sm: Pointer to WPA state machine data from wpa_sm_init() */ void wpa_sm_aborted_cached(struct wpa_sm *sm) { if (sm && sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt"); sm->cur_pmksa = NULL; } } static void wpa_eapol_key_dump(struct wpa_sm *sm, const struct wpa_eapol_key *key, unsigned int key_data_len, const u8 *mic, unsigned int mic_len) { #ifndef CONFIG_NO_STDOUT_DEBUG u16 key_info = WPA_GET_BE16(key->key_info); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " EAPOL-Key type=%d", key->type); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s%s%s%s%s%s%s%s)", key_info, key_info & WPA_KEY_INFO_TYPE_MASK, (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> WPA_KEY_INFO_KEY_INDEX_SHIFT, (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13, key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group", key_info & WPA_KEY_INFO_INSTALL ? " Install" : "", key_info & WPA_KEY_INFO_ACK ? " Ack" : "", key_info & WPA_KEY_INFO_MIC ? " MIC" : "", key_info & WPA_KEY_INFO_SECURE ? " Secure" : "", key_info & WPA_KEY_INFO_ERROR ? " Error" : "", key_info & WPA_KEY_INFO_REQUEST ? " Request" : "", key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : ""); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " key_length=%u key_data_length=%u", WPA_GET_BE16(key->key_length), key_data_len); wpa_hexdump(MSG_DEBUG, " replay_counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); wpa_hexdump(MSG_DEBUG, " key_nonce", key->key_nonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, " key_iv", key->key_iv, 16); wpa_hexdump(MSG_DEBUG, " key_rsc", key->key_rsc, 8); wpa_hexdump(MSG_DEBUG, " key_id (reserved)", key->key_id, 8); wpa_hexdump(MSG_DEBUG, " key_mic", mic, mic_len); #endif /* CONFIG_NO_STDOUT_DEBUG */ } /** * wpa_sm_rx_eapol - Process received WPA EAPOL frames * @sm: Pointer to WPA state machine data from wpa_sm_init() * @src_addr: Source MAC address of the EAPOL packet * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) * @len: Length of the EAPOL frame * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure * * This function is called for each received EAPOL frame. Other than EAPOL-Key * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is * only processing WPA and WPA2 EAPOL-Key frames. * * The received EAPOL-Key packets are validated and valid packets are replied * to. In addition, key material (PTK, GTK) is configured at the end of a * successful key handshake. */ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { size_t plen, data_len, key_data_len; const struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; struct wpa_eapol_key_192 *key192; u16 key_info, ver; u8 *tmp = NULL; int ret = -1; struct wpa_peerkey *peerkey = NULL; u8 *key_data; size_t mic_len, keyhdrlen; #ifdef CONFIG_IEEE80211R sm->ft_completed = 0; #endif /* CONFIG_IEEE80211R */ mic_len = wpa_mic_len(sm->key_mgmt); keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); if (len < sizeof(*hdr) + keyhdrlen) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA " "EAPOL-Key (len %lu, expecting at least %lu)", (unsigned long) len, (unsigned long) sizeof(*hdr) + keyhdrlen); return 0; } hdr = (const struct ieee802_1x_hdr *) buf; plen = be_to_host16(hdr->length); data_len = plen + sizeof(*hdr); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu", hdr->version, hdr->type, (unsigned long) plen); if (hdr->version < EAPOL_VERSION) { /* TODO: backwards compatibility */ } if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, " "not a Key frame", hdr->type); ret = 0; goto out; } wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len); if (plen > len - sizeof(*hdr) || plen < keyhdrlen) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL frame payload size %lu " "invalid (frame size %lu)", (unsigned long) plen, (unsigned long) len); ret = 0; goto out; } if (data_len < len) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE 802.1X data", (unsigned long) len - data_len); } /* * Make a copy of the frame since we need to modify the buffer during * MAC validation and Key Data decryption. */ tmp = os_malloc(data_len); if (tmp == NULL) goto out; os_memcpy(tmp, buf, data_len); key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr)); key192 = (struct wpa_eapol_key_192 *) (tmp + sizeof(struct ieee802_1x_hdr)); if (mic_len == 24) key_data = (u8 *) (key192 + 1); else key_data = (u8 *) (key + 1); if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, discarded", key->type); ret = 0; goto out; } if (mic_len == 24) key_data_len = WPA_GET_BE16(key192->key_data_length); else key_data_len = WPA_GET_BE16(key->key_data_length); wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len); if (key_data_len > plen - keyhdrlen) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " "frame - key_data overflow (%u > %u)", (unsigned int) key_data_len, (unsigned int) (plen - keyhdrlen)); goto out; } eapol_sm_notify_lower_layer_success(sm->eapol, 0); key_info = WPA_GET_BE16(key->key_info); ver = key_info & WPA_KEY_INFO_TYPE_MASK; if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES && !wpa_key_mgmt_suite_b(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor version %d", ver); goto out; } if (sm->key_mgmt == WPA_KEY_MGMT_OSEN && ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "OSEN: Unsupported EAPOL-Key descriptor version %d", ver); goto out; } if (wpa_key_mgmt_suite_b(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)", ver); goto out; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "FT: AP did not use AES-128-CMAC"); goto out; } } else #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) { if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && sm->key_mgmt != WPA_KEY_MGMT_OSEN && !wpa_key_mgmt_suite_b(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: AP did not use the " "negotiated AES-128-CMAC"); goto out; } } else #endif /* CONFIG_IEEE80211W */ if (sm->pairwise_cipher == WPA_CIPHER_CCMP && !wpa_key_mgmt_suite_b(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " "descriptor version (%d) is not 2", ver); if (sm->group_cipher != WPA_CIPHER_CCMP && !(key_info & WPA_KEY_INFO_KEY_TYPE)) { /* Earlier versions of IEEE 802.11i did not explicitly * require version 2 descriptor for all EAPOL-Key * packets, so allow group keys to use version 1 if * CCMP is not used for them. */ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Backwards compatibility: allow invalid " "version for non-CCMP group keys"); } else if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used"); } else goto out; } else if (sm->pairwise_cipher == WPA_CIPHER_GCMP && !wpa_key_mgmt_suite_b(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: GCMP is used, but EAPOL-Key " "descriptor version (%d) is not 2", ver); goto out; } #ifdef CONFIG_PEERKEY for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) break; } if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) { if (!peerkey->initiator && peerkey->replay_counter_set && os_memcmp(key->replay_counter, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: EAPOL-Key Replay Counter did not " "increase (STK) - dropping packet"); goto out; } else if (peerkey->initiator) { u8 _tmp[WPA_REPLAY_COUNTER_LEN]; os_memcpy(_tmp, key->replay_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN); if (os_memcmp(_tmp, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN) != 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: EAPOL-Key Replay " "Counter did not match (STK) - " "dropping packet"); goto out; } } } if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Ack bit in key_info from STK peer"); goto out; } #endif /* CONFIG_PEERKEY */ if (!peerkey && sm->rx_replay_counter_set && os_memcmp(key->replay_counter, sm->rx_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not increase - " "dropping packet"); goto out; } if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE)) #ifdef CONFIG_PEERKEY && (peerkey == NULL || !peerkey->initiator) #endif /* CONFIG_PEERKEY */ ) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No Ack bit in key_info"); goto out; } if (key_info & WPA_KEY_INFO_REQUEST) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: EAPOL-Key with Request bit - dropped"); goto out; } if ((key_info & WPA_KEY_INFO_MIC) && !peerkey && wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len)) goto out; #ifdef CONFIG_PEERKEY if ((key_info & WPA_KEY_INFO_MIC) && peerkey && peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp, data_len)) goto out; #endif /* CONFIG_PEERKEY */ if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data, &key_data_len)) goto out; } if (key_info & WPA_KEY_INFO_KEY_TYPE) { if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Ignored EAPOL-Key (Pairwise) with " "non-zero key index"); goto out; } if (peerkey) { /* PeerKey 4-Way Handshake */ peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver, key_data, key_data_len); } else if (key_info & WPA_KEY_INFO_MIC) { /* 3/4 4-Way Handshake */ wpa_supplicant_process_3_of_4(sm, key, ver, key_data, key_data_len); } else { /* 1/4 4-Way Handshake */ wpa_supplicant_process_1_of_4(sm, src_addr, key, ver, key_data, key_data_len); } } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { /* PeerKey SMK Handshake */ peerkey_rx_eapol_smk(sm, src_addr, key, key_data_len, key_info, ver); } else { if (key_info & WPA_KEY_INFO_MIC) { /* 1/2 Group Key Handshake */ wpa_supplicant_process_1_of_2(sm, src_addr, key, key_data, key_data_len, ver); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: EAPOL-Key (Group) without Mic bit - " "dropped"); } } ret = 1; out: bin_clear_free(tmp, data_len); return ret; } #ifdef CONFIG_CTRL_IFACE static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) { switch (sm->key_mgmt) { case WPA_KEY_MGMT_IEEE8021X: return ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? RSN_AUTH_KEY_MGMT_UNSPEC_802_1X : WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); case WPA_KEY_MGMT_PSK: return (sm->proto == WPA_PROTO_RSN ? RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X : WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); #ifdef CONFIG_IEEE80211R case WPA_KEY_MGMT_FT_IEEE8021X: return RSN_AUTH_KEY_MGMT_FT_802_1X; case WPA_KEY_MGMT_FT_PSK: return RSN_AUTH_KEY_MGMT_FT_PSK; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W case WPA_KEY_MGMT_IEEE8021X_SHA256: return RSN_AUTH_KEY_MGMT_802_1X_SHA256; case WPA_KEY_MGMT_PSK_SHA256: return RSN_AUTH_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ case WPA_KEY_MGMT_CCKM: return (sm->proto == WPA_PROTO_RSN ? RSN_AUTH_KEY_MGMT_CCKM: WPA_AUTH_KEY_MGMT_CCKM); case WPA_KEY_MGMT_WPA_NONE: return WPA_AUTH_KEY_MGMT_NONE; case WPA_KEY_MGMT_IEEE8021X_SUITE_B: return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; default: return 0; } } #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff /** * wpa_sm_get_mib - Dump text list of MIB entries * @sm: Pointer to WPA state machine data from wpa_sm_init() * @buf: Buffer for the list * @buflen: Length of the buffer * Returns: Number of bytes written to buffer * * This function is used fetch dot11 MIB variables. */ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) { char pmkid_txt[PMKID_LEN * 2 + 1]; int rsna, ret; size_t len; if (sm->cur_pmksa) { wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), sm->cur_pmksa->pmkid, PMKID_LEN); } else pmkid_txt[0] = '\0'; if ((wpa_key_mgmt_wpa_psk(sm->key_mgmt) || wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) && sm->proto == WPA_PROTO_RSN) rsna = 1; else rsna = 0; ret = os_snprintf(buf, buflen, "dot11RSNAOptionImplemented=TRUE\n" "dot11RSNAPreauthenticationImplemented=TRUE\n" "dot11RSNAEnabled=%s\n" "dot11RSNAPreauthenticationEnabled=%s\n" "dot11RSNAConfigVersion=%d\n" "dot11RSNAConfigPairwiseKeysSupported=5\n" "dot11RSNAConfigGroupCipherSize=%d\n" "dot11RSNAConfigPMKLifetime=%d\n" "dot11RSNAConfigPMKReauthThreshold=%d\n" "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n" "dot11RSNAConfigSATimeout=%d\n", rsna ? "TRUE" : "FALSE", rsna ? "TRUE" : "FALSE", RSN_VERSION, wpa_cipher_key_len(sm->group_cipher) * 8, sm->dot11RSNAConfigPMKLifetime, sm->dot11RSNAConfigPMKReauthThreshold, sm->dot11RSNAConfigSATimeout); if (os_snprintf_error(buflen, ret)) return 0; len = ret; ret = os_snprintf( buf + len, buflen - len, "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" "dot11RSNAPMKIDUsed=%s\n" "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n" "dot11RSNA4WayHandshakeFailures=%u\n", RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->pairwise_cipher)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->group_cipher)), pmkid_txt, RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->pairwise_cipher)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->group_cipher)), sm->dot11RSNA4WayHandshakeFailures); if (!os_snprintf_error(buflen - len, ret)) len += ret; return (int) len; } #endif /* CONFIG_CTRL_IFACE */ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason) { struct wpa_sm *sm = ctx; int deauth = 0; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: " MACSTR " reason=%d", MAC2STR(entry->aa), reason); if (sm->cur_pmksa == entry) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: %s current PMKSA entry", reason == PMKSA_REPLACE ? "replaced" : "removed"); pmksa_cache_clear_current(sm); /* * If an entry is simply being replaced, there's no need to * deauthenticate because it will be immediately re-added. * This happens when EAP authentication is completed again * (reauth or failed PMKSA caching attempt). */ if (reason != PMKSA_REPLACE) deauth = 1; } if (reason == PMKSA_EXPIRE && (sm->pmk_len == entry->pmk_len && os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: deauthenticating due to expired PMK"); pmksa_cache_clear_current(sm); deauth = 1; } if (deauth) { os_memset(sm->pmk, 0, sizeof(sm->pmk)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } } /** * wpa_sm_init - Initialize WPA state machine * @ctx: Context pointer for callbacks; this needs to be an allocated buffer * Returns: Pointer to the allocated WPA state machine data * * This function is used to allocate a new WPA state machine and the returned * value is passed to all WPA state machine calls. */ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) { struct wpa_sm *sm; sm = os_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; dl_list_init(&sm->pmksa_candidates); sm->renew_snonce = 1; sm->ctx = ctx; sm->dot11RSNAConfigPMKLifetime = 43200; sm->dot11RSNAConfigPMKReauthThreshold = 70; sm->dot11RSNAConfigSATimeout = 60; sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm); if (sm->pmksa == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "RSN: PMKSA cache initialization failed"); os_free(sm); return NULL; } return sm; } /** * wpa_sm_deinit - Deinitialize WPA state machine * @sm: Pointer to WPA state machine data from wpa_sm_init() */ void wpa_sm_deinit(struct wpa_sm *sm) { if (sm == NULL) return; pmksa_cache_deinit(sm->pmksa); eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); os_free(sm->assoc_wpa_ie); os_free(sm->ap_wpa_ie); os_free(sm->ap_rsn_ie); wpa_sm_drop_sa(sm); os_free(sm->ctx); peerkey_deinit(sm); #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ies); #endif /* CONFIG_IEEE80211R */ os_free(sm); } /** * wpa_sm_notify_assoc - Notify WPA state machine about association * @sm: Pointer to WPA state machine data from wpa_sm_init() * @bssid: The BSSID of the new association * * This function is called to let WPA state machine know that the connection * was established. */ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) { - int clear_ptk = 1; + int clear_keys = 1; if (sm == NULL) return; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Association event - clear replay counter"); os_memcpy(sm->bssid, bssid, ETH_ALEN); os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); sm->rx_replay_counter_set = 0; sm->renew_snonce = 1; if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0) rsn_preauth_deinit(sm); #ifdef CONFIG_IEEE80211R if (wpa_ft_is_completed(sm)) { /* * Clear portValid to kick EAPOL state machine to re-enter * AUTHENTICATED state to get the EAPOL port Authorized. */ eapol_sm_notify_portValid(sm->eapol, FALSE); wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); /* Prepare for the next transition */ wpa_ft_prepare_auth_request(sm, NULL); - clear_ptk = 0; + clear_keys = 0; } #endif /* CONFIG_IEEE80211R */ - if (clear_ptk) { + if (clear_keys) { /* * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if * this is not part of a Fast BSS Transition. */ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK"); sm->ptk_set = 0; os_memset(&sm->ptk, 0, sizeof(sm->ptk)); sm->tptk_set = 0; os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + os_memset(&sm->gtk, 0, sizeof(sm->gtk)); + os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep)); +#ifdef CONFIG_IEEE80211W + os_memset(&sm->igtk, 0, sizeof(sm->igtk)); + os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep)); +#endif /* CONFIG_IEEE80211W */ } #ifdef CONFIG_TDLS wpa_tdls_assoc(sm); #endif /* CONFIG_TDLS */ #ifdef CONFIG_P2P os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr)); #endif /* CONFIG_P2P */ } /** * wpa_sm_notify_disassoc - Notify WPA state machine about disassociation * @sm: Pointer to WPA state machine data from wpa_sm_init() * * This function is called to let WPA state machine know that the connection * was lost. This will abort any existing pre-authentication session. */ void wpa_sm_notify_disassoc(struct wpa_sm *sm) { eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); peerkey_deinit(sm); rsn_preauth_deinit(sm); pmksa_cache_clear_current(sm); if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) sm->dot11RSNA4WayHandshakeFailures++; #ifdef CONFIG_TDLS wpa_tdls_disassoc(sm); #endif /* CONFIG_TDLS */ +#ifdef CONFIG_IEEE80211R + sm->ft_reassoc_completed = 0; +#endif /* CONFIG_IEEE80211R */ /* Keys are not needed in the WPA state machine anymore */ wpa_sm_drop_sa(sm); sm->msg_3_of_4_ok = 0; } /** * wpa_sm_set_pmk - Set PMK * @sm: Pointer to WPA state machine data from wpa_sm_init() * @pmk: The new PMK * @pmk_len: The length of the new PMK in bytes * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK * * Configure the PMK for WPA state machine. */ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, const u8 *bssid) { if (sm == NULL) return; sm->pmk_len = pmk_len; os_memcpy(sm->pmk, pmk, pmk_len); #ifdef CONFIG_IEEE80211R /* Set XXKey to be PSK for FT key derivation */ sm->xxkey_len = pmk_len; os_memcpy(sm->xxkey, pmk, pmk_len); #endif /* CONFIG_IEEE80211R */ if (bssid) { pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0, bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt); } } /** * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA * @sm: Pointer to WPA state machine data from wpa_sm_init() * * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK * will be cleared. */ void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) { if (sm == NULL) return; if (sm->cur_pmksa) { sm->pmk_len = sm->cur_pmksa->pmk_len; os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len); } else { sm->pmk_len = PMK_LEN; os_memset(sm->pmk, 0, PMK_LEN); } } /** * wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled * @sm: Pointer to WPA state machine data from wpa_sm_init() * @fast_reauth: Whether fast reauthentication (EAP) is allowed */ void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth) { if (sm) sm->fast_reauth = fast_reauth; } /** * wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks * @sm: Pointer to WPA state machine data from wpa_sm_init() * @scard_ctx: Context pointer for smartcard related callback functions */ void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx) { if (sm == NULL) return; sm->scard_ctx = scard_ctx; if (sm->preauth_eapol) eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx); } /** * wpa_sm_set_config - Notification of current configration change * @sm: Pointer to WPA state machine data from wpa_sm_init() * @config: Pointer to current network configuration * * Notify WPA state machine that configuration has changed. config will be * stored as a backpointer to network configuration. This can be %NULL to clear * the stored pointed. */ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) { if (!sm) return; if (config) { sm->network_ctx = config->network_ctx; sm->peerkey_enabled = config->peerkey_enabled; sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher; sm->proactive_key_caching = config->proactive_key_caching; sm->eap_workaround = config->eap_workaround; sm->eap_conf_ctx = config->eap_conf_ctx; if (config->ssid) { os_memcpy(sm->ssid, config->ssid, config->ssid_len); sm->ssid_len = config->ssid_len; } else sm->ssid_len = 0; sm->wpa_ptk_rekey = config->wpa_ptk_rekey; sm->p2p = config->p2p; } else { sm->network_ctx = NULL; sm->peerkey_enabled = 0; sm->allowed_pairwise_cipher = 0; sm->proactive_key_caching = 0; sm->eap_workaround = 0; sm->eap_conf_ctx = NULL; sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; sm->p2p = 0; } } /** * wpa_sm_set_own_addr - Set own MAC address * @sm: Pointer to WPA state machine data from wpa_sm_init() * @addr: Own MAC address */ void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) { if (sm) os_memcpy(sm->own_addr, addr, ETH_ALEN); } /** * wpa_sm_set_ifname - Set network interface name * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ifname: Interface name * @bridge_ifname: Optional bridge interface name (for pre-auth) */ void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, const char *bridge_ifname) { if (sm) { sm->ifname = ifname; sm->bridge_ifname = bridge_ifname; } } /** * wpa_sm_set_eapol - Set EAPOL state machine pointer * @sm: Pointer to WPA state machine data from wpa_sm_init() * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init() */ void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) { if (sm) sm->eapol = eapol; } /** * wpa_sm_set_param - Set WPA state machine parameters * @sm: Pointer to WPA state machine data from wpa_sm_init() * @param: Parameter field * @value: Parameter value * Returns: 0 on success, -1 on failure */ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, unsigned int value) { int ret = 0; if (sm == NULL) return -1; switch (param) { case RSNA_PMK_LIFETIME: if (value > 0) sm->dot11RSNAConfigPMKLifetime = value; else ret = -1; break; case RSNA_PMK_REAUTH_THRESHOLD: if (value > 0 && value <= 100) sm->dot11RSNAConfigPMKReauthThreshold = value; else ret = -1; break; case RSNA_SA_TIMEOUT: if (value > 0) sm->dot11RSNAConfigSATimeout = value; else ret = -1; break; case WPA_PARAM_PROTO: sm->proto = value; break; case WPA_PARAM_PAIRWISE: sm->pairwise_cipher = value; break; case WPA_PARAM_GROUP: sm->group_cipher = value; break; case WPA_PARAM_KEY_MGMT: sm->key_mgmt = value; break; #ifdef CONFIG_IEEE80211W case WPA_PARAM_MGMT_GROUP: sm->mgmt_group_cipher = value; break; #endif /* CONFIG_IEEE80211W */ case WPA_PARAM_RSN_ENABLED: sm->rsn_enabled = value; break; case WPA_PARAM_MFP: sm->mfp = value; break; default: break; } return ret; } /** * wpa_sm_get_status - Get WPA state machine * @sm: Pointer to WPA state machine data from wpa_sm_init() * @buf: Buffer for status information * @buflen: Maximum buffer length * @verbose: Whether to include verbose status information * Returns: Number of bytes written to buf. * * Query WPA state machine for status information. This function fills in * a text area with current status information. If the buffer (buf) is not * large enough, status information will be truncated to fit the buffer. */ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose) { char *pos = buf, *end = buf + buflen; int ret; ret = os_snprintf(pos, end - pos, "pairwise_cipher=%s\n" "group_cipher=%s\n" "key_mgmt=%s\n", wpa_cipher_txt(sm->pairwise_cipher), wpa_cipher_txt(sm->group_cipher), wpa_key_mgmt_txt(sm->key_mgmt, sm->proto)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) { struct wpa_ie_data rsn; if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) { ret = os_snprintf(pos, end - pos, "pmf=%d\n", (rsn.capabilities & WPA_CAPABILITY_MFPR) ? 2 : 1); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } } return pos - buf; } int wpa_sm_pmf_enabled(struct wpa_sm *sm) { struct wpa_ie_data rsn; if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie) return 0; if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) return 1; return 0; } /** * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration * @sm: Pointer to WPA state machine data from wpa_sm_init() * @wpa_ie: Pointer to buffer for WPA/RSN IE * @wpa_ie_len: Pointer to the length of the wpa_ie buffer * Returns: 0 on success, -1 on failure */ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, size_t *wpa_ie_len) { int res; if (sm == NULL) return -1; res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len); if (res < 0) return -1; *wpa_ie_len = res; wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default", wpa_ie, *wpa_ie_len); if (sm->assoc_wpa_ie == NULL) { /* * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets * the correct version of the IE even if PMKSA caching is * aborted (which would remove PMKID from IE generation). */ sm->assoc_wpa_ie = os_malloc(*wpa_ie_len); if (sm->assoc_wpa_ie == NULL) return -1; os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len); sm->assoc_wpa_ie_len = *wpa_ie_len; } return 0; } /** * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ie: Pointer to IE data (starting from id) * @len: IE length * Returns: 0 on success, -1 on failure * * Inform WPA state machine about the WPA/RSN IE used in (Re)Association * Request frame. The IE will be used to override the default value generated * with wpa_sm_set_assoc_wpa_ie_default(). */ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { if (sm == NULL) return -1; os_free(sm->assoc_wpa_ie); if (ie == NULL || len == 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing own WPA/RSN IE"); sm->assoc_wpa_ie = NULL; sm->assoc_wpa_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len); sm->assoc_wpa_ie = os_malloc(len); if (sm->assoc_wpa_ie == NULL) return -1; os_memcpy(sm->assoc_wpa_ie, ie, len); sm->assoc_wpa_ie_len = len; } return 0; } /** * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ie: Pointer to IE data (starting from id) * @len: IE length * Returns: 0 on success, -1 on failure * * Inform WPA state machine about the WPA IE used in Beacon / Probe Response * frame. */ int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { if (sm == NULL) return -1; os_free(sm->ap_wpa_ie); if (ie == NULL || len == 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing AP WPA IE"); sm->ap_wpa_ie = NULL; sm->ap_wpa_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len); sm->ap_wpa_ie = os_malloc(len); if (sm->ap_wpa_ie == NULL) return -1; os_memcpy(sm->ap_wpa_ie, ie, len); sm->ap_wpa_ie_len = len; } return 0; } /** * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ie: Pointer to IE data (starting from id) * @len: IE length * Returns: 0 on success, -1 on failure * * Inform WPA state machine about the RSN IE used in Beacon / Probe Response * frame. */ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { if (sm == NULL) return -1; os_free(sm->ap_rsn_ie); if (ie == NULL || len == 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing AP RSN IE"); sm->ap_rsn_ie = NULL; sm->ap_rsn_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len); sm->ap_rsn_ie = os_malloc(len); if (sm->ap_rsn_ie == NULL) return -1; os_memcpy(sm->ap_rsn_ie, ie, len); sm->ap_rsn_ie_len = len; } return 0; } /** * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE * @sm: Pointer to WPA state machine data from wpa_sm_init() * @data: Pointer to data area for parsing results * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure * * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the * parsed data into data. */ int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) { if (sm == NULL) return -1; if (sm->assoc_wpa_ie == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: No WPA/RSN IE available from association info"); return -1; } if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data)) return -2; return 0; } int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) { return pmksa_cache_list(sm->pmksa, buf, len); } void wpa_sm_drop_sa(struct wpa_sm *sm) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); sm->ptk_set = 0; sm->tptk_set = 0; os_memset(sm->pmk, 0, sizeof(sm->pmk)); os_memset(&sm->ptk, 0, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + os_memset(&sm->gtk, 0, sizeof(sm->gtk)); + os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep)); +#ifdef CONFIG_IEEE80211W + os_memset(&sm->igtk, 0, sizeof(sm->igtk)); + os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep)); +#endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0)); os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); #endif /* CONFIG_IEEE80211R */ } int wpa_sm_has_ptk(struct wpa_sm *sm) { if (sm == NULL) return 0; return sm->ptk_set; } void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr) { os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN); } void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) { pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0); } #ifdef CONFIG_WNM int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) { u16 keyinfo; u8 keylen; /* plaintext key len */ u8 *key_rsc; if (subelem_id == WNM_SLEEP_SUBELEM_GTK) { struct wpa_gtk_data gd; os_memset(&gd, 0, sizeof(gd)); keylen = wpa_cipher_key_len(sm->group_cipher); gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher); gd.alg = wpa_cipher_to_alg(sm->group_cipher); if (gd.alg == WPA_ALG_NONE) { wpa_printf(MSG_DEBUG, "Unsupported group cipher suite"); return -1; } key_rsc = buf + 5; keyinfo = WPA_GET_LE16(buf + 2); gd.gtk_len = keylen; if (gd.gtk_len != buf[4]) { wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d", gd.gtk_len, buf[4]); return -1; } gd.keyidx = keyinfo & 0x03; /* B0 - B1 */ gd.tx = wpa_supplicant_gtk_tx_bit_workaround( sm, !!(keyinfo & WPA_KEY_INFO_TXRX)); os_memcpy(gd.gtk, buf + 13, gd.gtk_len); wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)", gd.gtk, gd.gtk_len); - if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) { + if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 1)) { os_memset(&gd, 0, sizeof(gd)); wpa_printf(MSG_DEBUG, "Failed to install the GTK in " "WNM mode"); return -1; } os_memset(&gd, 0, sizeof(gd)); #ifdef CONFIG_IEEE80211W } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { - struct wpa_igtk_kde igd; - u16 keyidx; + const struct wpa_igtk_kde *igtk; - os_memset(&igd, 0, sizeof(igd)); - keylen = wpa_cipher_key_len(sm->mgmt_group_cipher); - os_memcpy(igd.keyid, buf + 2, 2); - os_memcpy(igd.pn, buf + 4, 6); - - keyidx = WPA_GET_LE16(igd.keyid); - os_memcpy(igd.igtk, buf + 10, keylen); - - wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", - igd.igtk, keylen); - if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), - broadcast_ether_addr, - keyidx, 0, igd.pn, sizeof(igd.pn), - igd.igtk, keylen) < 0) { - wpa_printf(MSG_DEBUG, "Failed to install the IGTK in " - "WNM mode"); - os_memset(&igd, 0, sizeof(igd)); + igtk = (const struct wpa_igtk_kde *) (buf + 2); + if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0) return -1; - } - os_memset(&igd, 0, sizeof(igd)); #endif /* CONFIG_IEEE80211W */ } else { wpa_printf(MSG_DEBUG, "Unknown element id"); return -1; } return 0; } #endif /* CONFIG_WNM */ #ifdef CONFIG_PEERKEY int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_peerkey *peerkey; for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) break; } if (!peerkey) return 0; wpa_sm_rx_eapol(sm, src_addr, buf, len); return 1; } #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_P2P int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf) { if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0) return -1; os_memcpy(buf, sm->p2p_ip_addr, 3 * 4); return 0; } #endif /* CONFIG_P2P */ void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter) { if (rx_replay_counter == NULL) return; os_memcpy(sm->rx_replay_counter, rx_replay_counter, WPA_REPLAY_COUNTER_LEN); sm->rx_replay_counter_set = 1; wpa_printf(MSG_DEBUG, "Updated key replay counter"); } void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, size_t ptk_kck_len, const u8 *ptk_kek, size_t ptk_kek_len) { if (ptk_kck && ptk_kck_len <= WPA_KCK_MAX_LEN) { os_memcpy(sm->ptk.kck, ptk_kck, ptk_kck_len); sm->ptk.kck_len = ptk_kck_len; wpa_printf(MSG_DEBUG, "Updated PTK KCK"); } if (ptk_kek && ptk_kek_len <= WPA_KEK_MAX_LEN) { os_memcpy(sm->ptk.kek, ptk_kek, ptk_kek_len); sm->ptk.kek_len = ptk_kek_len; wpa_printf(MSG_DEBUG, "Updated PTK KEK"); } sm->ptk_set = 1; } Index: releng/11.1/contrib/wpa/src/rsn_supp/wpa_ft.c =================================================================== --- releng/11.1/contrib/wpa/src/rsn_supp/wpa_ft.c (revision 324698) +++ releng/11.1/contrib/wpa/src/rsn_supp/wpa_ft.c (revision 324699) @@ -1,847 +1,855 @@ /* * WPA Supplicant - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto/aes_wrap.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wpa.h" #include "wpa_i.h" #ifdef CONFIG_IEEE80211R int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *anonce = key->key_nonce; if (sm->xxkey_len == 0) { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " "derivation"); return -1; } wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid, sm->ssid_len, sm->mobility_domain, sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, sm->pmk_r0, sm->pmk_r0_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", sm->pmk_r0_name, WPA_PMK_NAME_LEN); wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk, ptk_name, sm->key_mgmt, sm->pairwise_cipher); } /** * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ies: Association Response IEs or %NULL to clear FT parameters * @ies_len: Length of ies buffer in octets * Returns: 0 on success, -1 on failure */ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) { struct wpa_ft_ies ft; if (sm == NULL) return 0; if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0) return -1; if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) return -1; if (ft.mdie) { wpa_hexdump(MSG_DEBUG, "FT: Mobility domain", ft.mdie, MOBILITY_DOMAIN_ID_LEN); os_memcpy(sm->mobility_domain, ft.mdie, MOBILITY_DOMAIN_ID_LEN); sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN]; wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x", sm->mdie_ft_capab); } else os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN); if (ft.r0kh_id) { wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", ft.r0kh_id, ft.r0kh_id_len); os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len); sm->r0kh_id_len = ft.r0kh_id_len; } else { /* FIX: When should R0KH-ID be cleared? We need to keep the * old R0KH-ID in order to be able to use this during FT. */ /* * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN); * sm->r0kh_id_len = 0; */ } if (ft.r1kh_id) { wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", ft.r1kh_id, FT_R1KH_ID_LEN); os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN); } else os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN); os_free(sm->assoc_resp_ies); sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2); if (sm->assoc_resp_ies) { u8 *pos = sm->assoc_resp_ies; if (ft.mdie) { os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2); pos += ft.mdie_len + 2; } if (ft.ftie) { os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2); pos += ft.ftie_len + 2; } sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies; wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from " "(Re)Association Response", sm->assoc_resp_ies, sm->assoc_resp_ies_len); } return 0; } /** * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @len: Buffer for returning the length of the IEs * @anonce: ANonce or %NULL if not yet available * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List * @kck: 128-bit KCK for MIC or %NULL if no MIC is used * @kck_len: KCK length in octets * @target_ap: Target AP address * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL * @ric_ies_len: Length of ric_ies buffer in octets * @ap_mdie: Mobility Domain IE from the target AP * Returns: Pointer to buffer with IEs or %NULL on failure * * Caller is responsible for freeing the returned buffer with os_free(); */ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, const u8 *anonce, const u8 *pmk_name, const u8 *kck, size_t kck_len, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len, const u8 *ap_mdie) { size_t buf_len; u8 *buf, *pos, *ftie_len, *ftie_pos; struct rsn_mdie *mdie; struct rsn_ftie *ftie; struct rsn_ie_hdr *rsnie; u16 capab; sm->ft_completed = 0; + sm->ft_reassoc_completed = 0; buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 2 + sm->r0kh_id_len + ric_ies_len + 100; buf = os_zalloc(buf_len); if (buf == NULL) return NULL; pos = buf; /* RSNIE[PMKR0Name/PMKR1Name] */ rsnie = (struct rsn_ie_hdr *) pos; rsnie->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(rsnie->version, RSN_VERSION); pos = (u8 *) (rsnie + 1); /* Group Suite Selector */ if (!wpa_cipher_valid_group(sm->group_cipher)) { wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", sm->group_cipher); os_free(buf); return NULL; } RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, sm->group_cipher)); pos += RSN_SELECTOR_LEN; /* Pairwise Suite Count */ WPA_PUT_LE16(pos, 1); pos += 2; /* Pairwise Suite List */ if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", sm->pairwise_cipher); os_free(buf); return NULL; } RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, sm->pairwise_cipher)); pos += RSN_SELECTOR_LEN; /* Authenticated Key Management Suite Count */ WPA_PUT_LE16(pos, 1); pos += 2; /* Authenticated Key Management Suite List */ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); else { wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)", sm->key_mgmt); os_free(buf); return NULL; } pos += RSN_SELECTOR_LEN; /* RSN Capabilities */ capab = 0; #ifdef CONFIG_IEEE80211W if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) capab |= WPA_CAPABILITY_MFPC; #endif /* CONFIG_IEEE80211W */ WPA_PUT_LE16(pos, capab); pos += 2; /* PMKID Count */ WPA_PUT_LE16(pos, 1); pos += 2; /* PMKID List [PMKR0Name/PMKR1Name] */ os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN); pos += WPA_PMK_NAME_LEN; #ifdef CONFIG_IEEE80211W if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { /* Management Group Cipher Suite */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); pos += RSN_SELECTOR_LEN; } #endif /* CONFIG_IEEE80211W */ rsnie->len = (pos - (u8 *) rsnie) - 2; /* MDIE */ *pos++ = WLAN_EID_MOBILITY_DOMAIN; *pos++ = sizeof(*mdie); mdie = (struct rsn_mdie *) pos; pos += sizeof(*mdie); os_memcpy(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN); mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : sm->mdie_ft_capab; /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */ ftie_pos = pos; *pos++ = WLAN_EID_FAST_BSS_TRANSITION; ftie_len = pos++; ftie = (struct rsn_ftie *) pos; pos += sizeof(*ftie); os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); if (anonce) os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); if (kck) { /* R1KH-ID sub-element in third FT message */ *pos++ = FTIE_SUBELEM_R1KH_ID; *pos++ = FT_R1KH_ID_LEN; os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN); pos += FT_R1KH_ID_LEN; } /* R0KH-ID sub-element */ *pos++ = FTIE_SUBELEM_R0KH_ID; *pos++ = sm->r0kh_id_len; os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len); pos += sm->r0kh_id_len; *ftie_len = pos - ftie_len - 1; if (ric_ies) { /* RIC Request */ os_memcpy(pos, ric_ies, ric_ies_len); pos += ric_ies_len; } if (kck) { /* * IEEE Std 802.11r-2008, 11A.8.4 * MIC shall be calculated over: * non-AP STA MAC address * Target AP MAC address * Transaction seq number (5 for ReassocReq, 3 otherwise) * RSN IE * MDIE * FTIE (with MIC field set to 0) * RIC-Request (if present) */ /* Information element count */ ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len); if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5, ((u8 *) mdie) - 2, 2 + sizeof(*mdie), ftie_pos, 2 + *ftie_len, (u8 *) rsnie, 2 + rsnie->len, ric_ies, ric_ies_len, ftie->mic) < 0) { wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); os_free(buf); return NULL; } } *len = pos - buf; return buf; } static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) { int keylen; enum wpa_alg alg; u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 }; wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver."); if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d", sm->pairwise_cipher); return -1; } alg = wpa_cipher_to_alg(sm->pairwise_cipher); keylen = wpa_cipher_key_len(sm->pairwise_cipher); if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc), (u8 *) sm->ptk.tk, keylen) < 0) { wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver"); return -1; } return 0; } /** * wpa_ft_prepare_auth_request - Generate over-the-air auth request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @mdie: Target AP MDIE * Returns: 0 on success, -1 on failure */ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) { u8 *ft_ies; size_t ft_ies_len; /* Generate a new SNonce */ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); return -1; } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, NULL, 0, sm->bssid, NULL, 0, mdie); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); os_free(ft_ies); } return 0; } int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len) { u8 *ft_ies; size_t ft_ies_len; struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; u8 ptk_name[WPA_PMK_NAME_LEN]; int ret; const u8 *bssid; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len); if (ft_action) { if (!sm->over_the_ds_in_progress) { wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " "- drop FT Action Response"); return -1; } if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " "with this Target AP - drop FT Action " "Response"); return -1; } } if (!wpa_key_mgmt_ft(sm->key_mgmt)) { wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " "enabled for this connection"); return -1; } if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; } mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); return -1; } ftie = (struct rsn_ftie *) parse.ftie; if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); return -1; } if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", ftie->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->snonce, WPA_NONCE_LEN); return -1; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); return -1; } if (parse.r0kh_id_len != sm->r0kh_id_len || os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " "the current R0KH-ID"); wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); return -1; } if (parse.rsn_pmkid == NULL || os_memcmp_const(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) { wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in " "RSNIE"); return -1; } os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN); os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN); wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0) return -1; ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, sm->pmk_r1_name, sm->ptk.kck, sm->ptk.kck_len, bssid, ric_ies, ric_ies_len, parse.mdie ? parse.mdie - 2 : NULL); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); os_free(ft_ies); } wpa_sm_mark_authenticated(sm, bssid); ret = wpa_ft_install_ptk(sm, bssid); if (ret) { /* * Some drivers do not support key configuration when we are * not associated with the target AP. Work around this by * trying again after the following reassociation gets * completed. */ wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to " "association - try again after reassociation"); sm->set_ptk_after_assoc = 1; } else sm->set_ptk_after_assoc = 0; sm->ft_completed = 1; if (ft_action) { /* * The caller is expected trigger re-association with the * Target AP. */ os_memcpy(sm->bssid, target_ap, ETH_ALEN); } return 0; } int wpa_ft_is_completed(struct wpa_sm *sm) { if (sm == NULL) return 0; if (!wpa_key_mgmt_ft(sm->key_mgmt)) return 0; return sm->ft_completed; } void wpa_reset_ft_completed(struct wpa_sm *sm) { if (sm != NULL) sm->ft_completed = 0; } static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, size_t gtk_elem_len) { u8 gtk[32]; int keyidx; enum wpa_alg alg; size_t gtk_len, keylen, rsc_len; if (gtk_elem == NULL) { wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE"); return 0; } wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp", gtk_elem, gtk_elem_len); if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 || gtk_elem_len - 19 > sizeof(gtk)) { wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem " "length %lu", (unsigned long) gtk_elem_len); return -1; } gtk_len = gtk_elem_len - 19; if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, gtk_len / 8, gtk_elem + 11, gtk)) { wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " "decrypt GTK"); return -1; } keylen = wpa_cipher_key_len(sm->group_cipher); rsc_len = wpa_cipher_rsc_len(sm->group_cipher); alg = wpa_cipher_to_alg(sm->group_cipher); if (alg == WPA_ALG_NONE) { wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", sm->group_cipher); return -1; } if (gtk_len < keylen) { wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE"); return -1; } /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */ keyidx = WPA_GET_LE16(gtk_elem) & 0x03; if (gtk_elem[2] != keylen) { wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d " "negotiated %lu", gtk_elem[2], (unsigned long) keylen); return -1; } wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); if (sm->group_cipher == WPA_CIPHER_TKIP) { /* Swap Tx/Rx keys for Michael MIC */ u8 tmp[8]; os_memcpy(tmp, gtk + 16, 8); os_memcpy(gtk + 16, gtk + 24, 8); os_memcpy(gtk + 24, tmp, 8); } if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0, gtk_elem + 3, rsc_len, gtk, keylen) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " "driver."); return -1; } return 0; } #ifdef CONFIG_IEEE80211W static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, size_t igtk_elem_len) { u8 igtk[WPA_IGTK_LEN]; u16 keyidx; if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) return 0; if (igtk_elem == NULL) { wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE"); return 0; } wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp", igtk_elem, igtk_elem_len); if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) { wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem " "length %lu", (unsigned long) igtk_elem_len); return -1; } if (igtk_elem[8] != WPA_IGTK_LEN) { wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length " "%d", igtk_elem[8]); return -1; } if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) { wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " "decrypt IGTK"); return -1; } /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */ keyidx = WPA_GET_LE16(igtk_elem); wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, WPA_IGTK_LEN); if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the " "driver."); return -1; } return 0; } #endif /* CONFIG_IEEE80211W */ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr) { struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; unsigned int count; u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); if (!wpa_key_mgmt_ft(sm->key_mgmt)) { wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " "enabled for this connection"); return -1; } + if (sm->ft_reassoc_completed) { + wpa_printf(MSG_DEBUG, "FT: Reassociation has already been completed for this FT protocol instance - ignore unexpected retransmission"); + return 0; + } + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; } mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); return -1; } ftie = (struct rsn_ftie *) parse.ftie; if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); return -1; } if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", ftie->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->snonce, WPA_NONCE_LEN); return -1; } if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", ftie->anonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", sm->anonce, WPA_NONCE_LEN); return -1; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); return -1; } if (parse.r0kh_id_len != sm->r0kh_id_len || os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " "the current R0KH-ID"); wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); return -1; } if (os_memcmp_const(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " "ReassocResp"); return -1; } if (parse.rsn_pmkid == NULL || os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); return -1; } count = 3; if (parse.ric) count += ieee802_11_ie_count(parse.ric, parse.ric_len); if (ftie->mic_control[1] != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", ftie->mic_control[1], count); return -1; } if (wpa_ft_mic(sm->ptk.kck, sm->ptk.kck_len, sm->own_addr, src_addr, 6, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, parse.rsn - 2, parse.rsn_len + 2, parse.ric, parse.ric_len, mic) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); return -1; } if (os_memcmp_const(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); return -1; } + + sm->ft_reassoc_completed = 1; if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) return -1; #ifdef CONFIG_IEEE80211W if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0) return -1; #endif /* CONFIG_IEEE80211W */ if (sm->set_ptk_after_assoc) { wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we " "are associated"); if (wpa_ft_install_ptk(sm, src_addr) < 0) return -1; sm->set_ptk_after_assoc = 0; } if (parse.ric) { wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", parse.ric, parse.ric_len); /* TODO: parse response and inform driver about results when * using wpa_supplicant SME */ } wpa_printf(MSG_DEBUG, "FT: Completed successfully"); return 0; } /** * wpa_ft_start_over_ds - Generate over-the-DS auth request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @target_ap: Target AP Address * @mdie: Mobility Domain IE from the target AP * Returns: 0 on success, -1 on failure */ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, const u8 *mdie) { u8 *ft_ies; size_t ft_ies_len; wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR, MAC2STR(target_ap)); /* Generate a new SNonce */ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); return -1; } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, NULL, 0, target_ap, NULL, 0, mdie); if (ft_ies) { sm->over_the_ds_in_progress = 1; os_memcpy(sm->target_ap, target_ap, ETH_ALEN); wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len); os_free(ft_ies); } return 0; } #endif /* CONFIG_IEEE80211R */ Index: releng/11.1/contrib/wpa/src/rsn_supp/wpa_i.h =================================================================== --- releng/11.1/contrib/wpa/src/rsn_supp/wpa_i.h (revision 324698) +++ releng/11.1/contrib/wpa/src/rsn_supp/wpa_i.h (revision 324699) @@ -1,371 +1,378 @@ /* * Internal WPA/RSN supplicant state machine definitions * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_I_H #define WPA_I_H #include "utils/list.h" struct wpa_peerkey; struct wpa_tdls_peer; struct wpa_eapol_key; /** * struct wpa_sm - Internal WPA state machine data */ struct wpa_sm { u8 pmk[PMK_LEN]; size_t pmk_len; struct wpa_ptk ptk, tptk; int ptk_set, tptk_set; unsigned int msg_3_of_4_ok:1; u8 snonce[WPA_NONCE_LEN]; u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ int renew_snonce; u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; int rx_replay_counter_set; u8 request_counter[WPA_REPLAY_COUNTER_LEN]; + struct wpa_gtk gtk; + struct wpa_gtk gtk_wnm_sleep; +#ifdef CONFIG_IEEE80211W + struct wpa_igtk igtk; + struct wpa_igtk igtk_wnm_sleep; +#endif /* CONFIG_IEEE80211W */ struct eapol_sm *eapol; /* EAPOL state machine from upper level code */ struct rsn_pmksa_cache *pmksa; /* PMKSA cache */ struct rsn_pmksa_cache_entry *cur_pmksa; /* current PMKSA entry */ struct dl_list pmksa_candidates; struct l2_packet_data *l2_preauth; struct l2_packet_data *l2_preauth_br; struct l2_packet_data *l2_tdls; u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or * 00:00:00:00:00:00 if no pre-auth is * in progress */ struct eapol_sm *preauth_eapol; struct wpa_sm_ctx *ctx; void *scard_ctx; /* context for smartcard callbacks */ int fast_reauth; /* whether EAP fast re-authentication is enabled */ void *network_ctx; int peerkey_enabled; int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ int proactive_key_caching; int eap_workaround; void *eap_conf_ctx; u8 ssid[32]; size_t ssid_len; int wpa_ptk_rekey; int p2p; u8 own_addr[ETH_ALEN]; const char *ifname; const char *bridge_ifname; u8 bssid[ETH_ALEN]; unsigned int dot11RSNAConfigPMKLifetime; unsigned int dot11RSNAConfigPMKReauthThreshold; unsigned int dot11RSNAConfigSATimeout; unsigned int dot11RSNA4WayHandshakeFailures; /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ unsigned int proto; unsigned int pairwise_cipher; unsigned int group_cipher; unsigned int key_mgmt; unsigned int mgmt_group_cipher; int rsn_enabled; /* Whether RSN is enabled in configuration */ int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */ u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ size_t assoc_wpa_ie_len; u8 *ap_wpa_ie, *ap_rsn_ie; size_t ap_wpa_ie_len, ap_rsn_ie_len; #ifdef CONFIG_PEERKEY struct wpa_peerkey *peerkey; #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_TDLS struct wpa_tdls_peer *tdls; int tdls_prohibited; int tdls_chan_switch_prohibited; int tdls_disabled; /* The driver supports TDLS */ int tdls_supported; /* * The driver requires explicit discovery/setup/teardown frames sent * to it via tdls_mgmt. */ int tdls_external_setup; /* The driver supports TDLS channel switching */ int tdls_chan_switch; #endif /* CONFIG_TDLS */ #ifdef CONFIG_IEEE80211R u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ size_t xxkey_len; u8 pmk_r0[PMK_LEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; size_t r0kh_id_len; u8 r1kh_id[FT_R1KH_ID_LEN]; int ft_completed; + int ft_reassoc_completed; int over_the_ds_in_progress; u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */ int set_ptk_after_assoc; u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */ u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */ size_t assoc_resp_ies_len; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P u8 p2p_ip_addr[3 * 4]; #endif /* CONFIG_P2P */ }; static inline void wpa_sm_set_state(struct wpa_sm *sm, enum wpa_states state) { WPA_ASSERT(sm->ctx->set_state); sm->ctx->set_state(sm->ctx->ctx, state); } static inline enum wpa_states wpa_sm_get_state(struct wpa_sm *sm) { WPA_ASSERT(sm->ctx->get_state); return sm->ctx->get_state(sm->ctx->ctx); } static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code) { WPA_ASSERT(sm->ctx->deauthenticate); sm->ctx->deauthenticate(sm->ctx->ctx, reason_code); } static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { WPA_ASSERT(sm->ctx->set_key); return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); } static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm) { WPA_ASSERT(sm->ctx->get_network_ctx); return sm->ctx->get_network_ctx(sm->ctx->ctx); } static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid) { WPA_ASSERT(sm->ctx->get_bssid); return sm->ctx->get_bssid(sm->ctx->ctx, bssid); } static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest, u16 proto, const u8 *buf, size_t len) { WPA_ASSERT(sm->ctx->ether_send); return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len); } static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm) { WPA_ASSERT(sm->ctx->get_beacon_ie); return sm->ctx->get_beacon_ie(sm->ctx->ctx); } static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm) { WPA_ASSERT(sm->ctx->cancel_auth_timeout); sm->ctx->cancel_auth_timeout(sm->ctx->ctx); } static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, const void *data, u16 data_len, size_t *msg_len, void **data_pos) { WPA_ASSERT(sm->ctx->alloc_eapol); return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len, msg_len, data_pos); } static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid, const u8 *pmkid) { WPA_ASSERT(sm->ctx->add_pmkid); return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid); } static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid, const u8 *pmkid) { WPA_ASSERT(sm->ctx->remove_pmkid); return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid); } static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr, int protect_type, int key_type) { WPA_ASSERT(sm->ctx->mlme_setprotection); return sm->ctx->mlme_setprotection(sm->ctx->ctx, addr, protect_type, key_type); } static inline int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md, const u8 *ies, size_t ies_len) { if (sm->ctx->update_ft_ies) return sm->ctx->update_ft_ies(sm->ctx->ctx, md, ies, ies_len); return -1; } static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action, const u8 *target_ap, const u8 *ies, size_t ies_len) { if (sm->ctx->send_ft_action) return sm->ctx->send_ft_action(sm->ctx->ctx, action, target_ap, ies, ies_len); return -1; } static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm, const u8 *target_ap) { if (sm->ctx->mark_authenticated) return sm->ctx->mark_authenticated(sm->ctx->ctx, target_ap); return -1; } static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm) { if (!sm->ctx->set_rekey_offload) return; sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, sm->ptk.kek_len, sm->ptk.kck, sm->ptk.kck_len, sm->rx_replay_counter); } #ifdef CONFIG_TDLS static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm, int *tdls_supported, int *tdls_ext_setup, int *tdls_chan_switch) { if (sm->ctx->tdls_get_capa) return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported, tdls_ext_setup, tdls_chan_switch); return -1; } static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, int initiator, const u8 *buf, size_t len) { if (sm->ctx->send_tdls_mgmt) return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code, dialog_token, status_code, peer_capab, initiator, buf, len); return -1; } static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper, const u8 *peer) { if (sm->ctx->tdls_oper) return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer); return -1; } static inline int wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len, const u8 *supp_channels, size_t supp_channels_len, const u8 *supp_oper_classes, size_t supp_oper_classes_len) { if (sm->ctx->tdls_peer_addset) return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, aid, capability, supp_rates, supp_rates_len, ht_capab, vht_capab, qosinfo, wmm, ext_capab, ext_capab_len, supp_channels, supp_channels_len, supp_oper_classes, supp_oper_classes_len); return -1; } static inline int wpa_sm_tdls_enable_channel_switch(struct wpa_sm *sm, const u8 *addr, u8 oper_class, const struct hostapd_freq_params *freq_params) { if (sm->ctx->tdls_enable_channel_switch) return sm->ctx->tdls_enable_channel_switch(sm->ctx->ctx, addr, oper_class, freq_params); return -1; } static inline int wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr) { if (sm->ctx->tdls_disable_channel_switch) return sm->ctx->tdls_disable_channel_switch(sm->ctx->ctx, addr); return -1; } #endif /* CONFIG_TDLS */ static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) { if (!sm->proactive_key_caching) return 0; if (!sm->ctx->key_mgmt_set_pmk) return -1; return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len); } void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic); int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, int ver, const u8 *nonce, const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ptk *ptk); int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, u16 ver, u16 key_info, struct wpa_ptk *ptk); int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk); void wpa_tdls_assoc(struct wpa_sm *sm); void wpa_tdls_disassoc(struct wpa_sm *sm); #endif /* WPA_I_H */ Index: releng/11.1/contrib/wpa/wpa_supplicant/ctrl_iface.c =================================================================== --- releng/11.1/contrib/wpa/wpa_supplicant/ctrl_iface.c (revision 324698) +++ releng/11.1/contrib/wpa/wpa_supplicant/ctrl_iface.c (revision 324699) @@ -1,9478 +1,9479 @@ /* * WPA Supplicant / Control interface (shared code for all backends) * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #ifdef CONFIG_TESTING_OPTIONS #include #include #endif /* CONFIG_TESTING_OPTIONS */ #include "utils/common.h" #include "utils/eloop.h" #include "utils/uuid.h" #include "common/version.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "crypto/tls.h" #include "ap/hostapd.h" #include "eap_peer/eap.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" #include "rsn_supp/preauth.h" #include "rsn_supp/pmksa_cache.h" #include "l2_packet/l2_packet.h" #include "wps/wps.h" #include "fst/fst.h" #include "fst/fst_ctrl_iface.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "wps_supplicant.h" #include "ibss_rsn.h" #include "ap.h" #include "p2p_supplicant.h" #include "p2p/p2p.h" #include "hs20_supplicant.h" #include "wifi_display.h" #include "notify.h" #include "bss.h" #include "scan.h" #include "ctrl_iface.h" #include "interworking.h" #include "blacklist.h" #include "autoscan.h" #include "wnm_sta.h" #include "offchannel.h" #include "drivers/driver.h" #include "mesh.h" static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, char *buf, int len); static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val); static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val) { char *pos; u8 addr[ETH_ALEN], *filter = NULL, *n; size_t count = 0; pos = val; while (pos) { if (*pos == '\0') break; if (hwaddr_aton(pos, addr)) { os_free(filter); return -1; } n = os_realloc_array(filter, count + 1, ETH_ALEN); if (n == NULL) { os_free(filter); return -1; } filter = n; os_memcpy(filter + count * ETH_ALEN, addr, ETH_ALEN); count++; pos = os_strchr(pos, ' '); if (pos) pos++; } wpa_hexdump(MSG_DEBUG, "bssid_filter", filter, count * ETH_ALEN); os_free(wpa_s->bssid_filter); wpa_s->bssid_filter = filter; wpa_s->bssid_filter_count = count; return 0; } static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val) { char *pos; u8 addr[ETH_ALEN], *bssid = NULL, *n; struct wpa_ssid_value *ssid = NULL, *ns; size_t count = 0, ssid_count = 0; struct wpa_ssid *c; /* * disallow_list ::= | | | "" * SSID_SPEC ::= ssid * BSSID_SPEC ::= bssid */ pos = val; while (pos) { if (*pos == '\0') break; if (os_strncmp(pos, "bssid ", 6) == 0) { int res; pos += 6; res = hwaddr_aton2(pos, addr); if (res < 0) { os_free(ssid); os_free(bssid); wpa_printf(MSG_DEBUG, "Invalid disallow_aps " "BSSID value '%s'", pos); return -1; } pos += res; n = os_realloc_array(bssid, count + 1, ETH_ALEN); if (n == NULL) { os_free(ssid); os_free(bssid); return -1; } bssid = n; os_memcpy(bssid + count * ETH_ALEN, addr, ETH_ALEN); count++; } else if (os_strncmp(pos, "ssid ", 5) == 0) { char *end; pos += 5; end = pos; while (*end) { if (*end == '\0' || *end == ' ') break; end++; } ns = os_realloc_array(ssid, ssid_count + 1, sizeof(struct wpa_ssid_value)); if (ns == NULL) { os_free(ssid); os_free(bssid); return -1; } ssid = ns; if ((end - pos) & 0x01 || end - pos > 2 * SSID_MAX_LEN || hexstr2bin(pos, ssid[ssid_count].ssid, (end - pos) / 2) < 0) { os_free(ssid); os_free(bssid); wpa_printf(MSG_DEBUG, "Invalid disallow_aps " "SSID value '%s'", pos); return -1; } ssid[ssid_count].ssid_len = (end - pos) / 2; wpa_hexdump_ascii(MSG_DEBUG, "disallow_aps SSID", ssid[ssid_count].ssid, ssid[ssid_count].ssid_len); ssid_count++; pos = end; } else { wpa_printf(MSG_DEBUG, "Unexpected disallow_aps value " "'%s'", pos); os_free(ssid); os_free(bssid); return -1; } pos = os_strchr(pos, ' '); if (pos) pos++; } wpa_hexdump(MSG_DEBUG, "disallow_aps_bssid", bssid, count * ETH_ALEN); os_free(wpa_s->disallow_aps_bssid); wpa_s->disallow_aps_bssid = bssid; wpa_s->disallow_aps_bssid_count = count; wpa_printf(MSG_DEBUG, "disallow_aps_ssid_count %d", (int) ssid_count); os_free(wpa_s->disallow_aps_ssid); wpa_s->disallow_aps_ssid = ssid; wpa_s->disallow_aps_ssid_count = ssid_count; if (!wpa_s->current_ssid || wpa_s->wpa_state < WPA_AUTHENTICATING) return 0; c = wpa_s->current_ssid; if (c->mode != WPAS_MODE_INFRA && c->mode != WPAS_MODE_IBSS) return 0; if (!disallowed_bssid(wpa_s, wpa_s->bssid) && !disallowed_ssid(wpa_s, c->ssid, c->ssid_len)) return 0; wpa_printf(MSG_DEBUG, "Disconnect and try to find another network " "because current AP was marked disallowed"); #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; #endif /* CONFIG_SME */ wpa_s->reassociate = 1; wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpa_supplicant_req_scan(wpa_s, 0, 0); return 0; } #ifndef CONFIG_NO_CONFIG_BLOBS static int wpas_ctrl_set_blob(struct wpa_supplicant *wpa_s, char *pos) { char *name = pos; struct wpa_config_blob *blob; size_t len; pos = os_strchr(pos, ' '); if (pos == NULL) return -1; *pos++ = '\0'; len = os_strlen(pos); if (len & 1) return -1; wpa_printf(MSG_DEBUG, "CTRL: Set blob '%s'", name); blob = os_zalloc(sizeof(*blob)); if (blob == NULL) return -1; blob->name = os_strdup(name); blob->data = os_malloc(len / 2); if (blob->name == NULL || blob->data == NULL) { wpa_config_free_blob(blob); return -1; } if (hexstr2bin(pos, blob->data, len / 2) < 0) { wpa_printf(MSG_DEBUG, "CTRL: Invalid blob hex data"); wpa_config_free_blob(blob); return -1; } blob->len = len / 2; wpa_config_set_blob(wpa_s->conf, blob); return 0; } #endif /* CONFIG_NO_CONFIG_BLOBS */ static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd) { char *params; char *pos; int *freqs = NULL; int ret; if (atoi(cmd)) { params = os_strchr(cmd, ' '); os_free(wpa_s->manual_sched_scan_freqs); if (params) { params++; pos = os_strstr(params, "freq="); if (pos) freqs = freq_range_to_channel_list(wpa_s, pos + 5); } wpa_s->manual_sched_scan_freqs = freqs; ret = wpas_start_pno(wpa_s); } else { ret = wpas_stop_pno(wpa_s); } return ret; } static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band) { union wpa_event_data event; if (os_strcmp(band, "AUTO") == 0) wpa_s->setband = WPA_SETBAND_AUTO; else if (os_strcmp(band, "5G") == 0) wpa_s->setband = WPA_SETBAND_5G; else if (os_strcmp(band, "2G") == 0) wpa_s->setband = WPA_SETBAND_2G; else return -1; if (wpa_drv_setband(wpa_s, wpa_s->setband) == 0) { os_memset(&event, 0, sizeof(event)); event.channel_list_changed.initiator = REGDOM_SET_BY_USER; event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN; wpa_supplicant_event(wpa_s, EVENT_CHANNEL_LIST_CHANGED, &event); } return 0; } static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { char *value; int ret = 0; value = os_strchr(cmd, ' '); if (value == NULL) return -1; *value++ = '\0'; wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value); if (os_strcasecmp(cmd, "EAPOL::heldPeriod") == 0) { eapol_sm_configure(wpa_s->eapol, atoi(value), -1, -1, -1); } else if (os_strcasecmp(cmd, "EAPOL::authPeriod") == 0) { eapol_sm_configure(wpa_s->eapol, -1, atoi(value), -1, -1); } else if (os_strcasecmp(cmd, "EAPOL::startPeriod") == 0) { eapol_sm_configure(wpa_s->eapol, -1, -1, atoi(value), -1); } else if (os_strcasecmp(cmd, "EAPOL::maxStart") == 0) { eapol_sm_configure(wpa_s->eapol, -1, -1, -1, atoi(value)); } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) { if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, atoi(value))) ret = -1; } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") == 0) { if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, atoi(value))) ret = -1; } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) { if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value))) ret = -1; } else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) { wpa_s->wps_fragment_size = atoi(value); #ifdef CONFIG_WPS_TESTING } else if (os_strcasecmp(cmd, "wps_version_number") == 0) { long int val; val = strtol(value, NULL, 0); if (val < 0 || val > 0xff) { ret = -1; wpa_printf(MSG_DEBUG, "WPS: Invalid " "wps_version_number %ld", val); } else { wps_version_number = val; wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS " "version %u.%u", (wps_version_number & 0xf0) >> 4, wps_version_number & 0x0f); } } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) { wps_testing_dummy_cred = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d", wps_testing_dummy_cred); } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) { wps_corrupt_pkhash = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", wps_corrupt_pkhash); #endif /* CONFIG_WPS_TESTING */ } else if (os_strcasecmp(cmd, "ampdu") == 0) { if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0) ret = -1; #ifdef CONFIG_TDLS #ifdef CONFIG_TDLS_TESTING } else if (os_strcasecmp(cmd, "tdls_testing") == 0) { extern unsigned int tdls_testing; tdls_testing = strtol(value, NULL, 0); wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing); #endif /* CONFIG_TDLS_TESTING */ } else if (os_strcasecmp(cmd, "tdls_disabled") == 0) { int disabled = atoi(value); wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled); if (disabled) { if (wpa_drv_tdls_oper(wpa_s, TDLS_DISABLE, NULL) < 0) ret = -1; } else if (wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL) < 0) ret = -1; wpa_tdls_enable(wpa_s->wpa, !disabled); #endif /* CONFIG_TDLS */ } else if (os_strcasecmp(cmd, "pno") == 0) { ret = wpas_ctrl_pno(wpa_s, value); } else if (os_strcasecmp(cmd, "radio_disabled") == 0) { int disabled = atoi(value); if (wpa_drv_radio_disable(wpa_s, disabled) < 0) ret = -1; else if (disabled) wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); } else if (os_strcasecmp(cmd, "uapsd") == 0) { if (os_strcmp(value, "disable") == 0) wpa_s->set_sta_uapsd = 0; else { int be, bk, vi, vo; char *pos; /* format: BE,BK,VI,VO;max SP Length */ be = atoi(value); pos = os_strchr(value, ','); if (pos == NULL) return -1; pos++; bk = atoi(pos); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; vi = atoi(pos); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; vo = atoi(pos); /* ignore max SP Length for now */ wpa_s->set_sta_uapsd = 1; wpa_s->sta_uapsd = 0; if (be) wpa_s->sta_uapsd |= BIT(0); if (bk) wpa_s->sta_uapsd |= BIT(1); if (vi) wpa_s->sta_uapsd |= BIT(2); if (vo) wpa_s->sta_uapsd |= BIT(3); } } else if (os_strcasecmp(cmd, "ps") == 0) { ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1); #ifdef CONFIG_WIFI_DISPLAY } else if (os_strcasecmp(cmd, "wifi_display") == 0) { int enabled = !!atoi(value); if (enabled && !wpa_s->global->p2p) ret = -1; else wifi_display_enable(wpa_s->global, enabled); #endif /* CONFIG_WIFI_DISPLAY */ } else if (os_strcasecmp(cmd, "bssid_filter") == 0) { ret = set_bssid_filter(wpa_s, value); } else if (os_strcasecmp(cmd, "disallow_aps") == 0) { ret = set_disallow_aps(wpa_s, value); } else if (os_strcasecmp(cmd, "no_keep_alive") == 0) { wpa_s->no_keep_alive = !!atoi(value); #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { wpa_s->ext_mgmt_frame_handling = !!atoi(value); } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { wpa_s->ext_eapol_frame_io = !!atoi(value); #ifdef CONFIG_AP if (wpa_s->ap_iface) { wpa_s->ap_iface->bss[0]->ext_eapol_frame_io = wpa_s->ext_eapol_frame_io; } #endif /* CONFIG_AP */ } else if (os_strcasecmp(cmd, "extra_roc_dur") == 0) { wpa_s->extra_roc_dur = atoi(value); } else if (os_strcasecmp(cmd, "test_failure") == 0) { wpa_s->test_failure = atoi(value); #endif /* CONFIG_TESTING_OPTIONS */ #ifndef CONFIG_NO_CONFIG_BLOBS } else if (os_strcmp(cmd, "blob") == 0) { ret = wpas_ctrl_set_blob(wpa_s, value); #endif /* CONFIG_NO_CONFIG_BLOBS */ } else if (os_strcasecmp(cmd, "setband") == 0) { ret = wpas_ctrl_set_band(wpa_s, value); } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); if (ret == 0) wpa_supplicant_update_config(wpa_s); } return ret; } static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int res = -1; wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd); if (os_strcmp(cmd, "version") == 0) { res = os_snprintf(buf, buflen, "%s", VERSION_STR); } else if (os_strcasecmp(cmd, "country") == 0) { if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) res = os_snprintf(buf, buflen, "%c%c", wpa_s->conf->country[0], wpa_s->conf->country[1]); #ifdef CONFIG_WIFI_DISPLAY } else if (os_strcasecmp(cmd, "wifi_display") == 0) { int enabled; if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) enabled = 0; else enabled = wpa_s->global->wifi_display; res = os_snprintf(buf, buflen, "%d", enabled); #endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_TESTING_GET_GTK } else if (os_strcmp(cmd, "gtk") == 0) { if (wpa_s->last_gtk_len == 0) return -1; res = wpa_snprintf_hex(buf, buflen, wpa_s->last_gtk, wpa_s->last_gtk_len); return res; #endif /* CONFIG_TESTING_GET_GTK */ } else if (os_strcmp(cmd, "tls_library") == 0) { res = tls_get_library_version(buf, buflen); } else { res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen); } if (os_snprintf_error(buflen, res)) return -1; return res; } #ifdef IEEE8021X_EAPOL static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s, char *addr) { u8 bssid[ETH_ALEN]; struct wpa_ssid *ssid = wpa_s->current_ssid; if (hwaddr_aton(addr, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address " "'%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid)); rsn_preauth_deinit(wpa_s->wpa); if (rsn_preauth_init(wpa_s->wpa, bssid, ssid ? &ssid->eap : NULL)) return -1; return 0; } #endif /* IEEE8021X_EAPOL */ #ifdef CONFIG_PEERKEY /* MLME-STKSTART.request(peer) */ static int wpa_supplicant_ctrl_iface_stkstart( struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR, MAC2STR(peer)); return wpa_sm_stkstart(wpa_s->wpa, peer); } #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_TDLS static int wpa_supplicant_ctrl_iface_tdls_discover( struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; int ret; if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER " MACSTR, MAC2STR(peer)); if (wpa_tdls_is_external_setup(wpa_s->wpa)) ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer); else ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer); return ret; } static int wpa_supplicant_ctrl_iface_tdls_setup( struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; int ret; if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR, MAC2STR(peer)); if ((wpa_s->conf->tdls_external_control) && wpa_tdls_is_external_setup(wpa_s->wpa)) return wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer); wpa_tdls_remove(wpa_s->wpa, peer); if (wpa_tdls_is_external_setup(wpa_s->wpa)) ret = wpa_tdls_start(wpa_s->wpa, peer); else ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer); return ret; } static int wpa_supplicant_ctrl_iface_tdls_teardown( struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; int ret; if (os_strcmp(addr, "*") == 0) { /* remove everyone */ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN *"); wpa_tdls_teardown_peers(wpa_s->wpa); return 0; } if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR, MAC2STR(peer)); if ((wpa_s->conf->tdls_external_control) && wpa_tdls_is_external_setup(wpa_s->wpa)) return wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer); if (wpa_tdls_is_external_setup(wpa_s->wpa)) ret = wpa_tdls_teardown_link( wpa_s->wpa, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); else ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer); return ret; } static int ctrl_iface_get_capability_tdls( struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { int ret; ret = os_snprintf(buf, buflen, "%s\n", wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT ? (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP ? "EXTERNAL" : "INTERNAL") : "UNSUPPORTED"); if (os_snprintf_error(buflen, ret)) return -1; return ret; } static int wpa_supplicant_ctrl_iface_tdls_chan_switch( struct wpa_supplicant *wpa_s, char *cmd) { u8 peer[ETH_ALEN]; struct hostapd_freq_params freq_params; u8 oper_class; char *pos, *end; if (!wpa_tdls_is_external_setup(wpa_s->wpa)) { wpa_printf(MSG_INFO, "tdls_chanswitch: Only supported with external setup"); return -1; } os_memset(&freq_params, 0, sizeof(freq_params)); pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; oper_class = strtol(pos, &end, 10); if (pos == end) { wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid op class provided"); return -1; } pos = end; freq_params.freq = atoi(pos); if (freq_params.freq == 0) { wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid freq provided"); return -1; } #define SET_FREQ_SETTING(str) \ do { \ const char *pos2 = os_strstr(pos, " " #str "="); \ if (pos2) { \ pos2 += sizeof(" " #str "=") - 1; \ freq_params.str = atoi(pos2); \ } \ } while (0) SET_FREQ_SETTING(center_freq1); SET_FREQ_SETTING(center_freq2); SET_FREQ_SETTING(bandwidth); SET_FREQ_SETTING(sec_channel_offset); #undef SET_FREQ_SETTING freq_params.ht_enabled = !!os_strstr(pos, " ht"); freq_params.vht_enabled = !!os_strstr(pos, " vht"); if (hwaddr_aton(cmd, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH: Invalid address '%s'", cmd); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH " MACSTR " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s", MAC2STR(peer), oper_class, freq_params.freq, freq_params.center_freq1, freq_params.center_freq2, freq_params.bandwidth, freq_params.sec_channel_offset, freq_params.ht_enabled ? " HT" : "", freq_params.vht_enabled ? " VHT" : ""); return wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class, &freq_params); } static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch( struct wpa_supplicant *wpa_s, char *cmd) { u8 peer[ETH_ALEN]; if (!wpa_tdls_is_external_setup(wpa_s->wpa)) { wpa_printf(MSG_INFO, "tdls_chanswitch: Only supported with external setup"); return -1; } if (hwaddr_aton(cmd, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH: Invalid address '%s'", cmd); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH " MACSTR, MAC2STR(peer)); return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer); } static int wpa_supplicant_ctrl_iface_tdls_link_status( struct wpa_supplicant *wpa_s, const char *addr, char *buf, size_t buflen) { u8 peer[ETH_ALEN]; const char *tdls_status; int ret; if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: Invalid address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS " MACSTR, MAC2STR(peer)); tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer); wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: %s", tdls_status); ret = os_snprintf(buf, buflen, "TDLS link status: %s\n", tdls_status); if (os_snprintf_error(buflen, ret)) return -1; return ret; } #endif /* CONFIG_TDLS */ static int wmm_ac_ctrl_addts(struct wpa_supplicant *wpa_s, char *cmd) { char *token, *context = NULL; struct wmm_ac_ts_setup_params params = { .tsid = 0xff, .direction = 0xff, }; while ((token = str_token(cmd, " ", &context))) { if (sscanf(token, "tsid=%i", ¶ms.tsid) == 1 || sscanf(token, "up=%i", ¶ms.user_priority) == 1 || sscanf(token, "nominal_msdu_size=%i", ¶ms.nominal_msdu_size) == 1 || sscanf(token, "mean_data_rate=%i", ¶ms.mean_data_rate) == 1 || sscanf(token, "min_phy_rate=%i", ¶ms.minimum_phy_rate) == 1 || sscanf(token, "sba=%i", ¶ms.surplus_bandwidth_allowance) == 1) continue; if (os_strcasecmp(token, "downlink") == 0) { params.direction = WMM_TSPEC_DIRECTION_DOWNLINK; } else if (os_strcasecmp(token, "uplink") == 0) { params.direction = WMM_TSPEC_DIRECTION_UPLINK; } else if (os_strcasecmp(token, "bidi") == 0) { params.direction = WMM_TSPEC_DIRECTION_BI_DIRECTIONAL; } else if (os_strcasecmp(token, "fixed_nominal_msdu") == 0) { params.fixed_nominal_msdu = 1; } else { wpa_printf(MSG_DEBUG, "CTRL: Invalid WMM_AC_ADDTS parameter: '%s'", token); return -1; } } return wpas_wmm_ac_addts(wpa_s, ¶ms); } static int wmm_ac_ctrl_delts(struct wpa_supplicant *wpa_s, char *cmd) { u8 tsid = atoi(cmd); return wpas_wmm_ac_delts(wpa_s, tsid); } #ifdef CONFIG_IEEE80211R static int wpa_supplicant_ctrl_iface_ft_ds( struct wpa_supplicant *wpa_s, char *addr) { u8 target_ap[ETH_ALEN]; struct wpa_bss *bss; const u8 *mdie; if (hwaddr_aton(addr, target_ap)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS " MACSTR, MAC2STR(target_ap)); bss = wpa_bss_get_bssid(wpa_s, target_ap); if (bss) mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); else mdie = NULL; return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie); } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_WPS static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, char *cmd) { u8 bssid[ETH_ALEN], *_bssid = bssid; #ifdef CONFIG_P2P u8 p2p_dev_addr[ETH_ALEN]; #endif /* CONFIG_P2P */ #ifdef CONFIG_AP u8 *_p2p_dev_addr = NULL; #endif /* CONFIG_AP */ if (cmd == NULL || os_strcmp(cmd, "any") == 0) { _bssid = NULL; #ifdef CONFIG_P2P } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { if (hwaddr_aton(cmd + 13, p2p_dev_addr)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid " "P2P Device Address '%s'", cmd + 13); return -1; } _p2p_dev_addr = p2p_dev_addr; #endif /* CONFIG_P2P */ } else if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'", cmd); return -1; } #ifdef CONFIG_AP if (wpa_s->ap_iface) return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr); #endif /* CONFIG_AP */ return wpas_wps_start_pbc(wpa_s, _bssid, 0); } static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u8 bssid[ETH_ALEN], *_bssid = bssid; char *pin; int ret; pin = os_strchr(cmd, ' '); if (pin) *pin++ = '\0'; if (os_strcmp(cmd, "any") == 0) _bssid = NULL; else if (os_strcmp(cmd, "get") == 0) { ret = wps_generate_pin(); goto done; } else if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'", cmd); return -1; } #ifdef CONFIG_AP if (wpa_s->ap_iface) { int timeout = 0; char *pos; if (pin) { pos = os_strchr(pin, ' '); if (pos) { *pos++ = '\0'; timeout = atoi(pos); } } return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin, buf, buflen, timeout); } #endif /* CONFIG_AP */ if (pin) { ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0, DEV_PW_DEFAULT); if (ret < 0) return -1; ret = os_snprintf(buf, buflen, "%s", pin); if (os_snprintf_error(buflen, ret)) return -1; return ret; } ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, DEV_PW_DEFAULT); if (ret < 0) return -1; done: /* Return the generated PIN */ ret = os_snprintf(buf, buflen, "%08d", ret); if (os_snprintf_error(buflen, ret)) return -1; return ret; } static int wpa_supplicant_ctrl_iface_wps_check_pin( struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { char pin[9]; size_t len; char *pos; int ret; wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN", (u8 *) cmd, os_strlen(cmd)); for (pos = cmd, len = 0; *pos != '\0'; pos++) { if (*pos < '0' || *pos > '9') continue; pin[len++] = *pos; if (len == 9) { wpa_printf(MSG_DEBUG, "WPS: Too long PIN"); return -1; } } if (len != 4 && len != 8) { wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len); return -1; } pin[len] = '\0'; if (len == 8) { unsigned int pin_val; pin_val = atoi(pin); if (!wps_pin_valid(pin_val)) { wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit"); ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n"); if (os_snprintf_error(buflen, ret)) return -1; return ret; } } ret = os_snprintf(buf, buflen, "%s", pin); if (os_snprintf_error(buflen, ret)) return -1; return ret; } #ifdef CONFIG_WPS_NFC static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s, char *cmd) { u8 bssid[ETH_ALEN], *_bssid = bssid; if (cmd == NULL || cmd[0] == '\0') _bssid = NULL; else if (hwaddr_aton(cmd, bssid)) return -1; return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL, 0, 0); } static int wpa_supplicant_ctrl_iface_wps_nfc_config_token( struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { int ndef; struct wpabuf *buf; int res; char *pos; pos = os_strchr(cmd, ' '); if (pos) *pos++ = '\0'; if (os_strcmp(cmd, "WPS") == 0) ndef = 0; else if (os_strcmp(cmd, "NDEF") == 0) ndef = 1; else return -1; buf = wpas_wps_nfc_config_token(wpa_s, ndef, pos); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } static int wpa_supplicant_ctrl_iface_wps_nfc_token( struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { int ndef; struct wpabuf *buf; int res; if (os_strcmp(cmd, "WPS") == 0) ndef = 0; else if (os_strcmp(cmd, "NDEF") == 0) ndef = 1; else return -1; buf = wpas_wps_nfc_token(wpa_s, ndef); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read( struct wpa_supplicant *wpa_s, char *pos) { size_t len; struct wpabuf *buf; int ret; char *freq; int forced_freq = 0; freq = strstr(pos, " freq="); if (freq) { *freq = '\0'; freq += 6; forced_freq = atoi(freq); } len = os_strlen(pos); if (len & 0x01) return -1; len /= 2; buf = wpabuf_alloc(len); if (buf == NULL) return -1; if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) { wpabuf_free(buf); return -1; } ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq); wpabuf_free(buf); return ret; } static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s, char *reply, size_t max_len, int ndef) { struct wpabuf *buf; int res; buf = wpas_wps_nfc_handover_req(wpa_s, ndef); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } #ifdef CONFIG_P2P static int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s, char *reply, size_t max_len, int ndef) { struct wpabuf *buf; int res; buf = wpas_p2p_nfc_handover_req(wpa_s, ndef); if (buf == NULL) { wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request"); return -1; } res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } #endif /* CONFIG_P2P */ static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { char *pos; int ndef; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (os_strcmp(cmd, "WPS") == 0) ndef = 0; else if (os_strcmp(cmd, "NDEF") == 0) ndef = 1; else return -1; if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) { if (!ndef) return -1; return wpas_ctrl_nfc_get_handover_req_wps( wpa_s, reply, max_len, ndef); } #ifdef CONFIG_P2P if (os_strcmp(pos, "P2P-CR") == 0) { return wpas_ctrl_nfc_get_handover_req_p2p( wpa_s, reply, max_len, ndef); } #endif /* CONFIG_P2P */ return -1; } static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s, char *reply, size_t max_len, int ndef, int cr, char *uuid) { struct wpabuf *buf; int res; buf = wpas_wps_nfc_handover_sel(wpa_s, ndef, cr, uuid); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } #ifdef CONFIG_P2P static int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s, char *reply, size_t max_len, int ndef, int tag) { struct wpabuf *buf; int res; buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } #endif /* CONFIG_P2P */ static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { char *pos, *pos2; int ndef; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (os_strcmp(cmd, "WPS") == 0) ndef = 0; else if (os_strcmp(cmd, "NDEF") == 0) ndef = 1; else return -1; pos2 = os_strchr(pos, ' '); if (pos2) *pos2++ = '\0'; if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) { if (!ndef) return -1; return wpas_ctrl_nfc_get_handover_sel_wps( wpa_s, reply, max_len, ndef, os_strcmp(pos, "WPS-CR") == 0, pos2); } #ifdef CONFIG_P2P if (os_strcmp(pos, "P2P-CR") == 0) { return wpas_ctrl_nfc_get_handover_sel_p2p( wpa_s, reply, max_len, ndef, 0); } if (os_strcmp(pos, "P2P-CR-TAG") == 0) { return wpas_ctrl_nfc_get_handover_sel_p2p( wpa_s, reply, max_len, ndef, 1); } #endif /* CONFIG_P2P */ return -1; } static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s, char *cmd) { size_t len; struct wpabuf *req, *sel; int ret; char *pos, *role, *type, *pos2; #ifdef CONFIG_P2P char *freq; int forced_freq = 0; freq = strstr(cmd, " freq="); if (freq) { *freq = '\0'; freq += 6; forced_freq = atoi(freq); } #endif /* CONFIG_P2P */ role = cmd; pos = os_strchr(role, ' '); if (pos == NULL) { wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report"); return -1; } *pos++ = '\0'; type = pos; pos = os_strchr(type, ' '); if (pos == NULL) { wpa_printf(MSG_DEBUG, "NFC: Missing request message in handover report"); return -1; } *pos++ = '\0'; pos2 = os_strchr(pos, ' '); if (pos2 == NULL) { wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report"); return -1; } *pos2++ = '\0'; len = os_strlen(pos); if (len & 0x01) { wpa_printf(MSG_DEBUG, "NFC: Invalid request message length in handover report"); return -1; } len /= 2; req = wpabuf_alloc(len); if (req == NULL) { wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for request message"); return -1; } if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) { wpa_printf(MSG_DEBUG, "NFC: Invalid request message hexdump in handover report"); wpabuf_free(req); return -1; } len = os_strlen(pos2); if (len & 0x01) { wpa_printf(MSG_DEBUG, "NFC: Invalid select message length in handover report"); wpabuf_free(req); return -1; } len /= 2; sel = wpabuf_alloc(len); if (sel == NULL) { wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for select message"); wpabuf_free(req); return -1; } if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) { wpa_printf(MSG_DEBUG, "NFC: Invalid select message hexdump in handover report"); wpabuf_free(req); wpabuf_free(sel); return -1; } wpa_printf(MSG_DEBUG, "NFC: Connection handover reported - role=%s type=%s req_len=%d sel_len=%d", role, type, (int) wpabuf_len(req), (int) wpabuf_len(sel)); if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) { ret = wpas_wps_nfc_report_handover(wpa_s, req, sel); #ifdef CONFIG_AP } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) { ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel); if (ret < 0) ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel); #endif /* CONFIG_AP */ #ifdef CONFIG_P2P } else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0) { ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel, 0); } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0) { ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel, forced_freq); #endif /* CONFIG_P2P */ } else { wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover " "reported: role=%s type=%s", role, type); ret = -1; } wpabuf_free(req); wpabuf_free(sel); if (ret) wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages"); return ret; } #endif /* CONFIG_WPS_NFC */ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, char *cmd) { u8 bssid[ETH_ALEN]; char *pin; char *new_ssid; char *new_auth; char *new_encr; char *new_key; struct wps_new_ap_settings ap; pin = os_strchr(cmd, ' '); if (pin == NULL) return -1; *pin++ = '\0'; if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'", cmd); return -1; } new_ssid = os_strchr(pin, ' '); if (new_ssid == NULL) return wpas_wps_start_reg(wpa_s, bssid, pin, NULL); *new_ssid++ = '\0'; new_auth = os_strchr(new_ssid, ' '); if (new_auth == NULL) return -1; *new_auth++ = '\0'; new_encr = os_strchr(new_auth, ' '); if (new_encr == NULL) return -1; *new_encr++ = '\0'; new_key = os_strchr(new_encr, ' '); if (new_key == NULL) return -1; *new_key++ = '\0'; os_memset(&ap, 0, sizeof(ap)); ap.ssid_hex = new_ssid; ap.auth = new_auth; ap.encr = new_encr; ap.key_hex = new_key; return wpas_wps_start_reg(wpa_s, bssid, pin, &ap); } #ifdef CONFIG_AP static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int timeout = 300; char *pos; const char *pin_txt; if (!wpa_s->ap_iface) return -1; pos = os_strchr(cmd, ' '); if (pos) *pos++ = '\0'; if (os_strcmp(cmd, "disable") == 0) { wpas_wps_ap_pin_disable(wpa_s); return os_snprintf(buf, buflen, "OK\n"); } if (os_strcmp(cmd, "random") == 0) { if (pos) timeout = atoi(pos); pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout); if (pin_txt == NULL) return -1; return os_snprintf(buf, buflen, "%s", pin_txt); } if (os_strcmp(cmd, "get") == 0) { pin_txt = wpas_wps_ap_pin_get(wpa_s); if (pin_txt == NULL) return -1; return os_snprintf(buf, buflen, "%s", pin_txt); } if (os_strcmp(cmd, "set") == 0) { char *pin; if (pos == NULL) return -1; pin = pos; pos = os_strchr(pos, ' '); if (pos) { *pos++ = '\0'; timeout = atoi(pos); } if (os_strlen(pin) > buflen) return -1; if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0) return -1; return os_snprintf(buf, buflen, "%s", pin); } return -1; } #endif /* CONFIG_AP */ #ifdef CONFIG_WPS_ER static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s, char *cmd) { char *uuid = cmd, *pin, *pos; u8 addr_buf[ETH_ALEN], *addr = NULL; pin = os_strchr(uuid, ' '); if (pin == NULL) return -1; *pin++ = '\0'; pos = os_strchr(pin, ' '); if (pos) { *pos++ = '\0'; if (hwaddr_aton(pos, addr_buf) == 0) addr = addr_buf; } return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin); } static int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s, char *cmd) { char *uuid = cmd, *pin; pin = os_strchr(uuid, ' '); if (pin == NULL) return -1; *pin++ = '\0'; return wpas_wps_er_learn(wpa_s, uuid, pin); } static int wpa_supplicant_ctrl_iface_wps_er_set_config( struct wpa_supplicant *wpa_s, char *cmd) { char *uuid = cmd, *id; id = os_strchr(uuid, ' '); if (id == NULL) return -1; *id++ = '\0'; return wpas_wps_er_set_config(wpa_s, uuid, atoi(id)); } static int wpa_supplicant_ctrl_iface_wps_er_config( struct wpa_supplicant *wpa_s, char *cmd) { char *pin; char *new_ssid; char *new_auth; char *new_encr; char *new_key; struct wps_new_ap_settings ap; pin = os_strchr(cmd, ' '); if (pin == NULL) return -1; *pin++ = '\0'; new_ssid = os_strchr(pin, ' '); if (new_ssid == NULL) return -1; *new_ssid++ = '\0'; new_auth = os_strchr(new_ssid, ' '); if (new_auth == NULL) return -1; *new_auth++ = '\0'; new_encr = os_strchr(new_auth, ' '); if (new_encr == NULL) return -1; *new_encr++ = '\0'; new_key = os_strchr(new_encr, ' '); if (new_key == NULL) return -1; *new_key++ = '\0'; os_memset(&ap, 0, sizeof(ap)); ap.ssid_hex = new_ssid; ap.auth = new_auth; ap.encr = new_encr; ap.key_hex = new_key; return wpas_wps_er_config(wpa_s, cmd, pin, &ap); } #ifdef CONFIG_WPS_NFC static int wpa_supplicant_ctrl_iface_wps_er_nfc_config_token( struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { int ndef; struct wpabuf *buf; int res; char *uuid; uuid = os_strchr(cmd, ' '); if (uuid == NULL) return -1; *uuid++ = '\0'; if (os_strcmp(cmd, "WPS") == 0) ndef = 0; else if (os_strcmp(cmd, "NDEF") == 0) ndef = 1; else return -1; buf = wpas_wps_er_nfc_config_token(wpa_s, ndef, uuid); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } #endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_ER */ #endif /* CONFIG_WPS */ #ifdef CONFIG_IBSS_RSN static int wpa_supplicant_ctrl_iface_ibss_rsn( struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN " MACSTR, MAC2STR(peer)); return ibss_rsn_start(wpa_s->ibss_rsn, peer); } #endif /* CONFIG_IBSS_RSN */ static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, char *rsp) { #ifdef IEEE8021X_EAPOL char *pos, *id_pos; int id; struct wpa_ssid *ssid; pos = os_strchr(rsp, '-'); if (pos == NULL) return -1; *pos++ = '\0'; id_pos = pos; pos = os_strchr(pos, ':'); if (pos == NULL) return -1; *pos++ = '\0'; id = atoi(id_pos); wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id); wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", (u8 *) pos, os_strlen(pos)); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " "to update", id); return -1; } return wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, rsp, pos); #else /* IEEE8021X_EAPOL */ wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included"); return -1; #endif /* IEEE8021X_EAPOL */ } static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, const char *params, char *buf, size_t buflen) { char *pos, *end, tmp[30]; int res, verbose, wps, ret; #ifdef CONFIG_HS20 const u8 *hs20; #endif /* CONFIG_HS20 */ const u8 *sess_id; size_t sess_id_len; if (os_strcmp(params, "-DRIVER") == 0) return wpa_drv_status(wpa_s, buf, buflen); verbose = os_strcmp(params, "-VERBOSE") == 0; wps = os_strcmp(params, "-WPS") == 0; pos = buf; end = buf + buflen; if (wpa_s->wpa_state >= WPA_ASSOCIATED) { struct wpa_ssid *ssid = wpa_s->current_ssid; ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", MAC2STR(wpa_s->bssid)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, "freq=%u\n", wpa_s->assoc_freq); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (ssid) { u8 *_ssid = ssid->ssid; size_t ssid_len = ssid->ssid_len; u8 ssid_buf[SSID_MAX_LEN]; if (ssid_len == 0) { int _res = wpa_drv_get_ssid(wpa_s, ssid_buf); if (_res < 0) ssid_len = 0; else ssid_len = _res; _ssid = ssid_buf; } ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n", wpa_ssid_txt(_ssid, ssid_len), ssid->id); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (wps && ssid->passphrase && wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO)) { ret = os_snprintf(pos, end - pos, "passphrase=%s\n", ssid->passphrase); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (ssid->id_str) { ret = os_snprintf(pos, end - pos, "id_str=%s\n", ssid->id_str); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } switch (ssid->mode) { case WPAS_MODE_INFRA: ret = os_snprintf(pos, end - pos, "mode=station\n"); break; case WPAS_MODE_IBSS: ret = os_snprintf(pos, end - pos, "mode=IBSS\n"); break; case WPAS_MODE_AP: ret = os_snprintf(pos, end - pos, "mode=AP\n"); break; case WPAS_MODE_P2P_GO: ret = os_snprintf(pos, end - pos, "mode=P2P GO\n"); break; case WPAS_MODE_P2P_GROUP_FORMATION: ret = os_snprintf(pos, end - pos, "mode=P2P GO - group " "formation\n"); break; default: ret = 0; break; } if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_AP if (wpa_s->ap_iface) { pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos, end - pos, verbose); } else #endif /* CONFIG_AP */ pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose); } #ifdef CONFIG_SAE if (wpa_s->wpa_state >= WPA_ASSOCIATED && #ifdef CONFIG_AP !wpa_s->ap_iface && #endif /* CONFIG_AP */ wpa_s->sme.sae.state == SAE_ACCEPTED) { ret = os_snprintf(pos, end - pos, "sae_group=%d\n", wpa_s->sme.sae.group); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SAE */ ret = os_snprintf(pos, end - pos, "wpa_state=%s\n", wpa_supplicant_state_txt(wpa_s->wpa_state)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (wpa_s->l2 && l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) { ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_P2P if (wpa_s->global->p2p) { ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR "\n", MAC2STR(wpa_s->global->p2p_dev_addr)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_P2P */ ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n", MAC2STR(wpa_s->own_addr)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; #ifdef CONFIG_HS20 if (wpa_s->current_bss && (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE)) && wpa_s->wpa_proto == WPA_PROTO_RSN && wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { int release = 1; if (hs20[1] >= 5) { u8 rel_num = (hs20[6] & 0xf0) >> 4; release = rel_num + 1; } ret = os_snprintf(pos, end - pos, "hs20=%d\n", release); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (wpa_s->current_ssid) { struct wpa_cred *cred; char *type; for (cred = wpa_s->conf->cred; cred; cred = cred->next) { size_t i; if (wpa_s->current_ssid->parent_cred != cred) continue; if (cred->provisioning_sp) { ret = os_snprintf(pos, end - pos, "provisioning_sp=%s\n", cred->provisioning_sp); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (!cred->domain) goto no_domain; i = 0; if (wpa_s->current_bss && wpa_s->current_bss->anqp) { struct wpabuf *names = wpa_s->current_bss->anqp->domain_name; for (i = 0; names && i < cred->num_domain; i++) { if (domain_name_list_contains( names, cred->domain[i], 1)) break; } if (i == cred->num_domain) i = 0; /* show first entry by default */ } ret = os_snprintf(pos, end - pos, "home_sp=%s\n", cred->domain[i]); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; no_domain: if (wpa_s->current_bss == NULL || wpa_s->current_bss->anqp == NULL) res = -1; else res = interworking_home_sp_cred( wpa_s, cred, wpa_s->current_bss->anqp->domain_name); if (res > 0) type = "home"; else if (res == 0) type = "roaming"; else type = "unknown"; ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; break; } } #endif /* CONFIG_HS20 */ if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, verbose); if (res >= 0) pos += res; } sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len); if (sess_id) { char *start = pos; ret = os_snprintf(pos, end - pos, "eap_session_id="); if (os_snprintf_error(end - pos, ret)) return start - buf; pos += ret; ret = wpa_snprintf_hex(pos, end - pos, sess_id, sess_id_len); if (ret <= 0) return start - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return start - buf; pos += ret; } res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose); if (res >= 0) pos += res; #ifdef CONFIG_WPS { char uuid_str[100]; uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str)); ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_WPS */ #ifdef ANDROID /* * Allow using the STATUS command with default behavior, say for debug, * i.e., don't generate a "fake" CONNECTION and SUPPLICANT_STATE_CHANGE * events with STATUS-NO_EVENTS. */ if (os_strcmp(params, "-NO_EVENTS")) { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE "id=%d state=%d BSSID=" MACSTR " SSID=%s", wpa_s->current_ssid ? wpa_s->current_ssid->id : -1, wpa_s->wpa_state, MAC2STR(wpa_s->bssid), wpa_s->current_ssid && wpa_s->current_ssid->ssid ? wpa_ssid_txt(wpa_s->current_ssid->ssid, wpa_s->current_ssid->ssid_len) : ""); if (wpa_s->wpa_state == WPA_COMPLETED) { struct wpa_ssid *ssid = wpa_s->current_ssid; wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- connection to " MACSTR " completed %s [id=%d id_str=%s]", MAC2STR(wpa_s->bssid), "(auth)", ssid ? ssid->id : -1, ssid && ssid->id_str ? ssid->id_str : ""); } } #endif /* ANDROID */ return pos - buf; } static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; int id; struct wpa_ssid *ssid; u8 bssid[ETH_ALEN]; /* cmd: " " */ pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: id=%d bssid='%s'", id, pos); if (hwaddr_aton(pos, bssid)) { wpa_printf(MSG_DEBUG ,"CTRL_IFACE: invalid BSSID '%s'", pos); return -1; } ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " "to update", id); return -1; } os_memcpy(ssid->bssid, bssid, ETH_ALEN); ssid->bssid_set = !is_zero_ether_addr(bssid); return 0; } static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u8 bssid[ETH_ALEN]; struct wpa_blacklist *e; char *pos, *end; int ret; /* cmd: "BLACKLIST []" */ if (*cmd == '\0') { pos = buf; end = buf + buflen; e = wpa_s->blacklist; while (e) { ret = os_snprintf(pos, end - pos, MACSTR "\n", MAC2STR(e->bssid)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; e = e->next; } return pos - buf; } cmd++; if (os_strncmp(cmd, "clear", 5) == 0) { wpa_blacklist_clear(wpa_s); os_memcpy(buf, "OK\n", 3); return 3; } wpa_printf(MSG_DEBUG, "CTRL_IFACE: BLACKLIST bssid='%s'", cmd); if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: invalid BSSID '%s'", cmd); return -1; } /* * Add the BSSID twice, so its count will be 2, causing it to be * skipped when processing scan results. */ ret = wpa_blacklist_add(wpa_s, bssid); if (ret < 0) return -1; ret = wpa_blacklist_add(wpa_s, bssid); if (ret < 0) return -1; os_memcpy(buf, "OK\n", 3); return 3; } static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { char *pos, *end, *stamp; int ret; /* cmd: "LOG_LEVEL []" */ if (*cmd == '\0') { pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, "Current level: %s\n" "Timestamp: %d\n", debug_level_str(wpa_debug_level), wpa_debug_timestamp); if (os_snprintf_error(end - pos, ret)) ret = 0; return ret; } while (*cmd == ' ') cmd++; stamp = os_strchr(cmd, ' '); if (stamp) { *stamp++ = '\0'; while (*stamp == ' ') { stamp++; } } if (os_strlen(cmd)) { int level = str_to_debug_level(cmd); if (level < 0) return -1; wpa_debug_level = level; } if (stamp && os_strlen(stamp)) wpa_debug_timestamp = atoi(stamp); os_memcpy(buf, "OK\n", 3); return 3; } static int wpa_supplicant_ctrl_iface_list_networks( struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { char *pos, *end, *prev; struct wpa_ssid *ssid; int ret; pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, "network id / ssid / bssid / flags\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; ssid = wpa_s->conf->ssid; /* skip over ssids until we find next one */ if (cmd != NULL && os_strncmp(cmd, "LAST_ID=", 8) == 0) { int last_id = atoi(cmd + 8); if (last_id != -1) { while (ssid != NULL && ssid->id <= last_id) { ssid = ssid->next; } } } while (ssid) { prev = pos; ret = os_snprintf(pos, end - pos, "%d\t%s", ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; if (ssid->bssid_set) { ret = os_snprintf(pos, end - pos, "\t" MACSTR, MAC2STR(ssid->bssid)); } else { ret = os_snprintf(pos, end - pos, "\tany"); } if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\t%s%s%s%s", ssid == wpa_s->current_ssid ? "[CURRENT]" : "", ssid->disabled ? "[DISABLED]" : "", ssid->disabled_until.sec ? "[TEMP-DISABLED]" : "", ssid->disabled == 2 ? "[P2P-PERSISTENT]" : ""); if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; ssid = ssid->next; } return pos - buf; } static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher) { int ret; ret = os_snprintf(pos, end - pos, "-"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; ret = wpa_write_ciphers(pos, end, cipher, "+"); if (ret < 0) return pos; pos += ret; return pos; } static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, const u8 *ie, size_t ie_len) { struct wpa_ie_data data; char *start; int ret; ret = os_snprintf(pos, end - pos, "[%s-", proto); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) { ret = os_snprintf(pos, end - pos, "?]"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; return pos; } start = pos; if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "%sEAP", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_PSK) { ret = os_snprintf(pos, end - pos, "%sPSK", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) { ret = os_snprintf(pos, end - pos, "%sNone", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_SAE) { ret = os_snprintf(pos, end - pos, "%sSAE", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #ifdef CONFIG_IEEE80211R if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "%sFT/EAP", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) { ret = os_snprintf(pos, end - pos, "%sFT/PSK", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) { ret = os_snprintf(pos, end - pos, "%sFT/SAE", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { ret = os_snprintf(pos, end - pos, "%sEAP-SHA256", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { ret = os_snprintf(pos, end - pos, "%sPSK-SHA256", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_SUITEB if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #endif /* CONFIG_SUITEB */ #ifdef CONFIG_SUITEB192 if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #endif /* CONFIG_SUITEB192 */ if (data.key_mgmt & WPA_KEY_MGMT_OSEN) { ret = os_snprintf(pos, end - pos, "%sOSEN", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher); if (data.capabilities & WPA_CAPABILITY_PREAUTH) { ret = os_snprintf(pos, end - pos, "-preauth"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } ret = os_snprintf(pos, end - pos, "]"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; return pos; } #ifdef CONFIG_WPS static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s, char *pos, char *end, struct wpabuf *wps_ie) { int ret; const char *txt; if (wps_ie == NULL) return pos; if (wps_is_selected_pbc_registrar(wps_ie)) txt = "[WPS-PBC]"; else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0)) txt = "[WPS-AUTH]"; else if (wps_is_selected_pin_registrar(wps_ie)) txt = "[WPS-PIN]"; else txt = "[WPS]"; ret = os_snprintf(pos, end - pos, "%s", txt); if (!os_snprintf_error(end - pos, ret)) pos += ret; wpabuf_free(wps_ie); return pos; } #endif /* CONFIG_WPS */ static char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s, char *pos, char *end, const struct wpa_bss *bss) { #ifdef CONFIG_WPS struct wpabuf *wps_ie; wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie); #else /* CONFIG_WPS */ return pos; #endif /* CONFIG_WPS */ } /* Format one result on one text line into a buffer. */ static int wpa_supplicant_ctrl_iface_scan_result( struct wpa_supplicant *wpa_s, const struct wpa_bss *bss, char *buf, size_t buflen) { char *pos, *end; int ret; const u8 *ie, *ie2, *osen_ie, *p2p, *mesh; mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID); p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); if (!p2p) p2p = wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE); if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN && os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) return 0; /* Do not show P2P listen discovery results here */ pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t", MAC2STR(bss->bssid), bss->freq, bss->level); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie) pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (ie2) { pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2", ie2, 2 + ie2[1]); } osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); if (osen_ie) pos = wpa_supplicant_ie_txt(pos, end, "OSEN", osen_ie, 2 + osen_ie[1]); pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (mesh) { ret = os_snprintf(pos, end - pos, "[MESH]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (bss_is_dmg(bss)) { const char *s; ret = os_snprintf(pos, end - pos, "[DMG]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; switch (bss->caps & IEEE80211_CAP_DMG_MASK) { case IEEE80211_CAP_DMG_IBSS: s = "[IBSS]"; break; case IEEE80211_CAP_DMG_AP: s = "[ESS]"; break; case IEEE80211_CAP_DMG_PBSS: s = "[PBSS]"; break; default: s = ""; break; } ret = os_snprintf(pos, end - pos, "%s", s); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } else { if (bss->caps & IEEE80211_CAP_IBSS) { ret = os_snprintf(pos, end - pos, "[IBSS]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (bss->caps & IEEE80211_CAP_ESS) { ret = os_snprintf(pos, end - pos, "[ESS]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } } if (p2p) { ret = os_snprintf(pos, end - pos, "[P2P]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } #ifdef CONFIG_HS20 if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) { ret = os_snprintf(pos, end - pos, "[HS20]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } #endif /* CONFIG_HS20 */ #ifdef CONFIG_FST if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) { ret = os_snprintf(pos, end - pos, "[FST]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } #endif /* CONFIG_FST */ ret = os_snprintf(pos, end - pos, "\t%s", wpa_ssid_txt(bss->ssid, bss->ssid_len)); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; return pos - buf; } static int wpa_supplicant_ctrl_iface_scan_results( struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { char *pos, *end; struct wpa_bss *bss; int ret; pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / " "flags / ssid\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos, end - pos); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } return pos - buf; } #ifdef CONFIG_MESH static int wpa_supplicant_ctrl_iface_mesh_interface_add( struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { char *pos, ifname[IFNAMSIZ + 1]; ifname[0] = '\0'; pos = os_strstr(cmd, "ifname="); if (pos) { pos += 7; os_strlcpy(ifname, pos, sizeof(ifname)); } if (wpas_mesh_add_interface(wpa_s, ifname, sizeof(ifname)) < 0) return -1; os_strlcpy(reply, ifname, max_len); return os_strlen(ifname); } static int wpa_supplicant_ctrl_iface_mesh_group_add( struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_ssid *ssid; id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_ADD id=%d", id); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network id=%d", id); return -1; } if (ssid->mode != WPAS_MODE_MESH) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use MESH_GROUP_ADD on a non mesh network"); return -1; } if (ssid->key_mgmt != WPA_KEY_MGMT_NONE && ssid->key_mgmt != WPA_KEY_MGMT_SAE) { wpa_printf(MSG_ERROR, "CTRL_IFACE: key_mgmt for mesh network should be open or SAE"); return -1; } /* * TODO: If necessary write our own group_add function, * for now we can reuse select_network */ wpa_supplicant_select_network(wpa_s, ssid); return 0; } static int wpa_supplicant_ctrl_iface_mesh_group_remove( struct wpa_supplicant *wpa_s, char *cmd) { struct wpa_supplicant *orig; struct wpa_global *global; int found = 0; wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd); global = wpa_s->global; orig = wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_strcmp(wpa_s->ifname, cmd) == 0) { found = 1; break; } } if (!found) { wpa_printf(MSG_ERROR, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s not found", cmd); return -1; } if (wpa_s->mesh_if_created && wpa_s == orig) { wpa_printf(MSG_ERROR, "CTRL_IFACE: MESH_GROUP_REMOVE can't remove itself"); return -1; } wpa_s->reassociate = 0; wpa_s->disconnected = 1; wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); /* * TODO: If necessary write our own group_remove function, * for now we can reuse deauthenticate */ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); if (wpa_s->mesh_if_created) wpa_supplicant_remove_iface(global, wpa_s, 0); return 0; } #endif /* CONFIG_MESH */ static int wpa_supplicant_ctrl_iface_select_network( struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_ssid *ssid; char *pos; /* cmd: "" or "any" */ if (os_strncmp(cmd, "any", 3) == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any"); ssid = NULL; } else { id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK id=%d", id); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id); return -1; } if (ssid->disabled == 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " "SELECT_NETWORK with persistent P2P group"); return -1; } } pos = os_strstr(cmd, " freq="); if (pos) { int *freqs = freq_range_to_channel_list(wpa_s, pos + 6); if (freqs) { wpa_s->scan_req = MANUAL_SCAN_REQ; os_free(wpa_s->manual_scan_freqs); wpa_s->manual_scan_freqs = freqs; } } wpa_s->scan_min_time.sec = 0; wpa_s->scan_min_time.usec = 0; wpa_supplicant_select_network(wpa_s, ssid); return 0; } static int wpa_supplicant_ctrl_iface_enable_network( struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_ssid *ssid; /* cmd: "" or "all" */ if (os_strcmp(cmd, "all") == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK all"); ssid = NULL; } else { id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK id=%d", id); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id); return -1; } if (ssid->disabled == 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " "ENABLE_NETWORK with persistent P2P group"); return -1; } if (os_strstr(cmd, " no-connect")) { ssid->disabled = 0; return 0; } } wpa_s->scan_min_time.sec = 0; wpa_s->scan_min_time.usec = 0; wpa_supplicant_enable_network(wpa_s, ssid); return 0; } static int wpa_supplicant_ctrl_iface_disable_network( struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_ssid *ssid; /* cmd: "" or "all" */ if (os_strcmp(cmd, "all") == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK all"); ssid = NULL; } else { id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK id=%d", id); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id); return -1; } if (ssid->disabled == 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " "DISABLE_NETWORK with persistent P2P " "group"); return -1; } } wpa_supplicant_disable_network(wpa_s, ssid); return 0; } static int wpa_supplicant_ctrl_iface_add_network( struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct wpa_ssid *ssid; int ret; wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK"); ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) return -1; wpas_notify_network_added(wpa_s, ssid); ssid->disabled = 1; wpa_config_set_network_defaults(ssid); ret = os_snprintf(buf, buflen, "%d\n", ssid->id); if (os_snprintf_error(buflen, ret)) return -1; return ret; } static int wpa_supplicant_ctrl_iface_remove_network( struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_ssid *ssid; int was_disabled; /* cmd: "" or "all" */ if (os_strcmp(cmd, "all") == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all"); if (wpa_s->sched_scanning) wpa_supplicant_cancel_sched_scan(wpa_s); eapol_sm_invalidate_cached_session(wpa_s->eapol); if (wpa_s->current_ssid) { #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; #endif /* CONFIG_SME */ wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); if (wpa_s->wpa_state >= WPA_AUTHENTICATING) wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } ssid = wpa_s->conf->ssid; while (ssid) { struct wpa_ssid *remove_ssid = ssid; id = ssid->id; ssid = ssid->next; if (wpa_s->last_ssid == remove_ssid) wpa_s->last_ssid = NULL; wpas_notify_network_removed(wpa_s, remove_ssid); wpa_config_remove_network(wpa_s->conf, id); } return 0; } id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid) wpas_notify_network_removed(wpa_s, ssid); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; } if (wpa_s->last_ssid == ssid) wpa_s->last_ssid = NULL; if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) { #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; #endif /* CONFIG_SME */ /* * Invalidate the EAP session cache if the current or * previously used network is removed. */ eapol_sm_invalidate_cached_session(wpa_s->eapol); } if (ssid == wpa_s->current_ssid) { wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); if (wpa_s->wpa_state >= WPA_AUTHENTICATING) wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } was_disabled = ssid->disabled; if (wpa_config_remove_network(wpa_s->conf, id) < 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the " "network id=%d", id); return -1; } if (!was_disabled && wpa_s->sched_scanning) { wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove " "network from filters"); wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } return 0; } static int wpa_supplicant_ctrl_iface_update_network( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, char *name, char *value) { if (wpa_config_set(ssid, name, value, 0) < 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network " "variable '%s'", name); return -1; } if (os_strcmp(name, "bssid") != 0 && os_strcmp(name, "priority") != 0) wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) { /* * Invalidate the EAP session cache if anything in the current * or previously used configuration changes. */ eapol_sm_invalidate_cached_session(wpa_s->eapol); } if ((os_strcmp(name, "psk") == 0 && value[0] == '"' && ssid->ssid_len) || (os_strcmp(name, "ssid") == 0 && ssid->passphrase)) wpa_config_update_psk(ssid); else if (os_strcmp(name, "priority") == 0) wpa_config_update_prio_list(wpa_s->conf); return 0; } static int wpa_supplicant_ctrl_iface_set_network( struct wpa_supplicant *wpa_s, char *cmd) { int id, ret, prev_bssid_set, prev_disabled; struct wpa_ssid *ssid; char *name, *value; u8 prev_bssid[ETH_ALEN]; /* cmd: " " */ name = os_strchr(cmd, ' '); if (name == NULL) return -1; *name++ = '\0'; value = os_strchr(name, ' '); if (value == NULL) return -1; *value++ = '\0'; id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s'", id, name); wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", (u8 *) value, os_strlen(value)); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; } prev_bssid_set = ssid->bssid_set; prev_disabled = ssid->disabled; os_memcpy(prev_bssid, ssid->bssid, ETH_ALEN); ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name, value); if (ret == 0 && (ssid->bssid_set != prev_bssid_set || os_memcmp(ssid->bssid, prev_bssid, ETH_ALEN) != 0)) wpas_notify_network_bssid_set_changed(wpa_s, ssid); if (prev_disabled != ssid->disabled && (prev_disabled == 2 || ssid->disabled == 2)) wpas_notify_network_type_changed(wpa_s, ssid); return ret; } static int wpa_supplicant_ctrl_iface_get_network( struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int id; size_t res; struct wpa_ssid *ssid; char *name, *value; /* cmd: " " */ name = os_strchr(cmd, ' '); if (name == NULL || buflen == 0) return -1; *name++ = '\0'; id = atoi(cmd); wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'", id, name); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; } value = wpa_config_get_no_key(ssid, name); if (value == NULL) { wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network " "variable '%s'", name); return -1; } res = os_strlcpy(buf, value, buflen); if (res >= buflen) { os_free(value); return -1; } os_free(value); return res; } static int wpa_supplicant_ctrl_iface_dup_network( struct wpa_supplicant *wpa_s, char *cmd, struct wpa_supplicant *dst_wpa_s) { struct wpa_ssid *ssid_s, *ssid_d; char *name, *id, *value; int id_s, id_d, ret; /* cmd: " " */ id = os_strchr(cmd, ' '); if (id == NULL) return -1; *id++ = '\0'; name = os_strchr(id, ' '); if (name == NULL) return -1; *name++ = '\0'; id_s = atoi(cmd); id_d = atoi(id); wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK ifname=%s->%s id=%d->%d name='%s'", wpa_s->ifname, dst_wpa_s->ifname, id_s, id_d, name); ssid_s = wpa_config_get_network(wpa_s->conf, id_s); if (ssid_s == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id_s); return -1; } ssid_d = wpa_config_get_network(dst_wpa_s->conf, id_d); if (ssid_d == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id_d); return -1; } value = wpa_config_get(ssid_s, name); if (value == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network " "variable '%s'", name); return -1; } ret = wpa_supplicant_ctrl_iface_update_network(dst_wpa_s, ssid_d, name, value); os_free(value); return ret; } static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { char *pos, *end; struct wpa_cred *cred; int ret; pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, "cred id / realm / username / domain / imsi\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; cred = wpa_s->conf->cred; while (cred) { ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n", cred->id, cred->realm ? cred->realm : "", cred->username ? cred->username : "", cred->domain ? cred->domain[0] : "", cred->imsi ? cred->imsi : ""); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; cred = cred->next; } return pos - buf; } static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct wpa_cred *cred; int ret; wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_CRED"); cred = wpa_config_add_cred(wpa_s->conf); if (cred == NULL) return -1; wpa_msg(wpa_s, MSG_INFO, CRED_ADDED "%d", cred->id); ret = os_snprintf(buf, buflen, "%d\n", cred->id); if (os_snprintf_error(buflen, ret)) return -1; return ret; } static int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s, struct wpa_cred *cred) { struct wpa_ssid *ssid; char str[20]; int id; if (cred == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred"); return -1; } id = cred->id; if (wpa_config_remove_cred(wpa_s->conf, id) < 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred"); return -1; } wpa_msg(wpa_s, MSG_INFO, CRED_REMOVED "%d", id); /* Remove any network entry created based on the removed credential */ ssid = wpa_s->conf->ssid; while (ssid) { if (ssid->parent_cred == cred) { int res; wpa_printf(MSG_DEBUG, "Remove network id %d since it " "used the removed credential", ssid->id); res = os_snprintf(str, sizeof(str), "%d", ssid->id); if (os_snprintf_error(sizeof(str), res)) str[sizeof(str) - 1] = '\0'; ssid = ssid->next; wpa_supplicant_ctrl_iface_remove_network(wpa_s, str); } else ssid = ssid->next; } return 0; } static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_cred *cred, *prev; /* cmd: "", "all", "sp_fqdn=", or * "provisioning_sp= */ if (os_strcmp(cmd, "all") == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all"); cred = wpa_s->conf->cred; while (cred) { prev = cred; cred = cred->next; wpas_ctrl_remove_cred(wpa_s, prev); } return 0; } if (os_strncmp(cmd, "sp_fqdn=", 8) == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED SP FQDN '%s'", cmd + 8); cred = wpa_s->conf->cred; while (cred) { prev = cred; cred = cred->next; if (prev->domain) { size_t i; for (i = 0; i < prev->num_domain; i++) { if (os_strcmp(prev->domain[i], cmd + 8) != 0) continue; wpas_ctrl_remove_cred(wpa_s, prev); break; } } } return 0; } if (os_strncmp(cmd, "provisioning_sp=", 16) == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED provisioning SP FQDN '%s'", cmd + 16); cred = wpa_s->conf->cred; while (cred) { prev = cred; cred = cred->next; if (prev->provisioning_sp && os_strcmp(prev->provisioning_sp, cmd + 16) == 0) wpas_ctrl_remove_cred(wpa_s, prev); } return 0; } id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id); cred = wpa_config_get_cred(wpa_s->conf, id); return wpas_ctrl_remove_cred(wpa_s, cred); } static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_cred *cred; char *name, *value; /* cmd: " " */ name = os_strchr(cmd, ' '); if (name == NULL) return -1; *name++ = '\0'; value = os_strchr(name, ' '); if (value == NULL) return -1; *value++ = '\0'; id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_CRED id=%d name='%s'", id, name); wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", (u8 *) value, os_strlen(value)); cred = wpa_config_get_cred(wpa_s->conf, id); if (cred == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d", id); return -1; } if (wpa_config_set_cred(cred, name, value, 0) < 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set cred " "variable '%s'", name); return -1; } wpa_msg(wpa_s, MSG_INFO, CRED_MODIFIED "%d %s", cred->id, name); return 0; } static int wpa_supplicant_ctrl_iface_get_cred(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int id; size_t res; struct wpa_cred *cred; char *name, *value; /* cmd: " " */ name = os_strchr(cmd, ' '); if (name == NULL) return -1; *name++ = '\0'; id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CRED id=%d name='%s'", id, name); cred = wpa_config_get_cred(wpa_s->conf, id); if (cred == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d", id); return -1; } value = wpa_config_get_cred_no_key(cred, name); if (value == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get cred variable '%s'", name); return -1; } res = os_strlcpy(buf, value, buflen); if (res >= buflen) { os_free(value); return -1; } os_free(value); return res; } #ifndef CONFIG_NO_CONFIG_WRITE static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s) { int ret; if (!wpa_s->conf->update_config) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed " "to update configuration (update_config=0)"); return -1; } ret = wpa_config_write(wpa_s->confname, wpa_s->conf); if (ret) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to " "update configuration"); } else { wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration" " updated"); } return ret; } #endif /* CONFIG_NO_CONFIG_WRITE */ struct cipher_info { unsigned int capa; const char *name; int group_only; }; static const struct cipher_info ciphers[] = { { WPA_DRIVER_CAPA_ENC_CCMP_256, "CCMP-256", 0 }, { WPA_DRIVER_CAPA_ENC_GCMP_256, "GCMP-256", 0 }, { WPA_DRIVER_CAPA_ENC_CCMP, "CCMP", 0 }, { WPA_DRIVER_CAPA_ENC_GCMP, "GCMP", 0 }, { WPA_DRIVER_CAPA_ENC_TKIP, "TKIP", 0 }, { WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE, "NONE", 0 }, { WPA_DRIVER_CAPA_ENC_WEP104, "WEP104", 1 }, { WPA_DRIVER_CAPA_ENC_WEP40, "WEP40", 1 } }; static const struct cipher_info ciphers_group_mgmt[] = { { WPA_DRIVER_CAPA_ENC_BIP, "AES-128-CMAC", 1 }, { WPA_DRIVER_CAPA_ENC_BIP_GMAC_128, "BIP-GMAC-128", 1 }, { WPA_DRIVER_CAPA_ENC_BIP_GMAC_256, "BIP-GMAC-256", 1 }, { WPA_DRIVER_CAPA_ENC_BIP_CMAC_256, "BIP-CMAC-256", 1 }, }; static int ctrl_iface_get_capability_pairwise(int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; size_t len; unsigned int i; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; len = os_strlcpy(buf, "CCMP TKIP NONE", buflen); if (len >= buflen) return -1; return len; } for (i = 0; i < ARRAY_SIZE(ciphers); i++) { if (!ciphers[i].group_only && capa->enc & ciphers[i].capa) { ret = os_snprintf(pos, end - pos, "%s%s", pos == buf ? "" : " ", ciphers[i].name); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } } return pos - buf; } static int ctrl_iface_get_capability_group(int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; size_t len; unsigned int i; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; len = os_strlcpy(buf, "CCMP TKIP WEP104 WEP40", buflen); if (len >= buflen) return -1; return len; } for (i = 0; i < ARRAY_SIZE(ciphers); i++) { if (capa->enc & ciphers[i].capa) { ret = os_snprintf(pos, end - pos, "%s%s", pos == buf ? "" : " ", ciphers[i].name); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } } return pos - buf; } static int ctrl_iface_get_capability_group_mgmt(int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; unsigned int i; pos = buf; end = pos + buflen; if (res < 0) return 0; for (i = 0; i < ARRAY_SIZE(ciphers_group_mgmt); i++) { if (capa->enc & ciphers_group_mgmt[i].capa) { ret = os_snprintf(pos, end - pos, "%s%s", pos == buf ? "" : " ", ciphers_group_mgmt[i].name); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } } return pos - buf; } static int ctrl_iface_get_capability_key_mgmt(int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; size_t len; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; len = os_strlcpy(buf, "WPA-PSK WPA-EAP IEEE8021X WPA-NONE " "NONE", buflen); if (len >= buflen) return -1; return len; } ret = os_snprintf(pos, end - pos, "NONE IEEE8021X"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { ret = os_snprintf(pos, end - pos, " WPA-EAP"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { ret = os_snprintf(pos, end - pos, " WPA-PSK"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { ret = os_snprintf(pos, end - pos, " WPA-NONE"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_SUITEB if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) { ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SUITEB */ #ifdef CONFIG_SUITEB192 if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) { ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B-192"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SUITEB192 */ return pos - buf; } static int ctrl_iface_get_capability_proto(int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; size_t len; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; len = os_strlcpy(buf, "RSN WPA", buflen); if (len >= buflen) return -1; return len; } if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { ret = os_snprintf(pos, end - pos, "%sRSN", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { ret = os_snprintf(pos, end - pos, "%sWPA", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } return pos - buf; } static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s, int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; size_t len; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; len = os_strlcpy(buf, "OPEN SHARED LEAP", buflen); if (len >= buflen) return -1; return len; } if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) { ret = os_snprintf(pos, end - pos, "%sOPEN", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) { ret = os_snprintf(pos, end - pos, "%sSHARED", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) { ret = os_snprintf(pos, end - pos, "%sLEAP", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_SAE if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) { ret = os_snprintf(pos, end - pos, "%sSAE", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SAE */ return pos - buf; } static int ctrl_iface_get_capability_modes(int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; size_t len; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; len = os_strlcpy(buf, "IBSS AP", buflen); if (len >= buflen) return -1; return len; } if (capa->flags & WPA_DRIVER_FLAGS_IBSS) { ret = os_snprintf(pos, end - pos, "%sIBSS", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->flags & WPA_DRIVER_FLAGS_AP) { ret = os_snprintf(pos, end - pos, "%sAP", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_MESH if (capa->flags & WPA_DRIVER_FLAGS_MESH) { ret = os_snprintf(pos, end - pos, "%sMESH", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_MESH */ return pos - buf; } static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct hostapd_channel_data *chnl; int ret, i, j; char *pos, *end, *hmode; pos = buf; end = pos + buflen; for (j = 0; j < wpa_s->hw.num_modes; j++) { switch (wpa_s->hw.modes[j].mode) { case HOSTAPD_MODE_IEEE80211B: hmode = "B"; break; case HOSTAPD_MODE_IEEE80211G: hmode = "G"; break; case HOSTAPD_MODE_IEEE80211A: hmode = "A"; break; case HOSTAPD_MODE_IEEE80211AD: hmode = "AD"; break; default: continue; } ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; chnl = wpa_s->hw.modes[j].channels; for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) { if (chnl[i].flag & HOSTAPD_CHAN_DISABLED) continue; ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } return pos - buf; } static int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct hostapd_channel_data *chnl; int ret, i, j; char *pos, *end, *hmode; pos = buf; end = pos + buflen; for (j = 0; j < wpa_s->hw.num_modes; j++) { switch (wpa_s->hw.modes[j].mode) { case HOSTAPD_MODE_IEEE80211B: hmode = "B"; break; case HOSTAPD_MODE_IEEE80211G: hmode = "G"; break; case HOSTAPD_MODE_IEEE80211A: hmode = "A"; break; case HOSTAPD_MODE_IEEE80211AD: hmode = "AD"; break; default: continue; } ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n", hmode); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; chnl = wpa_s->hw.modes[j].channels; for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) { if (chnl[i].flag & HOSTAPD_CHAN_DISABLED) continue; ret = os_snprintf(pos, end - pos, " %d = %d MHz%s%s\n", chnl[i].chan, chnl[i].freq, chnl[i].flag & HOSTAPD_CHAN_NO_IR ? " (NO_IR)" : "", chnl[i].flag & HOSTAPD_CHAN_RADAR ? " (DFS)" : ""); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } return pos - buf; } static int wpa_supplicant_ctrl_iface_get_capability( struct wpa_supplicant *wpa_s, const char *_field, char *buf, size_t buflen) { struct wpa_driver_capa capa; int res; char *strict; char field[30]; size_t len; /* Determine whether or not strict checking was requested */ len = os_strlcpy(field, _field, sizeof(field)); if (len >= sizeof(field)) return -1; strict = os_strchr(field, ' '); if (strict != NULL) { *strict++ = '\0'; if (os_strcmp(strict, "strict") != 0) return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s' %s", field, strict ? strict : ""); if (os_strcmp(field, "eap") == 0) { return eap_get_names(buf, buflen); } res = wpa_drv_get_capa(wpa_s, &capa); if (os_strcmp(field, "pairwise") == 0) return ctrl_iface_get_capability_pairwise(res, strict, &capa, buf, buflen); if (os_strcmp(field, "group") == 0) return ctrl_iface_get_capability_group(res, strict, &capa, buf, buflen); if (os_strcmp(field, "group_mgmt") == 0) return ctrl_iface_get_capability_group_mgmt(res, strict, &capa, buf, buflen); if (os_strcmp(field, "key_mgmt") == 0) return ctrl_iface_get_capability_key_mgmt(res, strict, &capa, buf, buflen); if (os_strcmp(field, "proto") == 0) return ctrl_iface_get_capability_proto(res, strict, &capa, buf, buflen); if (os_strcmp(field, "auth_alg") == 0) return ctrl_iface_get_capability_auth_alg(wpa_s, res, strict, &capa, buf, buflen); if (os_strcmp(field, "modes") == 0) return ctrl_iface_get_capability_modes(res, strict, &capa, buf, buflen); if (os_strcmp(field, "channels") == 0) return ctrl_iface_get_capability_channels(wpa_s, buf, buflen); if (os_strcmp(field, "freq") == 0) return ctrl_iface_get_capability_freq(wpa_s, buf, buflen); #ifdef CONFIG_TDLS if (os_strcmp(field, "tdls") == 0) return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen); #endif /* CONFIG_TDLS */ #ifdef CONFIG_ERP if (os_strcmp(field, "erp") == 0) { res = os_snprintf(buf, buflen, "ERP"); if (os_snprintf_error(buflen, res)) return -1; return res; } #endif /* CONFIG_EPR */ #ifdef CONFIG_FIPS if (os_strcmp(field, "fips") == 0) { res = os_snprintf(buf, buflen, "FIPS"); if (os_snprintf_error(buflen, res)) return -1; return res; } #endif /* CONFIG_FIPS */ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); return -1; } #ifdef CONFIG_INTERWORKING static char * anqp_add_hex(char *pos, char *end, const char *title, struct wpabuf *data) { char *start = pos; size_t i; int ret; const u8 *d; if (data == NULL) return start; ret = os_snprintf(pos, end - pos, "%s=", title); if (os_snprintf_error(end - pos, ret)) return start; pos += ret; d = wpabuf_head_u8(data); for (i = 0; i < wpabuf_len(data); i++) { ret = os_snprintf(pos, end - pos, "%02x", *d++); if (os_snprintf_error(end - pos, ret)) return start; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return start; pos += ret; return pos; } #endif /* CONFIG_INTERWORKING */ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, unsigned long mask, char *buf, size_t buflen) { size_t i; int ret; char *pos, *end; const u8 *ie, *ie2, *osen_ie; pos = buf; end = buf + buflen; if (mask & WPA_BSS_MASK_ID) { ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_BSSID) { ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", MAC2STR(bss->bssid)); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_FREQ) { ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_BEACON_INT) { ret = os_snprintf(pos, end - pos, "beacon_int=%d\n", bss->beacon_int); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_CAPABILITIES) { ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n", bss->caps); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_QUAL) { ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_NOISE) { ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_LEVEL) { ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_TSF) { ret = os_snprintf(pos, end - pos, "tsf=%016llu\n", (unsigned long long) bss->tsf); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_AGE) { struct os_reltime now; os_get_reltime(&now); ret = os_snprintf(pos, end - pos, "age=%d\n", (int) (now.sec - bss->last_update.sec)); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_IE) { ret = os_snprintf(pos, end - pos, "ie="); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; ie = (const u8 *) (bss + 1); for (i = 0; i < bss->ie_len; i++) { ret = os_snprintf(pos, end - pos, "%02x", *ie++); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_FLAGS) { ret = os_snprintf(pos, end - pos, "flags="); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie) pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (ie2) pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); if (osen_ie) pos = wpa_supplicant_ie_txt(pos, end, "OSEN", osen_ie, 2 + osen_ie[1]); pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (bss_is_dmg(bss)) { const char *s; ret = os_snprintf(pos, end - pos, "[DMG]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; switch (bss->caps & IEEE80211_CAP_DMG_MASK) { case IEEE80211_CAP_DMG_IBSS: s = "[IBSS]"; break; case IEEE80211_CAP_DMG_AP: s = "[ESS]"; break; case IEEE80211_CAP_DMG_PBSS: s = "[PBSS]"; break; default: s = ""; break; } ret = os_snprintf(pos, end - pos, "%s", s); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } else { if (bss->caps & IEEE80211_CAP_IBSS) { ret = os_snprintf(pos, end - pos, "[IBSS]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (bss->caps & IEEE80211_CAP_ESS) { ret = os_snprintf(pos, end - pos, "[ESS]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } } if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { ret = os_snprintf(pos, end - pos, "[P2P]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #ifdef CONFIG_HS20 if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) { ret = os_snprintf(pos, end - pos, "[HS20]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #endif /* CONFIG_HS20 */ ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_SSID) { ret = os_snprintf(pos, end - pos, "ssid=%s\n", wpa_ssid_txt(bss->ssid, bss->ssid_len)); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #ifdef CONFIG_WPS if (mask & WPA_BSS_MASK_WPS_SCAN) { ie = (const u8 *) (bss + 1); ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end); if (ret >= end - pos) return 0; if (ret > 0) pos += ret; } #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if (mask & WPA_BSS_MASK_P2P_SCAN) { ie = (const u8 *) (bss + 1); ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end); if (ret < 0 || ret >= end - pos) return 0; pos += ret; } #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY if (mask & WPA_BSS_MASK_WIFI_DISPLAY) { struct wpabuf *wfd; ie = (const u8 *) (bss + 1); wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len, WFD_IE_VENDOR_TYPE); if (wfd) { ret = os_snprintf(pos, end - pos, "wfd_subelems="); if (os_snprintf_error(end - pos, ret)) { wpabuf_free(wfd); return 0; } pos += ret; pos += wpa_snprintf_hex(pos, end - pos, wpabuf_head(wfd), wpabuf_len(wfd)); wpabuf_free(wfd); ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } } #endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_INTERWORKING if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) { struct wpa_bss_anqp *anqp = bss->anqp; pos = anqp_add_hex(pos, end, "anqp_capability_list", anqp->capability_list); pos = anqp_add_hex(pos, end, "anqp_venue_name", anqp->venue_name); pos = anqp_add_hex(pos, end, "anqp_network_auth_type", anqp->network_auth_type); pos = anqp_add_hex(pos, end, "anqp_roaming_consortium", anqp->roaming_consortium); pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability", anqp->ip_addr_type_availability); pos = anqp_add_hex(pos, end, "anqp_nai_realm", anqp->nai_realm); pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp); pos = anqp_add_hex(pos, end, "anqp_domain_name", anqp->domain_name); #ifdef CONFIG_HS20 pos = anqp_add_hex(pos, end, "hs20_capability_list", anqp->hs20_capability_list); pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name", anqp->hs20_operator_friendly_name); pos = anqp_add_hex(pos, end, "hs20_wan_metrics", anqp->hs20_wan_metrics); pos = anqp_add_hex(pos, end, "hs20_connection_capability", anqp->hs20_connection_capability); pos = anqp_add_hex(pos, end, "hs20_operating_class", anqp->hs20_operating_class); pos = anqp_add_hex(pos, end, "hs20_osu_providers_list", anqp->hs20_osu_providers_list); #endif /* CONFIG_HS20 */ } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_MESH if (mask & WPA_BSS_MASK_MESH_SCAN) { ie = (const u8 *) (bss + 1); ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end); if (ret < 0 || ret >= end - pos) return 0; pos += ret; } #endif /* CONFIG_MESH */ if (mask & WPA_BSS_MASK_SNR) { ret = os_snprintf(pos, end - pos, "snr=%d\n", bss->snr); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_EST_THROUGHPUT) { ret = os_snprintf(pos, end - pos, "est_throughput=%d\n", bss->est_throughput); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #ifdef CONFIG_FST if (mask & WPA_BSS_MASK_FST) { ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos); if (ret < 0 || ret >= end - pos) return 0; pos += ret; } #endif /* CONFIG_FST */ if (mask & WPA_BSS_MASK_DELIM) { ret = os_snprintf(pos, end - pos, "====\n"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } return pos - buf; } static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, const char *cmd, char *buf, size_t buflen) { u8 bssid[ETH_ALEN]; size_t i; struct wpa_bss *bss; struct wpa_bss *bsslast = NULL; struct dl_list *next; int ret = 0; int len; char *ctmp, *end = buf + buflen; unsigned long mask = WPA_BSS_MASK_ALL; if (os_strncmp(cmd, "RANGE=", 6) == 0) { if (os_strncmp(cmd + 6, "ALL", 3) == 0) { bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id); bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id); } else { /* N1-N2 */ unsigned int id1, id2; if ((ctmp = os_strchr(cmd + 6, '-')) == NULL) { wpa_printf(MSG_INFO, "Wrong BSS range " "format"); return 0; } if (*(cmd + 6) == '-') id1 = 0; else id1 = atoi(cmd + 6); ctmp++; if (*ctmp >= '0' && *ctmp <= '9') id2 = atoi(ctmp); else id2 = (unsigned int) -1; bss = wpa_bss_get_id_range(wpa_s, id1, id2); if (id2 == (unsigned int) -1) bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id); else { bsslast = wpa_bss_get_id(wpa_s, id2); if (bsslast == NULL && bss && id2 > id1) { struct wpa_bss *tmp = bss; for (;;) { next = tmp->list_id.next; if (next == &wpa_s->bss_id) break; tmp = dl_list_entry( next, struct wpa_bss, list_id); if (tmp->id > id2) break; bsslast = tmp; } } } } } else if (os_strncmp(cmd, "FIRST", 5) == 0) bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id); else if (os_strncmp(cmd, "LAST", 4) == 0) bss = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id); else if (os_strncmp(cmd, "ID-", 3) == 0) { i = atoi(cmd + 3); bss = wpa_bss_get_id(wpa_s, i); } else if (os_strncmp(cmd, "NEXT-", 5) == 0) { i = atoi(cmd + 5); bss = wpa_bss_get_id(wpa_s, i); if (bss) { next = bss->list_id.next; if (next == &wpa_s->bss_id) bss = NULL; else bss = dl_list_entry(next, struct wpa_bss, list_id); } #ifdef CONFIG_P2P } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { if (hwaddr_aton(cmd + 13, bssid) == 0) bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid); else bss = NULL; #endif /* CONFIG_P2P */ } else if (hwaddr_aton(cmd, bssid) == 0) bss = wpa_bss_get_bssid(wpa_s, bssid); else { struct wpa_bss *tmp; i = atoi(cmd); bss = NULL; dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id) { if (i-- == 0) { bss = tmp; break; } } } if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) { mask = strtoul(ctmp + 5, NULL, 0x10); if (mask == 0) mask = WPA_BSS_MASK_ALL; } if (bss == NULL) return 0; if (bsslast == NULL) bsslast = bss; do { len = print_bss_info(wpa_s, bss, mask, buf, buflen); ret += len; buf += len; buflen -= len; if (bss == bsslast) { if ((mask & WPA_BSS_MASK_DELIM) && len && (bss == dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id))) { int res; res = os_snprintf(buf - 5, end - buf + 5, "####\n"); if (os_snprintf_error(end - buf + 5, res)) { wpa_printf(MSG_DEBUG, "Could not add end delim"); } } break; } next = bss->list_id.next; if (next == &wpa_s->bss_id) break; bss = dl_list_entry(next, struct wpa_bss, list_id); } while (bss && len); return ret; } static int wpa_supplicant_ctrl_iface_ap_scan( struct wpa_supplicant *wpa_s, char *cmd) { int ap_scan = atoi(cmd); return wpa_supplicant_set_ap_scan(wpa_s, ap_scan); } static int wpa_supplicant_ctrl_iface_scan_interval( struct wpa_supplicant *wpa_s, char *cmd) { int scan_int = atoi(cmd); return wpa_supplicant_set_scan_interval(wpa_s, scan_int); } static int wpa_supplicant_ctrl_iface_bss_expire_age( struct wpa_supplicant *wpa_s, char *cmd) { int expire_age = atoi(cmd); return wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age); } static int wpa_supplicant_ctrl_iface_bss_expire_count( struct wpa_supplicant *wpa_s, char *cmd) { int expire_count = atoi(cmd); return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count); } static void wpa_supplicant_ctrl_iface_bss_flush( struct wpa_supplicant *wpa_s, char *cmd) { int flush_age = atoi(cmd); if (flush_age == 0) wpa_bss_flush(wpa_s); else wpa_bss_flush_by_age(wpa_s, flush_age); } #ifdef CONFIG_TESTING_OPTIONS static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication"); /* MLME-DELETEKEYS.request */ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0); #ifdef CONFIG_IEEE80211W wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0); #endif /* CONFIG_IEEE80211W */ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL, 0); /* MLME-SETPROTECTION.request(None) */ wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid, MLME_SETPROTECTION_PROTECT_TYPE_NONE, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); wpa_sm_drop_sa(wpa_s->wpa); } #endif /* CONFIG_TESTING_OPTIONS */ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s, char *addr) { #ifdef CONFIG_NO_SCAN_PROCESSING return -1; #else /* CONFIG_NO_SCAN_PROCESSING */ u8 bssid[ETH_ALEN]; struct wpa_bss *bss; struct wpa_ssid *ssid = wpa_s->current_ssid; if (hwaddr_aton(addr, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid)); if (!ssid) { wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network " "configuration known for the target AP"); return -1; } bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); if (!bss) { wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found " "from BSS table"); return -1; } /* * TODO: Find best network configuration block from configuration to * allow roaming to other networks */ wpa_s->reassociate = 1; wpa_supplicant_connect(wpa_s, bss, ssid); return 0; #endif /* CONFIG_NO_SCAN_PROCESSING */ } #ifdef CONFIG_P2P static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) { unsigned int timeout = atoi(cmd); enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL; u8 dev_id[ETH_ALEN], *_dev_id = NULL; u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL; char *pos; unsigned int search_delay; const char *_seek[P2P_MAX_QUERY_HASH + 1], **seek = NULL; u8 seek_count = 0; int freq = 0; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_dbg(wpa_s, MSG_INFO, "Reject P2P_FIND since interface is disabled"); return -1; } if (os_strstr(cmd, "type=social")) type = P2P_FIND_ONLY_SOCIAL; else if (os_strstr(cmd, "type=progressive")) type = P2P_FIND_PROGRESSIVE; pos = os_strstr(cmd, "dev_id="); if (pos) { pos += 7; if (hwaddr_aton(pos, dev_id)) return -1; _dev_id = dev_id; } pos = os_strstr(cmd, "dev_type="); if (pos) { pos += 9; if (wps_dev_type_str2bin(pos, dev_type) < 0) return -1; _dev_type = dev_type; } pos = os_strstr(cmd, "delay="); if (pos) { pos += 6; search_delay = atoi(pos); } else search_delay = wpas_p2p_search_delay(wpa_s); pos = os_strstr(cmd, "freq="); if (pos) { pos += 5; freq = atoi(pos); if (freq <= 0) return -1; } /* Must be searched for last, because it adds nul termination */ pos = os_strstr(cmd, " seek="); if (pos) pos += 6; while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) { char *term; _seek[seek_count++] = pos; seek = _seek; term = os_strchr(pos, ' '); if (!term) break; *term = '\0'; pos = os_strstr(term + 1, "seek="); if (pos) pos += 5; } if (seek_count > P2P_MAX_QUERY_HASH) { seek[0] = NULL; seek_count = 1; } return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type, _dev_id, search_delay, seek_count, seek, freq); } static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt) { const char *last = NULL; const char *token; long int token_len; unsigned int i; /* Expected predefined CPT names delimited by ':' */ for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) { if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) { wpa_printf(MSG_ERROR, "P2PS: CPT name list is too long, expected up to %d names", P2PS_FEATURE_CAPAB_CPT_MAX); cpt[0] = 0; return -1; } token_len = last - token; if (token_len == 3 && os_memcmp(token, "UDP", token_len) == 0) { cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; } else if (token_len == 3 && os_memcmp(token, "MAC", token_len) == 0) { cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT; } else { wpa_printf(MSG_ERROR, "P2PS: Unsupported CPT name '%s'", token); cpt[0] = 0; return -1; } if (isblank(*last)) { i++; break; } } cpt[i] = 0; return 0; } static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd) { struct p2ps_provision *p2ps_prov; char *pos; size_t info_len = 0; char *info = NULL; u8 role = P2PS_SETUP_NONE; long long unsigned val; int i; pos = os_strstr(cmd, "info="); if (pos) { pos += 5; info_len = os_strlen(pos); if (info_len) { info = os_malloc(info_len + 1); if (info) { info_len = utf8_unescape(pos, info_len, info, info_len + 1); } else info_len = 0; } } p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + info_len + 1); if (p2ps_prov == NULL) { os_free(info); return NULL; } if (info) { os_memcpy(p2ps_prov->info, info, info_len); p2ps_prov->info[info_len] = '\0'; os_free(info); } pos = os_strstr(cmd, "status="); if (pos) p2ps_prov->status = atoi(pos + 7); else p2ps_prov->status = -1; pos = os_strstr(cmd, "adv_id="); if (!pos || sscanf(pos + 7, "%llx", &val) != 1 || val > 0xffffffffULL) goto invalid_args; p2ps_prov->adv_id = val; pos = os_strstr(cmd, "method="); if (pos) p2ps_prov->method = strtol(pos + 7, NULL, 16); else p2ps_prov->method = 0; pos = os_strstr(cmd, "session="); if (!pos || sscanf(pos + 8, "%llx", &val) != 1 || val > 0xffffffffULL) goto invalid_args; p2ps_prov->session_id = val; pos = os_strstr(cmd, "adv_mac="); if (!pos || hwaddr_aton(pos + 8, p2ps_prov->adv_mac)) goto invalid_args; pos = os_strstr(cmd, "session_mac="); if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac)) goto invalid_args; pos = os_strstr(cmd, "cpt="); if (pos) { if (p2ps_ctrl_parse_cpt_priority(pos + 4, p2ps_prov->cpt_priority)) goto invalid_args; } else { p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; } for (i = 0; p2ps_prov->cpt_priority[i]; i++) p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i]; /* force conncap with tstCap (no sanity checks) */ pos = os_strstr(cmd, "tstCap="); if (pos) { role = strtol(pos + 7, NULL, 16); } else { pos = os_strstr(cmd, "role="); if (pos) { role = strtol(pos + 5, NULL, 16); if (role != P2PS_SETUP_CLIENT && role != P2PS_SETUP_GROUP_OWNER) role = P2PS_SETUP_NONE; } } p2ps_prov->role = role; return p2ps_prov; invalid_args: os_free(p2ps_prov); return NULL; } static int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; struct p2ps_provision *p2ps_prov; char *pos; /* id= [role=] [info=] */ wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd); if (hwaddr_aton(cmd, addr)) return -1; pos = cmd + 17; if (*pos != ' ') return -1; p2ps_prov = p2p_parse_asp_provision_cmd(pos); if (!p2ps_prov) return -1; if (p2ps_prov->status < 0) { os_free(p2ps_prov); return -1; } return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, p2ps_prov); } static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; struct p2ps_provision *p2ps_prov; char *pos; /* id= adv_mac= conncap= * session= mac= [info=] */ wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd); if (hwaddr_aton(cmd, addr)) return -1; pos = cmd + 17; if (*pos != ' ') return -1; p2ps_prov = p2p_parse_asp_provision_cmd(pos); if (!p2ps_prov) return -1; p2ps_prov->pd_seeker = 1; return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, p2ps_prov); } static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u8 addr[ETH_ALEN]; char *pos, *pos2; char *pin = NULL; enum p2p_wps_method wps_method; int new_pin; int ret; int persistent_group, persistent_id = -1; int join; int auth; int automatic; int go_intent = -1; int freq = 0; int pd; int ht40, vht; if (!wpa_s->global->p2p_init_wpa_s) return -1; if (wpa_s->global->p2p_init_wpa_s != wpa_s) { wpa_dbg(wpa_s, MSG_DEBUG, "Direct P2P_CONNECT command to %s", wpa_s->global->p2p_init_wpa_s->ifname); wpa_s = wpa_s->global->p2p_init_wpa_s; } /* <"pbc" | "pin" | PIN> [label|display|keypad|p2ps] * [persistent|persistent=] * [join] [auth] [go_intent=<0..15>] [freq=] [provdisc] * [ht40] [vht] [auto] */ if (hwaddr_aton(cmd, addr)) return -1; pos = cmd + 17; if (*pos != ' ') return -1; pos++; persistent_group = os_strstr(pos, " persistent") != NULL; pos2 = os_strstr(pos, " persistent="); if (pos2) { struct wpa_ssid *ssid; persistent_id = atoi(pos2 + 12); ssid = wpa_config_get_network(wpa_s->conf, persistent_id); if (ssid == NULL || ssid->disabled != 2 || ssid->mode != WPAS_MODE_P2P_GO) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "SSID id=%d for persistent P2P group (GO)", persistent_id); return -1; } } join = os_strstr(pos, " join") != NULL; auth = os_strstr(pos, " auth") != NULL; automatic = os_strstr(pos, " auto") != NULL; pd = os_strstr(pos, " provdisc") != NULL; vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht; ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || vht; pos2 = os_strstr(pos, " go_intent="); if (pos2) { pos2 += 11; go_intent = atoi(pos2); if (go_intent < 0 || go_intent > 15) return -1; } pos2 = os_strstr(pos, " freq="); if (pos2) { pos2 += 6; freq = atoi(pos2); if (freq <= 0) return -1; } if (os_strncmp(pos, "pin", 3) == 0) { /* Request random PIN (to be displayed) and enable the PIN */ wps_method = WPS_PIN_DISPLAY; } else if (os_strncmp(pos, "pbc", 3) == 0) { wps_method = WPS_PBC; } else { pin = pos; pos = os_strchr(pin, ' '); wps_method = WPS_PIN_KEYPAD; if (pos) { *pos++ = '\0'; if (os_strncmp(pos, "display", 7) == 0) wps_method = WPS_PIN_DISPLAY; else if (os_strncmp(pos, "p2ps", 4) == 0) wps_method = WPS_P2PS; } if (!wps_pin_str_valid(pin)) { os_memcpy(buf, "FAIL-INVALID-PIN\n", 17); return 17; } } new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, automatic, join, auth, go_intent, freq, persistent_id, pd, ht40, vht); if (new_pin == -2) { os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25); return 25; } if (new_pin == -3) { os_memcpy(buf, "FAIL-CHANNEL-UNSUPPORTED\n", 25); return 25; } if (new_pin < 0) return -1; if (wps_method == WPS_PIN_DISPLAY && pin == NULL) { ret = os_snprintf(buf, buflen, "%08d", new_pin); if (os_snprintf_error(buflen, ret)) return -1; return ret; } os_memcpy(buf, "OK\n", 3); return 3; } static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd) { unsigned int timeout = atoi(cmd); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_dbg(wpa_s, MSG_INFO, "Reject P2P_LISTEN since interface is disabled"); return -1; } return wpas_p2p_listen(wpa_s, timeout); } static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; char *pos; enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG; /* [join|auto] */ if (hwaddr_aton(cmd, addr)) return -1; pos = cmd + 17; if (*pos != ' ') return -1; pos++; if (os_strstr(pos, " join") != NULL) use = WPAS_P2P_PD_FOR_JOIN; else if (os_strstr(pos, " auto") != NULL) use = WPAS_P2P_PD_AUTO; return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL); } static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || ssid->passphrase == NULL) return -1; os_strlcpy(buf, ssid->passphrase, buflen); return os_strlen(buf); } static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u64 ref; int res; u8 dst_buf[ETH_ALEN], *dst; struct wpabuf *tlvs; char *pos; size_t len; if (hwaddr_aton(cmd, dst_buf)) return -1; dst = dst_buf; if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 && dst[3] == 0 && dst[4] == 0 && dst[5] == 0) dst = NULL; pos = cmd + 17; if (*pos != ' ') return -1; pos++; if (os_strncmp(pos, "upnp ", 5) == 0) { u8 version; pos += 5; if (hexstr2bin(pos, &version, 1) < 0) return -1; pos += 2; if (*pos != ' ') return -1; pos++; ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos); #ifdef CONFIG_WIFI_DISPLAY } else if (os_strncmp(pos, "wifi-display ", 13) == 0) { ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13); #endif /* CONFIG_WIFI_DISPLAY */ } else if (os_strncmp(pos, "asp ", 4) == 0) { char *svc_str; char *svc_info = NULL; u32 id; pos += 4; if (sscanf(pos, "%x", &id) != 1 || id > 0xff) return -1; pos = os_strchr(pos, ' '); if (pos == NULL || pos[1] == '\0' || pos[1] == ' ') return -1; svc_str = pos + 1; pos = os_strchr(svc_str, ' '); if (pos) *pos++ = '\0'; /* All remaining data is the svc_info string */ if (pos && pos[0] && pos[0] != ' ') { len = os_strlen(pos); /* Unescape in place */ len = utf8_unescape(pos, len, pos, len); if (len > 0xff) return -1; svc_info = pos; } ref = wpas_p2p_sd_request_asp(wpa_s, dst, (u8) id, svc_str, svc_info); } else { len = os_strlen(pos); if (len & 1) return -1; len /= 2; tlvs = wpabuf_alloc(len); if (tlvs == NULL) return -1; if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) { wpabuf_free(tlvs); return -1; } ref = wpas_p2p_sd_request(wpa_s, dst, tlvs); wpabuf_free(tlvs); } if (ref == 0) return -1; res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref); if (os_snprintf_error(buflen, res)) return -1; return res; } static int p2p_ctrl_serv_disc_cancel_req(struct wpa_supplicant *wpa_s, char *cmd) { long long unsigned val; u64 req; if (sscanf(cmd, "%llx", &val) != 1) return -1; req = val; return wpas_p2p_sd_cancel_request(wpa_s, req); } static int p2p_ctrl_serv_disc_resp(struct wpa_supplicant *wpa_s, char *cmd) { int freq; u8 dst[ETH_ALEN]; u8 dialog_token; struct wpabuf *resp_tlvs; char *pos, *pos2; size_t len; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; freq = atoi(cmd); if (freq == 0) return -1; if (hwaddr_aton(pos, dst)) return -1; pos += 17; if (*pos != ' ') return -1; pos++; pos2 = os_strchr(pos, ' '); if (pos2 == NULL) return -1; *pos2++ = '\0'; dialog_token = atoi(pos); len = os_strlen(pos2); if (len & 1) return -1; len /= 2; resp_tlvs = wpabuf_alloc(len); if (resp_tlvs == NULL) return -1; if (hexstr2bin(pos2, wpabuf_put(resp_tlvs, len), len) < 0) { wpabuf_free(resp_tlvs); return -1; } wpas_p2p_sd_response(wpa_s, freq, dst, dialog_token, resp_tlvs); wpabuf_free(resp_tlvs); return 0; } static int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s, char *cmd) { if (os_strcmp(cmd, "0") && os_strcmp(cmd, "1")) return -1; wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd); return 0; } static int p2p_ctrl_service_add_bonjour(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; size_t len; struct wpabuf *query, *resp; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; len = os_strlen(cmd); if (len & 1) return -1; len /= 2; query = wpabuf_alloc(len); if (query == NULL) return -1; if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) { wpabuf_free(query); return -1; } len = os_strlen(pos); if (len & 1) { wpabuf_free(query); return -1; } len /= 2; resp = wpabuf_alloc(len); if (resp == NULL) { wpabuf_free(query); return -1; } if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) { wpabuf_free(query); wpabuf_free(resp); return -1; } if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) { wpabuf_free(query); wpabuf_free(resp); return -1; } return 0; } static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; u8 version; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (hexstr2bin(cmd, &version, 1) < 0) return -1; return wpas_p2p_service_add_upnp(wpa_s, version, pos); } static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s, u8 replace, char *cmd) { char *pos; char *adv_str; u32 auto_accept, adv_id, svc_state, config_methods; char *svc_info = NULL; char *cpt_prio_str; u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; /* Auto-Accept value is mandatory, and must be one of the * single values (0, 1, 2, 4) */ auto_accept = atoi(cmd); switch (auto_accept) { case P2PS_SETUP_NONE: /* No auto-accept */ case P2PS_SETUP_NEW: case P2PS_SETUP_CLIENT: case P2PS_SETUP_GROUP_OWNER: break; default: return -1; } /* Advertisement ID is mandatory */ cmd = pos; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; /* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */ if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0) return -1; /* Only allow replacements if exist, and adds if not */ if (wpas_p2p_service_p2ps_id_exists(wpa_s, adv_id)) { if (!replace) return -1; } else { if (replace) return -1; } /* svc_state between 0 - 0xff is mandatory */ if (sscanf(pos, "%x", &svc_state) != 1 || svc_state > 0xff) return -1; pos = os_strchr(pos, ' '); if (pos == NULL) return -1; /* config_methods is mandatory */ pos++; if (sscanf(pos, "%x", &config_methods) != 1) return -1; if (!(config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS))) return -1; pos = os_strchr(pos, ' '); if (pos == NULL) return -1; pos++; adv_str = pos; /* Advertisement string is mandatory */ if (!pos[0] || pos[0] == ' ') return -1; /* Terminate svc string */ pos = os_strchr(pos, ' '); if (pos != NULL) *pos++ = '\0'; cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL; if (cpt_prio_str) { pos = os_strchr(pos, ' '); if (pos != NULL) *pos++ = '\0'; if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio)) return -1; } else { cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; cpt_prio[1] = 0; } /* Service and Response Information are optional */ if (pos && pos[0]) { size_t len; /* Note the bare ' included, which cannot exist legally * in unescaped string. */ svc_info = os_strstr(pos, "svc_info='"); if (svc_info) { svc_info += 9; len = os_strlen(svc_info); utf8_unescape(svc_info, len, svc_info, len); } } return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str, (u8) svc_state, (u16) config_methods, svc_info, cpt_prio); } static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (os_strcmp(cmd, "bonjour") == 0) return p2p_ctrl_service_add_bonjour(wpa_s, pos); if (os_strcmp(cmd, "upnp") == 0) return p2p_ctrl_service_add_upnp(wpa_s, pos); if (os_strcmp(cmd, "asp") == 0) return p2p_ctrl_service_add_asp(wpa_s, 0, pos); wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); return -1; } static int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s, char *cmd) { size_t len; struct wpabuf *query; int ret; len = os_strlen(cmd); if (len & 1) return -1; len /= 2; query = wpabuf_alloc(len); if (query == NULL) return -1; if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) { wpabuf_free(query); return -1; } ret = wpas_p2p_service_del_bonjour(wpa_s, query); wpabuf_free(query); return ret; } static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; u8 version; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (hexstr2bin(cmd, &version, 1) < 0) return -1; return wpas_p2p_service_del_upnp(wpa_s, version, pos); } static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd) { u32 adv_id; if (os_strcmp(cmd, "all") == 0) { wpas_p2p_service_flush_asp(wpa_s); return 0; } if (sscanf(cmd, "%x", &adv_id) != 1) return -1; return wpas_p2p_service_del_asp(wpa_s, adv_id); } static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (os_strcmp(cmd, "bonjour") == 0) return p2p_ctrl_service_del_bonjour(wpa_s, pos); if (os_strcmp(cmd, "upnp") == 0) return p2p_ctrl_service_del_upnp(wpa_s, pos); if (os_strcmp(cmd, "asp") == 0) return p2p_ctrl_service_del_asp(wpa_s, pos); wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); return -1; } static int p2p_ctrl_service_replace(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (os_strcmp(cmd, "asp") == 0) return p2p_ctrl_service_add_asp(wpa_s, 1, pos); wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); return -1; } static int p2p_ctrl_reject(struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; /* */ if (hwaddr_aton(cmd, addr)) return -1; return wpas_p2p_reject(wpa_s, addr); } static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; int id; struct wpa_ssid *ssid; u8 *_peer = NULL, peer[ETH_ALEN]; int freq = 0, pref_freq = 0; int ht40, vht; id = atoi(cmd); pos = os_strstr(cmd, " peer="); if (pos) { pos += 6; if (hwaddr_aton(pos, peer)) return -1; _peer = peer; } ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL || ssid->disabled != 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " "for persistent P2P group", id); return -1; } pos = os_strstr(cmd, " freq="); if (pos) { pos += 6; freq = atoi(pos); if (freq <= 0) return -1; } pos = os_strstr(cmd, " pref="); if (pos) { pos += 6; pref_freq = atoi(pos); if (pref_freq <= 0) return -1; } vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht; ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || vht; return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht, pref_freq); } static int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; u8 peer[ETH_ALEN], go_dev_addr[ETH_ALEN], *go_dev = NULL; pos = os_strstr(cmd, " peer="); if (!pos) return -1; *pos = '\0'; pos += 6; if (hwaddr_aton(pos, peer)) { wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos); return -1; } pos = os_strstr(pos, " go_dev_addr="); if (pos) { pos += 13; if (hwaddr_aton(pos, go_dev_addr)) { wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos); return -1; } go_dev = go_dev_addr; } return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev); } static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) { if (os_strncmp(cmd, "persistent=", 11) == 0) return p2p_ctrl_invite_persistent(wpa_s, cmd + 11); if (os_strncmp(cmd, "group=", 6) == 0) return p2p_ctrl_invite_group(wpa_s, cmd + 6); return -1; } static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, int id, int freq, int ht40, int vht) { struct wpa_ssid *ssid; ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL || ssid->disabled != 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " "for persistent P2P group", id); return -1; } return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht, NULL, 0, 0); } static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) { int freq = 0, persistent = 0, group_id = -1; int vht = wpa_s->conf->p2p_go_vht; int ht40 = wpa_s->conf->p2p_go_ht40 || vht; char *token, *context = NULL; while ((token = str_token(cmd, " ", &context))) { if (sscanf(token, "freq=%d", &freq) == 1 || sscanf(token, "persistent=%d", &group_id) == 1) { continue; } else if (os_strcmp(token, "ht40") == 0) { ht40 = 1; } else if (os_strcmp(token, "vht") == 0) { vht = 1; ht40 = 1; } else if (os_strcmp(token, "persistent") == 0) { persistent = 1; } else { wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameter: '%s'", token); return -1; } } if (group_id >= 0) return p2p_ctrl_group_add_persistent(wpa_s, group_id, freq, ht40, vht); return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht); } static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u8 addr[ETH_ALEN], *addr_ptr; int next, res; const struct p2p_peer_info *info; char *pos, *end; char devtype[WPS_DEV_TYPE_BUFSIZE]; struct wpa_ssid *ssid; size_t i; if (!wpa_s->global->p2p) return -1; if (os_strcmp(cmd, "FIRST") == 0) { addr_ptr = NULL; next = 0; } else if (os_strncmp(cmd, "NEXT-", 5) == 0) { if (hwaddr_aton(cmd + 5, addr) < 0) return -1; addr_ptr = addr; next = 1; } else { if (hwaddr_aton(cmd, addr) < 0) return -1; addr_ptr = addr; next = 0; } info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next); if (info == NULL) return -1; pos = buf; end = buf + buflen; res = os_snprintf(pos, end - pos, MACSTR "\n" "pri_dev_type=%s\n" "device_name=%s\n" "manufacturer=%s\n" "model_name=%s\n" "model_number=%s\n" "serial_number=%s\n" "config_methods=0x%x\n" "dev_capab=0x%x\n" "group_capab=0x%x\n" "level=%d\n", MAC2STR(info->p2p_device_addr), wps_dev_type_bin2str(info->pri_dev_type, devtype, sizeof(devtype)), info->device_name, info->manufacturer, info->model_name, info->model_number, info->serial_number, info->config_methods, info->dev_capab, info->group_capab, info->level); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; for (i = 0; i < info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; i++) { const u8 *t; t = &info->wps_sec_dev_type_list[i * WPS_DEV_TYPE_LEN]; res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n", wps_dev_type_bin2str(t, devtype, sizeof(devtype))); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0); if (ssid) { res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } res = p2p_get_peer_info_txt(info, pos, end - pos); if (res < 0) return pos - buf; pos += res; if (info->vendor_elems) { res = os_snprintf(pos, end - pos, "vendor_elems="); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; pos += wpa_snprintf_hex(pos, end - pos, wpabuf_head(info->vendor_elems), wpabuf_len(info->vendor_elems)); res = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } return pos - buf; } static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s, const char *param) { unsigned int i; if (wpa_s->global->p2p == NULL) return -1; if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0) return -1; for (i = 0; i < wpa_s->global->p2p_disallow_freq.num; i++) { struct wpa_freq_range *freq; freq = &wpa_s->global->p2p_disallow_freq.range[i]; wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u", freq->min, freq->max); } wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW); return 0; } static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) { char *param; if (wpa_s->global->p2p == NULL) return -1; param = os_strchr(cmd, ' '); if (param == NULL) return -1; *param++ = '\0'; if (os_strcmp(cmd, "discoverability") == 0) { p2p_set_client_discoverability(wpa_s->global->p2p, atoi(param)); return 0; } if (os_strcmp(cmd, "managed") == 0) { p2p_set_managed_oper(wpa_s->global->p2p, atoi(param)); return 0; } if (os_strcmp(cmd, "listen_channel") == 0) { return p2p_set_listen_channel(wpa_s->global->p2p, 81, atoi(param), 1); } if (os_strcmp(cmd, "ssid_postfix") == 0) { return p2p_set_ssid_postfix(wpa_s->global->p2p, (u8 *) param, os_strlen(param)); } if (os_strcmp(cmd, "noa") == 0) { char *pos; int count, start, duration; /* GO NoA parameters: count,start_offset(ms),duration(ms) */ count = atoi(param); pos = os_strchr(param, ','); if (pos == NULL) return -1; pos++; start = atoi(pos); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; duration = atoi(pos); if (count < 0 || count > 255 || start < 0 || duration < 0) return -1; if (count == 0 && duration > 0) return -1; wpa_printf(MSG_DEBUG, "CTRL_IFACE: P2P_SET GO NoA: count=%d " "start=%d duration=%d", count, start, duration); return wpas_p2p_set_noa(wpa_s, count, start, duration); } if (os_strcmp(cmd, "ps") == 0) return wpa_drv_set_p2p_powersave(wpa_s, atoi(param), -1, -1); if (os_strcmp(cmd, "oppps") == 0) return wpa_drv_set_p2p_powersave(wpa_s, -1, atoi(param), -1); if (os_strcmp(cmd, "ctwindow") == 0) return wpa_drv_set_p2p_powersave(wpa_s, -1, -1, atoi(param)); if (os_strcmp(cmd, "disabled") == 0) { wpa_s->global->p2p_disabled = atoi(param); wpa_printf(MSG_DEBUG, "P2P functionality %s", wpa_s->global->p2p_disabled ? "disabled" : "enabled"); if (wpa_s->global->p2p_disabled) { wpas_p2p_stop_find(wpa_s); os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); p2p_flush(wpa_s->global->p2p); } return 0; } if (os_strcmp(cmd, "conc_pref") == 0) { if (os_strcmp(param, "sta") == 0) wpa_s->global->conc_pref = WPA_CONC_PREF_STA; else if (os_strcmp(param, "p2p") == 0) wpa_s->global->conc_pref = WPA_CONC_PREF_P2P; else { wpa_printf(MSG_INFO, "Invalid conc_pref value"); return -1; } wpa_printf(MSG_DEBUG, "Single channel concurrency preference: " "%s", param); return 0; } if (os_strcmp(cmd, "force_long_sd") == 0) { wpa_s->force_long_sd = atoi(param); return 0; } if (os_strcmp(cmd, "peer_filter") == 0) { u8 addr[ETH_ALEN]; if (hwaddr_aton(param, addr)) return -1; p2p_set_peer_filter(wpa_s->global->p2p, addr); return 0; } if (os_strcmp(cmd, "cross_connect") == 0) return wpas_p2p_set_cross_connect(wpa_s, atoi(param)); if (os_strcmp(cmd, "go_apsd") == 0) { if (os_strcmp(param, "disable") == 0) wpa_s->set_ap_uapsd = 0; else { wpa_s->set_ap_uapsd = 1; wpa_s->ap_uapsd = atoi(param); } return 0; } if (os_strcmp(cmd, "client_apsd") == 0) { if (os_strcmp(param, "disable") == 0) wpa_s->set_sta_uapsd = 0; else { int be, bk, vi, vo; char *pos; /* format: BE,BK,VI,VO;max SP Length */ be = atoi(param); pos = os_strchr(param, ','); if (pos == NULL) return -1; pos++; bk = atoi(pos); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; vi = atoi(pos); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; vo = atoi(pos); /* ignore max SP Length for now */ wpa_s->set_sta_uapsd = 1; wpa_s->sta_uapsd = 0; if (be) wpa_s->sta_uapsd |= BIT(0); if (bk) wpa_s->sta_uapsd |= BIT(1); if (vi) wpa_s->sta_uapsd |= BIT(2); if (vo) wpa_s->sta_uapsd |= BIT(3); } return 0; } if (os_strcmp(cmd, "disallow_freq") == 0) return p2p_ctrl_disallow_freq(wpa_s, param); if (os_strcmp(cmd, "disc_int") == 0) { int min_disc_int, max_disc_int, max_disc_tu; char *pos; pos = param; min_disc_int = atoi(pos); pos = os_strchr(pos, ' '); if (pos == NULL) return -1; *pos++ = '\0'; max_disc_int = atoi(pos); pos = os_strchr(pos, ' '); if (pos == NULL) return -1; *pos++ = '\0'; max_disc_tu = atoi(pos); return p2p_set_disc_int(wpa_s->global->p2p, min_disc_int, max_disc_int, max_disc_tu); } if (os_strcmp(cmd, "per_sta_psk") == 0) { wpa_s->global->p2p_per_sta_psk = !!atoi(param); return 0; } #ifdef CONFIG_WPS_NFC if (os_strcmp(cmd, "nfc_tag") == 0) return wpas_p2p_nfc_tag_enabled(wpa_s, !!atoi(param)); #endif /* CONFIG_WPS_NFC */ if (os_strcmp(cmd, "disable_ip_addr_req") == 0) { wpa_s->p2p_disable_ip_addr_req = !!atoi(param); return 0; } wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'", cmd); return -1; } static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s) { os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->force_long_sd = 0; wpas_p2p_stop_find(wpa_s); wpa_s->parent->p2ps_method_config_any = 0; if (wpa_s->global->p2p) p2p_flush(wpa_s->global->p2p); } static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *pos2; unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0; if (cmd[0]) { pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; dur1 = atoi(cmd); pos2 = os_strchr(pos, ' '); if (pos2) *pos2++ = '\0'; int1 = atoi(pos); } else pos2 = NULL; if (pos2) { pos = os_strchr(pos2, ' '); if (pos == NULL) return -1; *pos++ = '\0'; dur2 = atoi(pos2); int2 = atoi(pos); } return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2); } static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; unsigned int period = 0, interval = 0; if (cmd[0]) { pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; period = atoi(cmd); interval = atoi(pos); } return wpas_p2p_ext_listen(wpa_s, period, interval); } static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd) { const char *pos; u8 peer[ETH_ALEN]; int iface_addr = 0; pos = cmd; if (os_strncmp(pos, "iface=", 6) == 0) { iface_addr = 1; pos += 6; } if (hwaddr_aton(pos, peer)) return -1; wpas_p2p_remove_client(wpa_s, peer, iface_addr); return 0; } #endif /* CONFIG_P2P */ static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val) { struct wpa_freq_range_list ranges; int *freqs = NULL; struct hostapd_hw_modes *mode; u16 i; if (wpa_s->hw.modes == NULL) return NULL; os_memset(&ranges, 0, sizeof(ranges)); if (freq_range_list_parse(&ranges, val) < 0) return NULL; for (i = 0; i < wpa_s->hw.num_modes; i++) { int j; mode = &wpa_s->hw.modes[i]; for (j = 0; j < mode->num_channels; j++) { unsigned int freq; if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED) continue; freq = mode->channels[j].freq; if (!freq_range_list_includes(&ranges, freq)) continue; int_array_add_unique(&freqs, freq); } } os_free(ranges.range); return freqs; } #ifdef CONFIG_INTERWORKING static int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param) { int auto_sel = 0; int *freqs = NULL; if (param) { char *pos; auto_sel = os_strstr(param, "auto") != NULL; pos = os_strstr(param, "freq="); if (pos) { freqs = freq_range_to_channel_list(wpa_s, pos + 5); if (freqs == NULL) return -1; } } return interworking_select(wpa_s, auto_sel, freqs); } static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst, int only_add) { u8 bssid[ETH_ALEN]; struct wpa_bss *bss; if (hwaddr_aton(dst, bssid)) { wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst); return -1; } bss = wpa_bss_get_bssid(wpa_s, bssid); if (bss == NULL) { wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR, MAC2STR(bssid)); return -1; } if (bss->ssid_len == 0) { int found = 0; wpa_printf(MSG_DEBUG, "Selected BSS entry for " MACSTR " does not have SSID information", MAC2STR(bssid)); dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && bss->ssid_len > 0) { found = 1; break; } } if (!found) return -1; wpa_printf(MSG_DEBUG, "Found another matching BSS entry with SSID"); } return interworking_connect(wpa_s, bss, only_add); } static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) { u8 dst_addr[ETH_ALEN]; int used; char *pos; #define MAX_ANQP_INFO_ID 100 u16 id[MAX_ANQP_INFO_ID]; size_t num_id = 0; u32 subtypes = 0; used = hwaddr_aton2(dst, dst_addr); if (used < 0) return -1; pos = dst + used; if (*pos == ' ') pos++; while (num_id < MAX_ANQP_INFO_ID) { if (os_strncmp(pos, "hs20:", 5) == 0) { #ifdef CONFIG_HS20 int num = atoi(pos + 5); if (num <= 0 || num > 31) return -1; subtypes |= BIT(num); #else /* CONFIG_HS20 */ return -1; #endif /* CONFIG_HS20 */ } else { id[num_id] = atoi(pos); if (id[num_id]) num_id++; } pos = os_strchr(pos + 1, ','); if (pos == NULL) break; pos++; } if (num_id == 0) return -1; return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes); } static int gas_request(struct wpa_supplicant *wpa_s, char *cmd) { u8 dst_addr[ETH_ALEN]; struct wpabuf *advproto, *query = NULL; int used, ret = -1; char *pos, *end; size_t len; used = hwaddr_aton2(cmd, dst_addr); if (used < 0) return -1; pos = cmd + used; while (*pos == ' ') pos++; /* Advertisement Protocol ID */ end = os_strchr(pos, ' '); if (end) len = end - pos; else len = os_strlen(pos); if (len & 0x01) return -1; len /= 2; if (len == 0) return -1; advproto = wpabuf_alloc(len); if (advproto == NULL) return -1; if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0) goto fail; if (end) { /* Optional Query Request */ pos = end + 1; while (*pos == ' ') pos++; len = os_strlen(pos); if (len) { if (len & 0x01) goto fail; len /= 2; if (len == 0) goto fail; query = wpabuf_alloc(len); if (query == NULL) goto fail; if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0) goto fail; } } ret = gas_send_request(wpa_s, dst_addr, advproto, query); fail: wpabuf_free(advproto); wpabuf_free(query); return ret; } static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u8 addr[ETH_ALEN]; int dialog_token; int used; char *pos; size_t resp_len, start, requested_len; struct wpabuf *resp; int ret; used = hwaddr_aton2(cmd, addr); if (used < 0) return -1; pos = cmd + used; while (*pos == ' ') pos++; dialog_token = atoi(pos); if (wpa_s->last_gas_resp && os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 && dialog_token == wpa_s->last_gas_dialog_token) resp = wpa_s->last_gas_resp; else if (wpa_s->prev_gas_resp && os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 && dialog_token == wpa_s->prev_gas_dialog_token) resp = wpa_s->prev_gas_resp; else return -1; resp_len = wpabuf_len(resp); start = 0; requested_len = resp_len; pos = os_strchr(pos, ' '); if (pos) { start = atoi(pos); if (start > resp_len) return os_snprintf(buf, buflen, "FAIL-Invalid range"); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; requested_len = atoi(pos); if (start + requested_len > resp_len) return os_snprintf(buf, buflen, "FAIL-Invalid range"); } if (requested_len * 2 + 1 > buflen) return os_snprintf(buf, buflen, "FAIL-Too long response"); ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start, requested_len); if (start + requested_len == resp_len) { /* * Free memory by dropping the response after it has been * fetched. */ if (resp == wpa_s->prev_gas_resp) { wpabuf_free(wpa_s->prev_gas_resp); wpa_s->prev_gas_resp = NULL; } else { wpabuf_free(wpa_s->last_gas_resp); wpa_s->last_gas_resp = NULL; } } return ret; } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst) { u8 dst_addr[ETH_ALEN]; int used; char *pos; u32 subtypes = 0; used = hwaddr_aton2(dst, dst_addr); if (used < 0) return -1; pos = dst + used; if (*pos == ' ') pos++; for (;;) { int num = atoi(pos); if (num <= 0 || num > 31) return -1; subtypes |= BIT(num); pos = os_strchr(pos + 1, ','); if (pos == NULL) break; pos++; } if (subtypes == 0) return -1; return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0); } static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s, const u8 *addr, const char *realm) { u8 *buf; size_t rlen, len; int ret; rlen = os_strlen(realm); len = 3 + rlen; buf = os_malloc(len); if (buf == NULL) return -1; buf[0] = 1; /* NAI Home Realm Count */ buf[1] = 0; /* Formatted in accordance with RFC 4282 */ buf[2] = rlen; os_memcpy(buf + 3, realm, rlen); ret = hs20_anqp_send_req(wpa_s, addr, BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), buf, len); os_free(buf); return ret; } static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s, char *dst) { struct wpa_cred *cred = wpa_s->conf->cred; u8 dst_addr[ETH_ALEN]; int used; u8 *buf; size_t len; int ret; used = hwaddr_aton2(dst, dst_addr); if (used < 0) return -1; while (dst[used] == ' ') used++; if (os_strncmp(dst + used, "realm=", 6) == 0) return hs20_nai_home_realm_list(wpa_s, dst_addr, dst + used + 6); len = os_strlen(dst + used); if (len == 0 && cred && cred->realm) return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm); if (len & 1) return -1; len /= 2; buf = os_malloc(len); if (buf == NULL) return -1; if (hexstr2bin(dst + used, buf, len) < 0) { os_free(buf); return -1; } ret = hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), buf, len); os_free(buf); return ret; } static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd) { u8 dst_addr[ETH_ALEN]; int used; char *icon; used = hwaddr_aton2(cmd, dst_addr); if (used < 0) return -1; while (cmd[used] == ' ') used++; icon = &cmd[used]; wpa_s->fetch_osu_icon_in_progress = 0; return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST), (u8 *) icon, os_strlen(icon)); } #endif /* CONFIG_HS20 */ #ifdef CONFIG_AUTOSCAN static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s, char *cmd) { enum wpa_states state = wpa_s->wpa_state; char *new_params = NULL; if (os_strlen(cmd) > 0) { new_params = os_strdup(cmd); if (new_params == NULL) return -1; } os_free(wpa_s->conf->autoscan); wpa_s->conf->autoscan = new_params; if (wpa_s->conf->autoscan == NULL) autoscan_deinit(wpa_s); else if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) autoscan_init(wpa_s, 1); else if (state == WPA_SCANNING) wpa_supplicant_reinit_autoscan(wpa_s); return 0; } #endif /* CONFIG_AUTOSCAN */ #ifdef CONFIG_WNM static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd) { int enter; int intval = 0; char *pos; int ret; struct wpabuf *tfs_req = NULL; if (os_strncmp(cmd, "enter", 5) == 0) enter = 1; else if (os_strncmp(cmd, "exit", 4) == 0) enter = 0; else return -1; pos = os_strstr(cmd, " interval="); if (pos) intval = atoi(pos + 10); pos = os_strstr(cmd, " tfs_req="); if (pos) { char *end; size_t len; pos += 9; end = os_strchr(pos, ' '); if (end) len = end - pos; else len = os_strlen(pos); if (len & 1) return -1; len /= 2; tfs_req = wpabuf_alloc(len); if (tfs_req == NULL) return -1; if (hexstr2bin(pos, wpabuf_put(tfs_req, len), len) < 0) { wpabuf_free(tfs_req); return -1; } } ret = ieee802_11_send_wnmsleep_req(wpa_s, enter ? WNM_SLEEP_MODE_ENTER : WNM_SLEEP_MODE_EXIT, intval, tfs_req); wpabuf_free(tfs_req); return ret; } static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd) { int query_reason; query_reason = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d", query_reason); return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason); } #endif /* CONFIG_WNM */ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct wpa_signal_info si; int ret; char *pos, *end; ret = wpa_drv_signal_poll(wpa_s, &si); if (ret) return -1; pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n" "NOISE=%d\nFREQUENCY=%u\n", si.current_signal, si.current_txrate / 1000, si.current_noise, si.frequency); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; if (si.chanwidth != CHAN_WIDTH_UNKNOWN) { ret = os_snprintf(pos, end - pos, "WIDTH=%s\n", channel_width_to_string(si.chanwidth)); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (si.center_frq1 > 0 && si.center_frq2 > 0) { ret = os_snprintf(pos, end - pos, "CENTER_FRQ1=%d\nCENTER_FRQ2=%d\n", si.center_frq1, si.center_frq2); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (si.avg_signal) { ret = os_snprintf(pos, end - pos, "AVG_RSSI=%d\n", si.avg_signal); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (si.avg_beacon_signal) { ret = os_snprintf(pos, end - pos, "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } return pos - buf; } static int wpas_ctrl_iface_get_pref_freq_list( struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { unsigned int freq_list[100], num = 100, i; int ret; enum wpa_driver_if_type iface_type; char *pos, *end; pos = buf; end = buf + buflen; /* buf: "" */ if (os_strcmp(cmd, "STATION") == 0) iface_type = WPA_IF_STATION; else if (os_strcmp(cmd, "AP") == 0) iface_type = WPA_IF_AP_BSS; else if (os_strcmp(cmd, "P2P_GO") == 0) iface_type = WPA_IF_P2P_GO; else if (os_strcmp(cmd, "P2P_CLIENT") == 0) iface_type = WPA_IF_P2P_CLIENT; else if (os_strcmp(cmd, "IBSS") == 0) iface_type = WPA_IF_IBSS; else if (os_strcmp(cmd, "TDLS") == 0) iface_type = WPA_IF_TDLS; else return -1; wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)", iface_type, buf); ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list); if (ret) return -1; for (i = 0; i < num; i++) { ret = os_snprintf(pos, end - pos, "%s%u", i > 0 ? "," : "", freq_list[i]); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } return pos - buf; } static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct hostap_sta_driver_data sta; int ret; ret = wpa_drv_pktcnt_poll(wpa_s, &sta); if (ret) return -1; ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n", sta.tx_packets, sta.tx_retry_failed, sta.rx_packets); if (os_snprintf_error(buflen, ret)) return -1; return ret; } #ifdef ANDROID static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int ret; ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen); if (ret == 0) { if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) { struct p2p_data *p2p = wpa_s->global->p2p; if (p2p) { char country[3]; country[0] = cmd[8]; country[1] = cmd[9]; country[2] = 0x04; p2p_set_country(p2p, country); } } ret = os_snprintf(buf, buflen, "%s\n", "OK"); if (os_snprintf_error(buflen, ret)) ret = -1; } return ret; } #endif /* ANDROID */ static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int ret; char *pos; u8 *data = NULL; unsigned int vendor_id, subcmd; struct wpabuf *reply; size_t data_len = 0; /* cmd: [] */ vendor_id = strtoul(cmd, &pos, 16); if (!isblank(*pos)) return -EINVAL; subcmd = strtoul(pos, &pos, 10); if (*pos != '\0') { if (!isblank(*pos++)) return -EINVAL; data_len = os_strlen(pos); } if (data_len) { data_len /= 2; data = os_malloc(data_len); if (!data) return -1; if (hexstr2bin(pos, data, data_len)) { wpa_printf(MSG_DEBUG, "Vendor command: wrong parameter format"); os_free(data); return -EINVAL; } } reply = wpabuf_alloc((buflen - 1) / 2); if (!reply) { os_free(data); return -1; } ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len, reply); if (ret == 0) ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply), wpabuf_len(reply)); wpabuf_free(reply); os_free(data); return ret; } static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_P2P struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s ? wpa_s->global->p2p_init_wpa_s : wpa_s; #endif /* CONFIG_P2P */ wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state"); #ifdef CONFIG_P2P wpas_p2p_cancel(p2p_wpa_s); p2p_ctrl_flush(p2p_wpa_s); wpas_p2p_group_remove(p2p_wpa_s, "*"); wpas_p2p_service_flush(p2p_wpa_s); p2p_wpa_s->global->p2p_disabled = 0; p2p_wpa_s->global->p2p_per_sta_psk = 0; p2p_wpa_s->conf->num_sec_device_types = 0; p2p_wpa_s->p2p_disable_ip_addr_req = 0; os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range); p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL; p2p_wpa_s->global->p2p_go_avoid_freq.num = 0; p2p_wpa_s->global->pending_p2ps_group = 0; #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS_TESTING wps_version_number = 0x20; wps_testing_dummy_cred = 0; wps_corrupt_pkhash = 0; #endif /* CONFIG_WPS_TESTING */ #ifdef CONFIG_WPS wpa_s->wps_fragment_size = 0; wpas_wps_cancel(wpa_s); wps_registrar_flush(wpa_s->wps->registrar); #endif /* CONFIG_WPS */ wpa_s->after_wps = 0; wpa_s->known_wps_freq = 0; #ifdef CONFIG_TDLS #ifdef CONFIG_TDLS_TESTING extern unsigned int tdls_testing; tdls_testing = 0; #endif /* CONFIG_TDLS_TESTING */ wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL); wpa_tdls_enable(wpa_s->wpa, 1); #endif /* CONFIG_TDLS */ eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL); wpa_supplicant_stop_countermeasures(wpa_s, NULL); wpa_s->no_keep_alive = 0; wpa_s->own_disconnect_req = 0; os_free(wpa_s->disallow_aps_bssid); wpa_s->disallow_aps_bssid = NULL; wpa_s->disallow_aps_bssid_count = 0; os_free(wpa_s->disallow_aps_ssid); wpa_s->disallow_aps_ssid = NULL; wpa_s->disallow_aps_ssid_count = 0; wpa_s->set_sta_uapsd = 0; wpa_s->sta_uapsd = 0; wpa_drv_radio_disable(wpa_s, 0); wpa_blacklist_clear(wpa_s); wpa_s->extra_blacklist_count = 0; wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all"); wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all"); wpa_config_flush_blobs(wpa_s->conf); wpa_s->conf->auto_interworking = 0; wpa_s->conf->okc = 0; wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); rsn_preauth_deinit(wpa_s->wpa); wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200); wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70); wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60); eapol_sm_notify_logoff(wpa_s->eapol, FALSE); radio_remove_works(wpa_s, NULL, 1); wpa_s->ext_work_in_progress = 0; wpa_s->next_ssid = NULL; #ifdef CONFIG_INTERWORKING hs20_cancel_fetch_osu(wpa_s); #endif /* CONFIG_INTERWORKING */ wpa_s->ext_mgmt_frame_handling = 0; wpa_s->ext_eapol_frame_io = 0; #ifdef CONFIG_TESTING_OPTIONS wpa_s->extra_roc_dur = 0; wpa_s->test_failure = WPAS_TEST_FAILURE_NONE; #endif /* CONFIG_TESTING_OPTIONS */ wpa_s->disconnected = 0; os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; wpa_bss_flush(wpa_s); if (!dl_list_empty(&wpa_s->bss)) { wpa_printf(MSG_DEBUG, "BSS table not empty after flush: %u entries, current_bss=%p bssid=" MACSTR " pending_bssid=" MACSTR, dl_list_len(&wpa_s->bss), wpa_s->current_bss, MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid)); } eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + wpa_s->wnmsleep_used = 0; } static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct wpa_radio_work *work; char *pos, *end; struct os_reltime now, diff; pos = buf; end = buf + buflen; os_get_reltime(&now); dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list) { int ret; os_reltime_sub(&now, &work->time, &diff); ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n", work->type, work->wpa_s->ifname, work->freq, work->started, diff.sec, diff.usec); if (os_snprintf_error(end - pos, ret)) break; pos += ret; } return pos - buf; } static void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_radio_work *work = eloop_ctx; struct wpa_external_work *ework = work->ctx; wpa_dbg(work->wpa_s, MSG_DEBUG, "Timing out external radio work %u (%s)", ework->id, work->type); wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id); work->wpa_s->ext_work_in_progress = 0; radio_work_done(work); os_free(ework); } static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit) { struct wpa_external_work *ework = work->ctx; if (deinit) { if (work->started) eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL); os_free(ework); return; } wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)", ework->id, ework->type); wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id); work->wpa_s->ext_work_in_progress = 1; if (!ework->timeout) ework->timeout = 10; eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout, work, NULL); } static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { struct wpa_external_work *ework; char *pos, *pos2; size_t type_len; int ret; unsigned int freq = 0; /* format: [freq=] [timeout=] */ ework = os_zalloc(sizeof(*ework)); if (ework == NULL) return -1; pos = os_strchr(cmd, ' '); if (pos) { type_len = pos - cmd; pos++; pos2 = os_strstr(pos, "freq="); if (pos2) freq = atoi(pos2 + 5); pos2 = os_strstr(pos, "timeout="); if (pos2) ework->timeout = atoi(pos2 + 8); } else { type_len = os_strlen(cmd); } if (4 + type_len >= sizeof(ework->type)) type_len = sizeof(ework->type) - 4 - 1; os_strlcpy(ework->type, "ext:", sizeof(ework->type)); os_memcpy(ework->type + 4, cmd, type_len); ework->type[4 + type_len] = '\0'; wpa_s->ext_work_id++; if (wpa_s->ext_work_id == 0) wpa_s->ext_work_id++; ework->id = wpa_s->ext_work_id; if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb, ework) < 0) { os_free(ework); return -1; } ret = os_snprintf(buf, buflen, "%u", ework->id); if (os_snprintf_error(buflen, ret)) return -1; return ret; } static int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd) { struct wpa_radio_work *work; unsigned int id = atoi(cmd); dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list) { struct wpa_external_work *ework; if (os_strncmp(work->type, "ext:", 4) != 0) continue; ework = work->ctx; if (id && ework->id != id) continue; wpa_dbg(wpa_s, MSG_DEBUG, "Completed external radio work %u (%s)", ework->id, ework->type); eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL); wpa_s->ext_work_in_progress = 0; radio_work_done(work); os_free(ework); return 3; /* "OK\n" */ } return -1; } static int wpas_ctrl_radio_work(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { if (os_strcmp(cmd, "show") == 0) return wpas_ctrl_radio_work_show(wpa_s, buf, buflen); if (os_strncmp(cmd, "add ", 4) == 0) return wpas_ctrl_radio_work_add(wpa_s, cmd + 4, buf, buflen); if (os_strncmp(cmd, "done ", 5) == 0) return wpas_ctrl_radio_work_done(wpa_s, cmd + 4); return -1; } void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s) { struct wpa_radio_work *work, *tmp; if (!wpa_s || !wpa_s->radio) return; dl_list_for_each_safe(work, tmp, &wpa_s->radio->work, struct wpa_radio_work, list) { struct wpa_external_work *ework; if (os_strncmp(work->type, "ext:", 4) != 0) continue; ework = work->ctx; wpa_dbg(wpa_s, MSG_DEBUG, "Flushing%s external radio work %u (%s)", work->started ? " started" : "", ework->id, ework->type); if (work->started) eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL); radio_work_done(work); os_free(ework); } } static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; eapol_sm_notify_ctrl_response(wpa_s->eapol); } static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value, unsigned int *scan_id_count, int scan_id[]) { const char *pos = value; while (pos) { if (*pos == ' ' || *pos == '\0') break; if (*scan_id_count == MAX_SCAN_ID) return -1; scan_id[(*scan_id_count)++] = atoi(pos); pos = os_strchr(pos, ','); if (pos) pos++; } return 0; } static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, char *reply, int reply_size, int *reply_len) { char *pos; unsigned int manual_scan_passive = 0; unsigned int manual_scan_use_id = 0; unsigned int manual_scan_only_new = 0; unsigned int scan_only = 0; unsigned int scan_id_count = 0; int scan_id[MAX_SCAN_ID]; void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); int *manual_scan_freqs = NULL; struct wpa_ssid_value *ssid = NULL, *ns; unsigned int ssid_count = 0; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { *reply_len = -1; return; } if (radio_work_pending(wpa_s, "scan")) { wpa_printf(MSG_DEBUG, "Pending scan scheduled - reject new request"); *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); return; } #ifdef CONFIG_INTERWORKING if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) { wpa_printf(MSG_DEBUG, "Interworking select in progress - reject new scan"); *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); return; } #endif /* CONFIG_INTERWORKING */ if (params) { if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0) scan_only = 1; pos = os_strstr(params, "freq="); if (pos) { manual_scan_freqs = freq_range_to_channel_list(wpa_s, pos + 5); if (manual_scan_freqs == NULL) { *reply_len = -1; goto done; } } pos = os_strstr(params, "passive="); if (pos) manual_scan_passive = !!atoi(pos + 8); pos = os_strstr(params, "use_id="); if (pos) manual_scan_use_id = atoi(pos + 7); pos = os_strstr(params, "only_new=1"); if (pos) manual_scan_only_new = 1; pos = os_strstr(params, "scan_id="); if (pos && scan_id_list_parse(wpa_s, pos + 8, &scan_id_count, scan_id) < 0) { *reply_len = -1; goto done; } pos = params; while (pos && *pos != '\0') { if (os_strncmp(pos, "ssid ", 5) == 0) { char *end; pos += 5; end = pos; while (*end) { if (*end == '\0' || *end == ' ') break; end++; } ns = os_realloc_array( ssid, ssid_count + 1, sizeof(struct wpa_ssid_value)); if (ns == NULL) { *reply_len = -1; goto done; } ssid = ns; if ((end - pos) & 0x01 || end - pos > 2 * SSID_MAX_LEN || hexstr2bin(pos, ssid[ssid_count].ssid, (end - pos) / 2) < 0) { wpa_printf(MSG_DEBUG, "Invalid SSID value '%s'", pos); *reply_len = -1; goto done; } ssid[ssid_count].ssid_len = (end - pos) / 2; wpa_hexdump_ascii(MSG_DEBUG, "scan SSID", ssid[ssid_count].ssid, ssid[ssid_count].ssid_len); ssid_count++; pos = end; } pos = os_strchr(pos, ' '); if (pos) pos++; } } wpa_s->num_ssids_from_scan_req = ssid_count; os_free(wpa_s->ssids_from_scan_req); if (ssid_count) { wpa_s->ssids_from_scan_req = ssid; ssid = NULL; } else { wpa_s->ssids_from_scan_req = NULL; } if (scan_only) scan_res_handler = scan_only_handler; else if (wpa_s->scan_res_handler == scan_only_handler) scan_res_handler = NULL; else scan_res_handler = wpa_s->scan_res_handler; if (!wpa_s->sched_scanning && !wpa_s->scanning && ((wpa_s->wpa_state <= WPA_SCANNING) || (wpa_s->wpa_state == WPA_COMPLETED))) { wpa_s->manual_scan_passive = manual_scan_passive; wpa_s->manual_scan_use_id = manual_scan_use_id; wpa_s->manual_scan_only_new = manual_scan_only_new; wpa_s->scan_id_count = scan_id_count; os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int)); wpa_s->scan_res_handler = scan_res_handler; os_free(wpa_s->manual_scan_freqs); wpa_s->manual_scan_freqs = manual_scan_freqs; manual_scan_freqs = NULL; wpa_s->normal_scans = 0; wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_s->after_wps = 0; wpa_s->known_wps_freq = 0; wpa_supplicant_req_scan(wpa_s, 0, 0); if (wpa_s->manual_scan_use_id) { wpa_s->manual_scan_id++; wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u", wpa_s->manual_scan_id); *reply_len = os_snprintf(reply, reply_size, "%u\n", wpa_s->manual_scan_id); } } else if (wpa_s->sched_scanning) { wpa_s->manual_scan_passive = manual_scan_passive; wpa_s->manual_scan_use_id = manual_scan_use_id; wpa_s->manual_scan_only_new = manual_scan_only_new; wpa_s->scan_id_count = scan_id_count; os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int)); wpa_s->scan_res_handler = scan_res_handler; os_free(wpa_s->manual_scan_freqs); wpa_s->manual_scan_freqs = manual_scan_freqs; manual_scan_freqs = NULL; wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed"); wpa_supplicant_cancel_sched_scan(wpa_s); wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); if (wpa_s->manual_scan_use_id) { wpa_s->manual_scan_id++; *reply_len = os_snprintf(reply, reply_size, "%u\n", wpa_s->manual_scan_id); wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u", wpa_s->manual_scan_id); } } else { wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request"); *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); } done: os_free(manual_scan_freqs); os_free(ssid); } #ifdef CONFIG_TESTING_OPTIONS static void wpas_ctrl_iface_mgmt_tx_cb(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, enum offchannel_send_action_result result) { wpa_msg(wpa_s, MSG_INFO, "MGMT-TX-STATUS freq=%u dst=" MACSTR " src=" MACSTR " bssid=" MACSTR " result=%s", freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "NO_ACK" : "FAILED")); } static int wpas_ctrl_iface_mgmt_tx(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *param; size_t len; u8 *buf, da[ETH_ALEN], bssid[ETH_ALEN]; int res, used; int freq = 0, no_cck = 0, wait_time = 0; /* [freq=] [wait_time=] [no_cck=1] * */ wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd); pos = cmd; used = hwaddr_aton2(pos, da); if (used < 0) return -1; pos += used; while (*pos == ' ') pos++; used = hwaddr_aton2(pos, bssid); if (used < 0) return -1; pos += used; param = os_strstr(pos, " freq="); if (param) { param += 6; freq = atoi(param); } param = os_strstr(pos, " no_cck="); if (param) { param += 8; no_cck = atoi(param); } param = os_strstr(pos, " wait_time="); if (param) { param += 11; wait_time = atoi(param); } param = os_strstr(pos, " action="); if (param == NULL) return -1; param += 8; len = os_strlen(param); if (len & 1) return -1; len /= 2; buf = os_malloc(len); if (buf == NULL) return -1; if (hexstr2bin(param, buf, len) < 0) { os_free(buf); return -1; } res = offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, bssid, buf, len, wait_time, wpas_ctrl_iface_mgmt_tx_cb, no_cck); os_free(buf); return res; } static void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "External MGMT TX - done waiting"); offchannel_send_action_done(wpa_s); } static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *param; union wpa_event_data event; enum wpa_event_type ev; /* [parameters..] */ wpa_dbg(wpa_s, MSG_DEBUG, "Testing - external driver event: %s", cmd); pos = cmd; param = os_strchr(pos, ' '); if (param) *param++ = '\0'; os_memset(&event, 0, sizeof(event)); if (os_strcmp(cmd, "INTERFACE_ENABLED") == 0) { ev = EVENT_INTERFACE_ENABLED; } else if (os_strcmp(cmd, "INTERFACE_DISABLED") == 0) { ev = EVENT_INTERFACE_DISABLED; } else if (os_strcmp(cmd, "AVOID_FREQUENCIES") == 0) { ev = EVENT_AVOID_FREQUENCIES; if (param == NULL) param = ""; if (freq_range_list_parse(&event.freq_range, param) < 0) return -1; wpa_supplicant_event(wpa_s, ev, &event); os_free(event.freq_range.range); return 0; } else { wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s", cmd); return -1; } wpa_supplicant_event(wpa_s, ev, &event); return 0; } static int wpas_ctrl_iface_eapol_rx(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; u8 src[ETH_ALEN], *buf; int used; size_t len; wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd); pos = cmd; used = hwaddr_aton2(pos, src); if (used < 0) return -1; pos += used; while (*pos == ' ') pos++; len = os_strlen(pos); if (len & 1) return -1; len /= 2; buf = os_malloc(len); if (buf == NULL) return -1; if (hexstr2bin(pos, buf, len) < 0) { os_free(buf); return -1; } wpa_supplicant_rx_eapol(wpa_s, src, buf, len); os_free(buf); return 0; } static u16 ipv4_hdr_checksum(const void *buf, size_t len) { size_t i; u32 sum = 0; const u16 *pos = buf; for (i = 0; i < len / 2; i++) sum += *pos++; while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); return sum ^ 0xffff; } #define HWSIM_PACKETLEN 1500 #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_supplicant *wpa_s = ctx; const struct ether_header *eth; struct iphdr ip; const u8 *pos; unsigned int i; if (len != HWSIM_PACKETLEN) return; eth = (const struct ether_header *) buf; os_memcpy(&ip, eth + 1, sizeof(ip)); pos = &buf[sizeof(*eth) + sizeof(ip)]; if (ip.ihl != 5 || ip.version != 4 || ntohs(ip.tot_len) != HWSIM_IP_LEN) return; for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) { if (*pos != (u8) i) return; pos++; } wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR, MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost)); } static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s, char *cmd) { int enabled = atoi(cmd); if (!enabled) { if (wpa_s->l2_test) { l2_packet_deinit(wpa_s->l2_test); wpa_s->l2_test = NULL; wpa_dbg(wpa_s, MSG_DEBUG, "test data: Disabled"); } return 0; } if (wpa_s->l2_test) return 0; wpa_s->l2_test = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, ETHERTYPE_IP, wpas_data_test_rx, wpa_s, 1); if (wpa_s->l2_test == NULL) return -1; wpa_dbg(wpa_s, MSG_DEBUG, "test data: Enabled"); return 0; } static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) { u8 dst[ETH_ALEN], src[ETH_ALEN]; char *pos; int used; long int val; u8 tos; u8 buf[2 + HWSIM_PACKETLEN]; struct ether_header *eth; struct iphdr *ip; u8 *dpos; unsigned int i; if (wpa_s->l2_test == NULL) return -1; /* format: */ pos = cmd; used = hwaddr_aton2(pos, dst); if (used < 0) return -1; pos += used; while (*pos == ' ') pos++; used = hwaddr_aton2(pos, src); if (used < 0) return -1; pos += used; val = strtol(pos, NULL, 0); if (val < 0 || val > 0xff) return -1; tos = val; eth = (struct ether_header *) &buf[2]; os_memcpy(eth->ether_dhost, dst, ETH_ALEN); os_memcpy(eth->ether_shost, src, ETH_ALEN); eth->ether_type = htons(ETHERTYPE_IP); ip = (struct iphdr *) (eth + 1); os_memset(ip, 0, sizeof(*ip)); ip->ihl = 5; ip->version = 4; ip->ttl = 64; ip->tos = tos; ip->tot_len = htons(HWSIM_IP_LEN); ip->protocol = 1; ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1); ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2); ip->check = ipv4_hdr_checksum(ip, sizeof(*ip)); dpos = (u8 *) (ip + 1); for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) *dpos++ = i; if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2], HWSIM_PACKETLEN) < 0) return -1; wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX dst=" MACSTR " src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos); return 0; } static int wpas_ctrl_iface_data_test_frame(struct wpa_supplicant *wpa_s, char *cmd) { u8 *buf; struct ether_header *eth; struct l2_packet_data *l2 = NULL; size_t len; u16 ethertype; int res = -1; len = os_strlen(cmd); if (len & 1 || len < ETH_HLEN * 2) return -1; len /= 2; buf = os_malloc(len); if (buf == NULL) return -1; if (hexstr2bin(cmd, buf, len) < 0) goto done; eth = (struct ether_header *) buf; ethertype = ntohs(eth->ether_type); l2 = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, ethertype, wpas_data_test_rx, wpa_s, 1); if (l2 == NULL) goto done; res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len); wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX frame res=%d", res); done: if (l2) l2_packet_deinit(l2); os_free(buf); return res < 0 ? -1 : 0; } static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd) { #ifdef WPA_TRACE_BFD extern char wpa_trace_fail_func[256]; extern unsigned int wpa_trace_fail_after; char *pos; wpa_trace_fail_after = atoi(cmd); pos = os_strchr(cmd, ':'); if (pos) { pos++; os_strlcpy(wpa_trace_fail_func, pos, sizeof(wpa_trace_fail_func)); } else { wpa_trace_fail_after = 0; } return 0; #else /* WPA_TRACE_BFD */ return -1; #endif /* WPA_TRACE_BFD */ } static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD extern char wpa_trace_fail_func[256]; extern unsigned int wpa_trace_fail_after; return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after, wpa_trace_fail_func); #else /* WPA_TRACE_BFD */ return -1; #endif /* WPA_TRACE_BFD */ } static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd) { #ifdef WPA_TRACE_BFD extern char wpa_trace_test_fail_func[256]; extern unsigned int wpa_trace_test_fail_after; char *pos; wpa_trace_test_fail_after = atoi(cmd); pos = os_strchr(cmd, ':'); if (pos) { pos++; os_strlcpy(wpa_trace_test_fail_func, pos, sizeof(wpa_trace_test_fail_func)); } else { wpa_trace_test_fail_after = 0; } return 0; #else /* WPA_TRACE_BFD */ return -1; #endif /* WPA_TRACE_BFD */ } static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD extern char wpa_trace_test_fail_func[256]; extern unsigned int wpa_trace_test_fail_after; return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after, wpa_trace_test_fail_func); #else /* WPA_TRACE_BFD */ return -1; #endif /* WPA_TRACE_BFD */ } #endif /* CONFIG_TESTING_OPTIONS */ static void wpas_ctrl_vendor_elem_update(struct wpa_supplicant *wpa_s) { unsigned int i; char buf[30]; wpa_printf(MSG_DEBUG, "Update vendor elements"); for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) { if (wpa_s->vendor_elem[i]) { int res; res = os_snprintf(buf, sizeof(buf), "frame[%u]", i); if (!os_snprintf_error(sizeof(buf), res)) { wpa_hexdump_buf(MSG_DEBUG, buf, wpa_s->vendor_elem[i]); } } } #ifdef CONFIG_P2P if (wpa_s->parent == wpa_s && wpa_s->global->p2p && !wpa_s->global->p2p_disabled) p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem); #endif /* CONFIG_P2P */ } static struct wpa_supplicant * wpas_ctrl_vendor_elem_iface(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame) { switch (frame) { #ifdef CONFIG_P2P case VENDOR_ELEM_PROBE_REQ_P2P: case VENDOR_ELEM_PROBE_RESP_P2P: case VENDOR_ELEM_PROBE_RESP_P2P_GO: case VENDOR_ELEM_BEACON_P2P_GO: case VENDOR_ELEM_P2P_PD_REQ: case VENDOR_ELEM_P2P_PD_RESP: case VENDOR_ELEM_P2P_GO_NEG_REQ: case VENDOR_ELEM_P2P_GO_NEG_RESP: case VENDOR_ELEM_P2P_GO_NEG_CONF: case VENDOR_ELEM_P2P_INV_REQ: case VENDOR_ELEM_P2P_INV_RESP: case VENDOR_ELEM_P2P_ASSOC_REQ: return wpa_s->parent; #endif /* CONFIG_P2P */ default: return wpa_s; } } static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) { char *pos = cmd; int frame; size_t len; struct wpabuf *buf; struct ieee802_11_elems elems; frame = atoi(pos); if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); pos = os_strchr(pos, ' '); if (pos == NULL) return -1; pos++; len = os_strlen(pos); if (len == 0) return 0; if (len & 1) return -1; len /= 2; buf = wpabuf_alloc(len); if (buf == NULL) return -1; if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) { wpabuf_free(buf); return -1; } if (ieee802_11_parse_elems(wpabuf_head_u8(buf), len, &elems, 0) == ParseFailed) { wpabuf_free(buf); return -1; } if (wpa_s->vendor_elem[frame] == NULL) { wpa_s->vendor_elem[frame] = buf; wpas_ctrl_vendor_elem_update(wpa_s); return 0; } if (wpabuf_resize(&wpa_s->vendor_elem[frame], len) < 0) { wpabuf_free(buf); return -1; } wpabuf_put_buf(wpa_s->vendor_elem[frame], buf); wpabuf_free(buf); wpas_ctrl_vendor_elem_update(wpa_s); return 0; } static int wpas_ctrl_vendor_elem_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int frame = atoi(cmd); if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); if (wpa_s->vendor_elem[frame] == NULL) return 0; return wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(wpa_s->vendor_elem[frame]), wpabuf_len(wpa_s->vendor_elem[frame])); } static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd) { char *pos = cmd; int frame; size_t len; u8 *buf; struct ieee802_11_elems elems; u8 *ie, *end; frame = atoi(pos); if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); pos = os_strchr(pos, ' '); if (pos == NULL) return -1; pos++; if (*pos == '*') { wpabuf_free(wpa_s->vendor_elem[frame]); wpa_s->vendor_elem[frame] = NULL; wpas_ctrl_vendor_elem_update(wpa_s); return 0; } if (wpa_s->vendor_elem[frame] == NULL) return -1; len = os_strlen(pos); if (len == 0) return 0; if (len & 1) return -1; len /= 2; buf = os_malloc(len); if (buf == NULL) return -1; if (hexstr2bin(pos, buf, len) < 0) { os_free(buf); return -1; } if (ieee802_11_parse_elems(buf, len, &elems, 0) == ParseFailed) { os_free(buf); return -1; } ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]); end = ie + wpabuf_len(wpa_s->vendor_elem[frame]); for (; ie + 1 < end; ie += 2 + ie[1]) { if (ie + len > end) break; if (os_memcmp(ie, buf, len) != 0) continue; if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) { wpabuf_free(wpa_s->vendor_elem[frame]); wpa_s->vendor_elem[frame] = NULL; } else { os_memmove(ie, ie + len, end - (ie + len)); wpa_s->vendor_elem[frame]->used -= len; } os_free(buf); wpas_ctrl_vendor_elem_update(wpa_s); return 0; } os_free(buf); return -1; } static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep) { struct wpa_supplicant *wpa_s = ctx; if (neighbor_rep) { wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED "length=%u", (unsigned int) wpabuf_len(neighbor_rep)); wpabuf_free(neighbor_rep); } else { wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED); } } static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s, char *cmd) { struct wpa_ssid ssid; struct wpa_ssid *ssid_p = NULL; int ret = 0; if (os_strncmp(cmd, " ssid=", 6) == 0) { ssid.ssid_len = os_strlen(cmd + 6); if (ssid.ssid_len > SSID_MAX_LEN) return -1; ssid.ssid = (u8 *) (cmd + 6); ssid_p = &ssid; } ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p, wpas_ctrl_neighbor_rep_cb, wpa_s); return ret; } static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s) { eapol_sm_erp_flush(wpa_s->eapol); return 0; } static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, char *cmd) { char *token, *context = NULL; unsigned int enable = ~0, type = 0; u8 _addr[ETH_ALEN], _mask[ETH_ALEN]; u8 *addr = NULL, *mask = NULL; while ((token = str_token(cmd, " ", &context))) { if (os_strcasecmp(token, "scan") == 0) { type |= MAC_ADDR_RAND_SCAN; } else if (os_strcasecmp(token, "sched") == 0) { type |= MAC_ADDR_RAND_SCHED_SCAN; } else if (os_strcasecmp(token, "pno") == 0) { type |= MAC_ADDR_RAND_PNO; } else if (os_strcasecmp(token, "all") == 0) { type = wpa_s->mac_addr_rand_supported; } else if (os_strncasecmp(token, "enable=", 7) == 0) { enable = atoi(token + 7); } else if (os_strncasecmp(token, "addr=", 5) == 0) { addr = _addr; if (hwaddr_aton(token + 5, addr)) { wpa_printf(MSG_INFO, "CTRL: Invalid MAC address: %s", token); return -1; } } else if (os_strncasecmp(token, "mask=", 5) == 0) { mask = _mask; if (hwaddr_aton(token + 5, mask)) { wpa_printf(MSG_INFO, "CTRL: Invalid MAC address mask: %s", token); return -1; } } else { wpa_printf(MSG_INFO, "CTRL: Invalid MAC_RAND_SCAN parameter: %s", token); return -1; } } if (!type) { wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN no type specified"); return -1; } if ((wpa_s->mac_addr_rand_supported & type) != type) { wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN types=%u != supported=%u", type, wpa_s->mac_addr_rand_supported); return -1; } if (enable > 1) { wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN enable=<0/1> not specified"); return -1; } if (!enable) { wpas_mac_addr_rand_scan_clear(wpa_s, type); if (wpa_s->pno) { if (type & MAC_ADDR_RAND_PNO) { wpas_stop_pno(wpa_s); wpas_start_pno(wpa_s); } } else if (wpa_s->sched_scanning && (type & MAC_ADDR_RAND_SCHED_SCAN)) { /* simulate timeout to restart the sched scan */ wpa_s->sched_scan_timed_out = 1; wpa_s->prev_sched_ssid = NULL; wpa_supplicant_cancel_sched_scan(wpa_s); } return 0; } if ((addr && !mask) || (!addr && mask)) { wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN invalid addr/mask combination"); return -1; } if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) { wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN cannot allow multicast address"); return -1; } if (type & MAC_ADDR_RAND_SCAN) { wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN, addr, mask); } if (type & MAC_ADDR_RAND_SCHED_SCAN) { wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN, addr, mask); if (wpa_s->sched_scanning && !wpa_s->pno) { /* simulate timeout to restart the sched scan */ wpa_s->sched_scan_timed_out = 1; wpa_s->prev_sched_ssid = NULL; wpa_supplicant_cancel_sched_scan(wpa_s); } } if (type & MAC_ADDR_RAND_PNO) { wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO, addr, mask); if (wpa_s->pno) { wpas_stop_pno(wpa_s); wpas_start_pno(wpa_s); } } return 0; } static int wpas_ctrl_cmd_debug_level(const char *cmd) { if (os_strcmp(cmd, "PING") == 0 || os_strncmp(cmd, "BSS ", 4) == 0 || os_strncmp(cmd, "GET_NETWORK ", 12) == 0 || os_strncmp(cmd, "STATUS", 6) == 0 || os_strncmp(cmd, "STA ", 4) == 0 || os_strncmp(cmd, "STA-", 4) == 0) return MSG_EXCESSIVE; return MSG_DEBUG; } char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { char *reply; const int reply_size = 4096; int reply_len; if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 || os_strncmp(buf, "SET_NETWORK ", 12) == 0) { if (wpa_debug_show_keys) wpa_dbg(wpa_s, MSG_DEBUG, "Control interface command '%s'", buf); else wpa_dbg(wpa_s, MSG_DEBUG, "Control interface command '%s [REMOVED]'", os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ? WPA_CTRL_RSP : "SET_NETWORK"); } else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 || os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0) { wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", (const u8 *) buf, os_strlen(buf)); } else { int level = wpas_ctrl_cmd_debug_level(buf); wpa_dbg(wpa_s, level, "Control interface command '%s'", buf); } reply = os_malloc(reply_size); if (reply == NULL) { *resp_len = 1; return NULL; } os_memcpy(reply, "OK\n", 3); reply_len = 3; if (os_strcmp(buf, "PING") == 0) { os_memcpy(reply, "PONG\n", 5); reply_len = 5; } else if (os_strcmp(buf, "IFNAME") == 0) { reply_len = os_strlen(wpa_s->ifname); os_memcpy(reply, wpa_s->ifname, reply_len); } else if (os_strncmp(buf, "RELOG", 5) == 0) { if (wpa_debug_reopen_file() < 0) reply_len = -1; } else if (os_strncmp(buf, "NOTE ", 5) == 0) { wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); } else if (os_strcmp(buf, "MIB") == 0) { reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); if (reply_len >= 0) { reply_len += eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, reply_size - reply_len); } } else if (os_strncmp(buf, "STATUS", 6) == 0) { reply_len = wpa_supplicant_ctrl_iface_status( wpa_s, buf + 6, reply, reply_size); } else if (os_strcmp(buf, "PMKSA") == 0) { reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply, reply_size); } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); } else if (os_strncmp(buf, "SET ", 4) == 0) { if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) reply_len = -1; } else if (os_strncmp(buf, "DUMP", 4) == 0) { reply_len = wpa_config_dump_values(wpa_s->conf, reply, reply_size); } else if (os_strncmp(buf, "GET ", 4) == 0) { reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4, reply, reply_size); } else if (os_strcmp(buf, "LOGON") == 0) { eapol_sm_notify_logoff(wpa_s->eapol, FALSE); } else if (os_strcmp(buf, "LOGOFF") == 0) { eapol_sm_notify_logoff(wpa_s->eapol, TRUE); } else if (os_strcmp(buf, "REASSOCIATE") == 0) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) reply_len = -1; else wpas_request_connection(wpa_s); } else if (os_strcmp(buf, "REATTACH") == 0) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED || !wpa_s->current_ssid) reply_len = -1; else { wpa_s->reattach = 1; wpas_request_connection(wpa_s); } } else if (os_strcmp(buf, "RECONNECT") == 0) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) reply_len = -1; else if (wpa_s->disconnected) wpas_request_connection(wpa_s); #ifdef IEEE8021X_EAPOL } else if (os_strncmp(buf, "PREAUTH ", 8) == 0) { if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8)) reply_len = -1; #endif /* IEEE8021X_EAPOL */ #ifdef CONFIG_PEERKEY } else if (os_strncmp(buf, "STKSTART ", 9) == 0) { if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9)) reply_len = -1; #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211R } else if (os_strncmp(buf, "FT_DS ", 6) == 0) { if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6)) reply_len = -1; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_WPS } else if (os_strcmp(buf, "WPS_PBC") == 0) { int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL); if (res == -2) { os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); reply_len = 17; } else if (res) reply_len = -1; } else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) { int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8); if (res == -2) { os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); reply_len = 17; } else if (res) reply_len = -1; } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8, reply, reply_size); } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_check_pin( wpa_s, buf + 14, reply, reply_size); } else if (os_strcmp(buf, "WPS_CANCEL") == 0) { if (wpas_wps_cancel(wpa_s)) reply_len = -1; #ifdef CONFIG_WPS_NFC } else if (os_strcmp(buf, "WPS_NFC") == 0) { if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL)) reply_len = -1; } else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8)) reply_len = -1; } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_nfc_config_token( wpa_s, buf + 21, reply, reply_size); } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token( wpa_s, buf + 14, reply, reply_size); } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) { if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s, buf + 17)) reply_len = -1; } else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) { reply_len = wpas_ctrl_nfc_get_handover_req( wpa_s, buf + 21, reply, reply_size); } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) { reply_len = wpas_ctrl_nfc_get_handover_sel( wpa_s, buf + 21, reply, reply_size); } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) { if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20)) reply_len = -1; #endif /* CONFIG_WPS_NFC */ } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) reply_len = -1; #ifdef CONFIG_AP } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin( wpa_s, buf + 11, reply, reply_size); #endif /* CONFIG_AP */ #ifdef CONFIG_WPS_ER } else if (os_strcmp(buf, "WPS_ER_START") == 0) { if (wpas_wps_er_start(wpa_s, NULL)) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_START ", 13) == 0) { if (wpas_wps_er_start(wpa_s, buf + 13)) reply_len = -1; } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) { wpas_wps_er_stop(wpa_s); } else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11)) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) { int ret = wpas_wps_er_pbc(wpa_s, buf + 11); if (ret == -2) { os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); reply_len = 17; } else if (ret == -3) { os_memcpy(reply, "FAIL-UNKNOWN-UUID\n", 18); reply_len = 18; } else if (ret == -4) { os_memcpy(reply, "FAIL-NO-AP-SETTINGS\n", 20); reply_len = 20; } else if (ret) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13)) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_SET_CONFIG ", 18) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_set_config(wpa_s, buf + 18)) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14)) reply_len = -1; #ifdef CONFIG_WPS_NFC } else if (os_strncmp(buf, "WPS_ER_NFC_CONFIG_TOKEN ", 24) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_er_nfc_config_token( wpa_s, buf + 24, reply, reply_size); #endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_ER */ #endif /* CONFIG_WPS */ #ifdef CONFIG_IBSS_RSN } else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) { if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9)) reply_len = -1; #endif /* CONFIG_IBSS_RSN */ #ifdef CONFIG_MESH } else if (os_strncmp(buf, "MESH_INTERFACE_ADD ", 19) == 0) { reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add( wpa_s, buf + 19, reply, reply_size); } else if (os_strcmp(buf, "MESH_INTERFACE_ADD") == 0) { reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add( wpa_s, "", reply, reply_size); } else if (os_strncmp(buf, "MESH_GROUP_ADD ", 15) == 0) { if (wpa_supplicant_ctrl_iface_mesh_group_add(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "MESH_GROUP_REMOVE ", 18) == 0) { if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s, buf + 18)) reply_len = -1; #endif /* CONFIG_MESH */ #ifdef CONFIG_P2P } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) { if (p2p_ctrl_find(wpa_s, buf + 8)) reply_len = -1; } else if (os_strcmp(buf, "P2P_FIND") == 0) { if (p2p_ctrl_find(wpa_s, "")) reply_len = -1; } else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) { wpas_p2p_stop_find(wpa_s); } else if (os_strncmp(buf, "P2P_ASP_PROVISION ", 18) == 0) { if (p2p_ctrl_asp_provision(wpa_s, buf + 18)) reply_len = -1; } else if (os_strncmp(buf, "P2P_ASP_PROVISION_RESP ", 23) == 0) { if (p2p_ctrl_asp_provision_resp(wpa_s, buf + 23)) reply_len = -1; } else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) { reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply, reply_size); } else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) { if (p2p_ctrl_listen(wpa_s, buf + 11)) reply_len = -1; } else if (os_strcmp(buf, "P2P_LISTEN") == 0) { if (p2p_ctrl_listen(wpa_s, "")) reply_len = -1; } else if (os_strncmp(buf, "P2P_GROUP_REMOVE ", 17) == 0) { if (wpas_p2p_group_remove(wpa_s, buf + 17)) reply_len = -1; } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) { if (p2p_ctrl_group_add(wpa_s, "")) reply_len = -1; } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) { if (p2p_ctrl_group_add(wpa_s, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) { if (p2p_ctrl_prov_disc(wpa_s, buf + 14)) reply_len = -1; } else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) { reply_len = p2p_get_passphrase(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) { reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply, reply_size); } else if (os_strncmp(buf, "P2P_SERV_DISC_CANCEL_REQ ", 25) == 0) { if (p2p_ctrl_serv_disc_cancel_req(wpa_s, buf + 25) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_SERV_DISC_RESP ", 19) == 0) { if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) { wpas_p2p_sd_service_update(wpa_s); } else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) { if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_SERVICE_FLUSH") == 0) { wpas_p2p_service_flush(wpa_s); } else if (os_strncmp(buf, "P2P_SERVICE_ADD ", 16) == 0) { if (p2p_ctrl_service_add(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) { if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_SERVICE_REP ", 16) == 0) { if (p2p_ctrl_service_replace(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) { if (p2p_ctrl_reject(wpa_s, buf + 11) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_INVITE ", 11) == 0) { if (p2p_ctrl_invite(wpa_s, buf + 11) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_PEER ", 9) == 0) { reply_len = p2p_ctrl_peer(wpa_s, buf + 9, reply, reply_size); } else if (os_strncmp(buf, "P2P_SET ", 8) == 0) { if (p2p_ctrl_set(wpa_s, buf + 8) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_FLUSH") == 0) { p2p_ctrl_flush(wpa_s); } else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) { if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_CANCEL") == 0) { if (wpas_p2p_cancel(wpa_s)) reply_len = -1; } else if (os_strncmp(buf, "P2P_PRESENCE_REQ ", 17) == 0) { if (p2p_ctrl_presence_req(wpa_s, buf + 17) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_PRESENCE_REQ") == 0) { if (p2p_ctrl_presence_req(wpa_s, "") < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_EXT_LISTEN ", 15) == 0) { if (p2p_ctrl_ext_listen(wpa_s, buf + 15) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) { if (p2p_ctrl_ext_listen(wpa_s, "") < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) { if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0) reply_len = -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) { if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) { reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16, reply, reply_size); #endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_INTERWORKING } else if (os_strcmp(buf, "FETCH_ANQP") == 0) { if (interworking_fetch_anqp(wpa_s) < 0) reply_len = -1; } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) { interworking_stop_fetch_anqp(wpa_s); } else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) { if (ctrl_interworking_select(wpa_s, NULL) < 0) reply_len = -1; } else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) { if (ctrl_interworking_select(wpa_s, buf + 20) < 0) reply_len = -1; } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) { if (ctrl_interworking_connect(wpa_s, buf + 21, 0) < 0) reply_len = -1; } else if (os_strncmp(buf, "INTERWORKING_ADD_NETWORK ", 25) == 0) { int id; id = ctrl_interworking_connect(wpa_s, buf + 25, 1); if (id < 0) reply_len = -1; else { reply_len = os_snprintf(reply, reply_size, "%d\n", id); if (os_snprintf_error(reply_size, reply_len)) reply_len = -1; } } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) { if (get_anqp(wpa_s, buf + 9) < 0) reply_len = -1; } else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) { if (gas_request(wpa_s, buf + 12) < 0) reply_len = -1; } else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) { reply_len = gas_response_get(wpa_s, buf + 17, reply, reply_size); #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 } else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) { if (get_hs20_anqp(wpa_s, buf + 14) < 0) reply_len = -1; } else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) { if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0) reply_len = -1; } else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) { if (hs20_icon_request(wpa_s, buf + 18) < 0) reply_len = -1; } else if (os_strcmp(buf, "FETCH_OSU") == 0) { if (hs20_fetch_osu(wpa_s) < 0) reply_len = -1; } else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) { hs20_cancel_fetch_osu(wpa_s); #endif /* CONFIG_HS20 */ } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0) { if (wpa_supplicant_ctrl_iface_ctrl_rsp( wpa_s, buf + os_strlen(WPA_CTRL_RSP))) reply_len = -1; else { /* * Notify response from timeout to allow the control * interface response to be sent first. */ eloop_register_timeout(0, 0, wpas_ctrl_eapol_response, wpa_s, NULL); } } else if (os_strcmp(buf, "RECONFIGURE") == 0) { if (wpa_supplicant_reload_configuration(wpa_s)) reply_len = -1; } else if (os_strcmp(buf, "TERMINATE") == 0) { wpa_supplicant_terminate_proc(wpa_s->global); } else if (os_strncmp(buf, "BSSID ", 6) == 0) { if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6)) reply_len = -1; } else if (os_strncmp(buf, "BLACKLIST", 9) == 0) { reply_len = wpa_supplicant_ctrl_iface_blacklist( wpa_s, buf + 9, reply, reply_size); } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) { reply_len = wpa_supplicant_ctrl_iface_log_level( wpa_s, buf + 9, reply, reply_size); } else if (os_strncmp(buf, "LIST_NETWORKS ", 14) == 0) { reply_len = wpa_supplicant_ctrl_iface_list_networks( wpa_s, buf + 14, reply, reply_size); } else if (os_strcmp(buf, "LIST_NETWORKS") == 0) { reply_len = wpa_supplicant_ctrl_iface_list_networks( wpa_s, NULL, reply, reply_size); } else if (os_strcmp(buf, "DISCONNECT") == 0) { #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; #endif /* CONFIG_SME */ wpa_s->reassociate = 0; wpa_s->disconnected = 1; wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); } else if (os_strcmp(buf, "SCAN") == 0) { wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len); } else if (os_strncmp(buf, "SCAN ", 5) == 0) { wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len); } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) { reply_len = wpa_supplicant_ctrl_iface_scan_results( wpa_s, reply, reply_size); } else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) { if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) { if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) { if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16)) reply_len = -1; } else if (os_strcmp(buf, "ADD_NETWORK") == 0) { reply_len = wpa_supplicant_ctrl_iface_add_network( wpa_s, reply, reply_size); } else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) { if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) { if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12)) reply_len = -1; } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) { reply_len = wpa_supplicant_ctrl_iface_get_network( wpa_s, buf + 12, reply, reply_size); } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12, wpa_s)) reply_len = -1; } else if (os_strcmp(buf, "LIST_CREDS") == 0) { reply_len = wpa_supplicant_ctrl_iface_list_creds( wpa_s, reply, reply_size); } else if (os_strcmp(buf, "ADD_CRED") == 0) { reply_len = wpa_supplicant_ctrl_iface_add_cred( wpa_s, reply, reply_size); } else if (os_strncmp(buf, "REMOVE_CRED ", 12) == 0) { if (wpa_supplicant_ctrl_iface_remove_cred(wpa_s, buf + 12)) reply_len = -1; } else if (os_strncmp(buf, "SET_CRED ", 9) == 0) { if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9)) reply_len = -1; } else if (os_strncmp(buf, "GET_CRED ", 9) == 0) { reply_len = wpa_supplicant_ctrl_iface_get_cred(wpa_s, buf + 9, reply, reply_size); #ifndef CONFIG_NO_CONFIG_WRITE } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { if (wpa_supplicant_ctrl_iface_save_config(wpa_s)) reply_len = -1; #endif /* CONFIG_NO_CONFIG_WRITE */ } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) { reply_len = wpa_supplicant_ctrl_iface_get_capability( wpa_s, buf + 15, reply, reply_size); } else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) { if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8)) reply_len = -1; } else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) { if (wpa_supplicant_ctrl_iface_scan_interval(wpa_s, buf + 14)) reply_len = -1; } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { reply_len = wpa_supplicant_global_iface_list( wpa_s->global, reply, reply_size); } else if (os_strcmp(buf, "INTERFACES") == 0) { reply_len = wpa_supplicant_global_iface_interfaces( wpa_s->global, reply, reply_size); } else if (os_strncmp(buf, "BSS ", 4) == 0) { reply_len = wpa_supplicant_ctrl_iface_bss( wpa_s, buf + 4, reply, reply_size); #ifdef CONFIG_AP } else if (os_strcmp(buf, "STA-FIRST") == 0) { reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "STA ", 4) == 0) { reply_len = ap_ctrl_iface_sta(wpa_s, buf + 4, reply, reply_size); } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) { reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply, reply_size); } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) { if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) { if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13)) reply_len = -1; } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12)) reply_len = -1; } else if (os_strcmp(buf, "STOP_AP") == 0) { if (wpas_ap_stop_ap(wpa_s)) reply_len = -1; #endif /* CONFIG_AP */ } else if (os_strcmp(buf, "SUSPEND") == 0) { wpas_notify_suspend(wpa_s->global); } else if (os_strcmp(buf, "RESUME") == 0) { wpas_notify_resume(wpa_s->global); #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcmp(buf, "DROP_SA") == 0) { wpa_supplicant_ctrl_iface_drop_sa(wpa_s); #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "ROAM ", 5) == 0) { if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5)) reply_len = -1; } else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) { wpa_s->auto_reconnect_disabled = atoi(buf + 16) == 0; } else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) { if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "BSS_EXPIRE_COUNT ", 17) == 0) { if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s, buf + 17)) reply_len = -1; } else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) { wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10); #ifdef CONFIG_TDLS } else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) { if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "TDLS_SETUP ", 11) == 0) { if (wpa_supplicant_ctrl_iface_tdls_setup(wpa_s, buf + 11)) reply_len = -1; } else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) { if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "TDLS_CHAN_SWITCH ", 17) == 0) { if (wpa_supplicant_ctrl_iface_tdls_chan_switch(wpa_s, buf + 17)) reply_len = -1; } else if (os_strncmp(buf, "TDLS_CANCEL_CHAN_SWITCH ", 24) == 0) { if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s, buf + 24)) reply_len = -1; } else if (os_strncmp(buf, "TDLS_LINK_STATUS ", 17) == 0) { reply_len = wpa_supplicant_ctrl_iface_tdls_link_status( wpa_s, buf + 17, reply, reply_size); #endif /* CONFIG_TDLS */ } else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) { reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "WMM_AC_ADDTS ", 13) == 0) { if (wmm_ac_ctrl_addts(wpa_s, buf + 13)) reply_len = -1; } else if (os_strncmp(buf, "WMM_AC_DELTS ", 13) == 0) { if (wmm_ac_ctrl_delts(wpa_s, buf + 13)) reply_len = -1; } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) { reply_len = wpa_supplicant_signal_poll(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) { reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply, reply_size); #ifdef CONFIG_AUTOSCAN } else if (os_strncmp(buf, "AUTOSCAN ", 9) == 0) { if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9)) reply_len = -1; #endif /* CONFIG_AUTOSCAN */ #ifdef ANDROID } else if (os_strncmp(buf, "DRIVER ", 7) == 0) { reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply, reply_size); #endif /* ANDROID */ } else if (os_strncmp(buf, "VENDOR ", 7) == 0) { reply_len = wpa_supplicant_vendor_cmd(wpa_s, buf + 7, reply, reply_size); } else if (os_strcmp(buf, "REAUTHENTICATE") == 0) { pmksa_cache_clear_current(wpa_s->wpa); eapol_sm_request_reauth(wpa_s->eapol); #ifdef CONFIG_WNM } else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) { if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10)) reply_len = -1; } else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) { if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14)) reply_len = -1; #endif /* CONFIG_WNM */ } else if (os_strcmp(buf, "FLUSH") == 0) { wpa_supplicant_ctrl_iface_flush(wpa_s); } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) { reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply, reply_size); #ifdef CONFIG_TESTING_OPTIONS } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { if (wpas_ctrl_iface_mgmt_tx(wpa_s, buf + 8) < 0) reply_len = -1; } else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) { wpas_ctrl_iface_mgmt_tx_done(wpa_s); } else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) { if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0) reply_len = -1; } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) { if (wpas_ctrl_iface_eapol_rx(wpa_s, buf + 9) < 0) reply_len = -1; } else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) { if (wpas_ctrl_iface_data_test_config(wpa_s, buf + 17) < 0) reply_len = -1; } else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) { if (wpas_ctrl_iface_data_test_tx(wpa_s, buf + 13) < 0) reply_len = -1; } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) { if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) { if (wpas_ctrl_test_alloc_fail(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) { reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) { if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0) reply_len = -1; } else if (os_strcmp(buf, "GET_FAIL") == 0) { reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size); #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "VENDOR_ELEM_GET ", 16) == 0) { reply_len = wpas_ctrl_vendor_elem_get(wpa_s, buf + 16, reply, reply_size); } else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) { if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0) reply_len = -1; } else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) { if (wpas_ctrl_iface_send_neigbor_rep(wpa_s, buf + 20)) reply_len = -1; } else if (os_strcmp(buf, "ERP_FLUSH") == 0) { wpas_ctrl_iface_erp_flush(wpa_s); } else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) { if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) { reply_len = wpas_ctrl_iface_get_pref_freq_list( wpa_s, buf + 19, reply, reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; } if (reply_len < 0) { os_memcpy(reply, "FAIL\n", 5); reply_len = 5; } *resp_len = reply_len; return reply; } static int wpa_supplicant_global_iface_add(struct wpa_global *global, char *cmd) { struct wpa_interface iface; char *pos, *extra; struct wpa_supplicant *wpa_s; unsigned int create_iface = 0; u8 mac_addr[ETH_ALEN]; /* * TABTABTABTAB * TAB[TAB] */ wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); os_memset(&iface, 0, sizeof(iface)); do { iface.ifname = pos = cmd; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.ifname[0] == '\0') return -1; if (pos == NULL) break; iface.confname = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.confname[0] == '\0') iface.confname = NULL; if (pos == NULL) break; iface.driver = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.driver[0] == '\0') iface.driver = NULL; if (pos == NULL) break; iface.ctrl_interface = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.ctrl_interface[0] == '\0') iface.ctrl_interface = NULL; if (pos == NULL) break; iface.driver_param = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.driver_param[0] == '\0') iface.driver_param = NULL; if (pos == NULL) break; iface.bridge_ifname = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.bridge_ifname[0] == '\0') iface.bridge_ifname = NULL; if (pos == NULL) break; extra = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (!extra[0]) break; if (os_strcmp(extra, "create") == 0) create_iface = 1; else { wpa_printf(MSG_DEBUG, "INTERFACE_ADD unsupported extra parameter: '%s'", extra); return -1; } } while (0); if (create_iface) { wpa_printf(MSG_DEBUG, "CTRL_IFACE creating interface '%s'", iface.ifname); if (!global->ifaces) return -1; if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname, NULL, NULL, NULL, mac_addr, NULL) < 0) { wpa_printf(MSG_ERROR, "CTRL_IFACE interface creation failed"); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE interface '%s' created with MAC addr: " MACSTR, iface.ifname, MAC2STR(mac_addr)); } if (wpa_supplicant_get_iface(global, iface.ifname)) goto fail; wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); if (!wpa_s) goto fail; wpa_s->added_vif = create_iface; return 0; fail: if (create_iface) wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname); return -1; } static int wpa_supplicant_global_iface_remove(struct wpa_global *global, char *cmd) { struct wpa_supplicant *wpa_s; int ret; unsigned int delete_iface; wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd); wpa_s = wpa_supplicant_get_iface(global, cmd); if (wpa_s == NULL) return -1; delete_iface = wpa_s->added_vif; ret = wpa_supplicant_remove_iface(global, wpa_s, 0); if (!ret && delete_iface) { wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'", cmd); ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd); } return ret; } static void wpa_free_iface_info(struct wpa_interface_info *iface) { struct wpa_interface_info *prev; while (iface) { prev = iface; iface = iface->next; os_free(prev->ifname); os_free(prev->desc); os_free(prev); } } static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len) { int i, res; struct wpa_interface_info *iface = NULL, *last = NULL, *tmp; char *pos, *end; for (i = 0; wpa_drivers[i]; i++) { const struct wpa_driver_ops *drv = wpa_drivers[i]; if (drv->get_interfaces == NULL) continue; tmp = drv->get_interfaces(global->drv_priv[i]); if (tmp == NULL) continue; if (last == NULL) iface = last = tmp; else last->next = tmp; while (last->next) last = last->next; } pos = buf; end = buf + len; for (tmp = iface; tmp; tmp = tmp->next) { res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n", tmp->drv_name, tmp->ifname, tmp->desc ? tmp->desc : ""); if (os_snprintf_error(end - pos, res)) { *pos = '\0'; break; } pos += res; } wpa_free_iface_info(iface); return pos - buf; } static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, char *buf, int len) { int res; char *pos, *end; struct wpa_supplicant *wpa_s; wpa_s = global->ifaces; pos = buf; end = buf + len; while (wpa_s) { res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname); if (os_snprintf_error(end - pos, res)) { *pos = '\0'; break; } pos += res; wpa_s = wpa_s->next; } return pos - buf; } static char * wpas_global_ctrl_iface_ifname(struct wpa_global *global, const char *ifname, char *cmd, size_t *resp_len) { struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_strcmp(ifname, wpa_s->ifname) == 0) break; } if (wpa_s == NULL) { char *resp = os_strdup("FAIL-NO-IFNAME-MATCH\n"); if (resp) *resp_len = os_strlen(resp); else *resp_len = 1; return resp; } return wpa_supplicant_ctrl_iface_process(wpa_s, cmd, resp_len); } static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, char *buf, size_t *resp_len) { #ifdef CONFIG_P2P static const char * cmd[] = { "LIST_NETWORKS", "P2P_FIND", "P2P_STOP_FIND", "P2P_LISTEN", "P2P_GROUP_ADD", "P2P_GET_PASSPHRASE", "P2P_SERVICE_UPDATE", "P2P_SERVICE_FLUSH", "P2P_FLUSH", "P2P_CANCEL", "P2P_PRESENCE_REQ", "P2P_EXT_LISTEN", NULL }; static const char * prefix[] = { #ifdef ANDROID "DRIVER ", #endif /* ANDROID */ "GET_NETWORK ", "REMOVE_NETWORK ", "P2P_FIND ", "P2P_CONNECT ", "P2P_LISTEN ", "P2P_GROUP_REMOVE ", "P2P_GROUP_ADD ", "P2P_PROV_DISC ", "P2P_SERV_DISC_REQ ", "P2P_SERV_DISC_CANCEL_REQ ", "P2P_SERV_DISC_RESP ", "P2P_SERV_DISC_EXTERNAL ", "P2P_SERVICE_ADD ", "P2P_SERVICE_DEL ", "P2P_SERVICE_REP ", "P2P_REJECT ", "P2P_INVITE ", "P2P_PEER ", "P2P_SET ", "P2P_UNAUTHORIZE ", "P2P_PRESENCE_REQ ", "P2P_EXT_LISTEN ", "P2P_REMOVE_CLIENT ", "WPS_NFC_TOKEN ", "WPS_NFC_TAG_READ ", "NFC_GET_HANDOVER_SEL ", "NFC_GET_HANDOVER_REQ ", "NFC_REPORT_HANDOVER ", "P2P_ASP_PROVISION ", "P2P_ASP_PROVISION_RESP ", NULL }; int found = 0; int i; if (global->p2p_init_wpa_s == NULL) return NULL; for (i = 0; !found && cmd[i]; i++) { if (os_strcmp(buf, cmd[i]) == 0) found = 1; } for (i = 0; !found && prefix[i]; i++) { if (os_strncmp(buf, prefix[i], os_strlen(prefix[i])) == 0) found = 1; } if (found) return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s, buf, resp_len); #endif /* CONFIG_P2P */ return NULL; } static char * wpas_global_ctrl_iface_redir_wfd(struct wpa_global *global, char *buf, size_t *resp_len) { #ifdef CONFIG_WIFI_DISPLAY if (global->p2p_init_wpa_s == NULL) return NULL; if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0 || os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s, buf, resp_len); #endif /* CONFIG_WIFI_DISPLAY */ return NULL; } static char * wpas_global_ctrl_iface_redir(struct wpa_global *global, char *buf, size_t *resp_len) { char *ret; ret = wpas_global_ctrl_iface_redir_p2p(global, buf, resp_len); if (ret) return ret; ret = wpas_global_ctrl_iface_redir_wfd(global, buf, resp_len); if (ret) return ret; return NULL; } static int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd) { char *value; value = os_strchr(cmd, ' '); if (value == NULL) return -1; *value++ = '\0'; wpa_printf(MSG_DEBUG, "GLOBAL_CTRL_IFACE SET '%s'='%s'", cmd, value); #ifdef CONFIG_WIFI_DISPLAY if (os_strcasecmp(cmd, "wifi_display") == 0) { wifi_display_enable(global, !!atoi(value)); return 0; } #endif /* CONFIG_WIFI_DISPLAY */ /* Restore cmd to its original value to allow redirection */ value[-1] = ' '; return -1; } static int wpas_global_ctrl_iface_dup_network(struct wpa_global *global, char *cmd) { struct wpa_supplicant *wpa_s[2]; /* src, dst */ char *p; unsigned int i; /* cmd: " * */ for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) { p = os_strchr(cmd, ' '); if (p == NULL) return -1; *p = '\0'; wpa_s[i] = global->ifaces; for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) { if (os_strcmp(cmd, wpa_s[i]->ifname) == 0) break; } if (!wpa_s[i]) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find iface=%s", cmd); return -1; } cmd = p + 1; } return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[1]); } #ifndef CONFIG_NO_CONFIG_WRITE static int wpas_global_ctrl_iface_save_config(struct wpa_global *global) { int ret = 0, saved = 0; struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (!wpa_s->conf->update_config) { wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed to update configuration (update_config=0)"); continue; } if (wpa_config_write(wpa_s->confname, wpa_s->conf)) { wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to update configuration"); ret = 1; } else { wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration updated"); saved++; } } if (!saved && !ret) { wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - No configuration files could be updated"); ret = 1; } return ret; } #endif /* CONFIG_NO_CONFIG_WRITE */ static int wpas_global_ctrl_iface_status(struct wpa_global *global, char *buf, size_t buflen) { char *pos, *end; int ret; struct wpa_supplicant *wpa_s; pos = buf; end = buf + buflen; #ifdef CONFIG_P2P if (global->p2p && !global->p2p_disabled) { ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR "\n" "p2p_state=%s\n", MAC2STR(global->p2p_dev_addr), p2p_get_state_txt(global->p2p)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } else if (global->p2p) { ret = os_snprintf(pos, end - pos, "p2p_state=DISABLED\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY ret = os_snprintf(pos, end - pos, "wifi_display=%d\n", !!global->wifi_display); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; #endif /* CONFIG_WIFI_DISPLAY */ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { ret = os_snprintf(pos, end - pos, "ifname=%s\n" "address=" MACSTR "\n", wpa_s->ifname, MAC2STR(wpa_s->own_addr)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } return pos - buf; } #ifdef CONFIG_FST static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global, char *cmd, char *buf, size_t reply_size) { char ifname[IFNAMSIZ + 1]; struct fst_iface_cfg cfg; struct wpa_supplicant *wpa_s; struct fst_wpa_obj iface_obj; if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) { wpa_s = wpa_supplicant_get_iface(global, ifname); if (wpa_s) { if (wpa_s->fst) { wpa_printf(MSG_INFO, "FST: Already attached"); return -1; } fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj); wpa_s->fst = fst_attach(ifname, wpa_s->own_addr, &iface_obj, &cfg); if (wpa_s->fst) return os_snprintf(buf, reply_size, "OK\n"); } } return -1; } static int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global, char *cmd, char *buf, size_t reply_size) { char ifname[IFNAMSIZ + 1]; struct wpa_supplicant *wpa_s; if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) { wpa_s = wpa_supplicant_get_iface(global, ifname); if (wpa_s) { if (!fst_iface_detach(ifname)) { wpa_s->fst = NULL; return os_snprintf(buf, reply_size, "OK\n"); } } } return -1; } #endif /* CONFIG_FST */ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, char *buf, size_t *resp_len) { char *reply; const int reply_size = 2048; int reply_len; int level = MSG_DEBUG; if (os_strncmp(buf, "IFNAME=", 7) == 0) { char *pos = os_strchr(buf + 7, ' '); if (pos) { *pos++ = '\0'; return wpas_global_ctrl_iface_ifname(global, buf + 7, pos, resp_len); } } reply = wpas_global_ctrl_iface_redir(global, buf, resp_len); if (reply) return reply; if (os_strcmp(buf, "PING") == 0) level = MSG_EXCESSIVE; wpa_hexdump_ascii(level, "RX global ctrl_iface", (const u8 *) buf, os_strlen(buf)); reply = os_malloc(reply_size); if (reply == NULL) { *resp_len = 1; return NULL; } os_memcpy(reply, "OK\n", 3); reply_len = 3; if (os_strcmp(buf, "PING") == 0) { os_memcpy(reply, "PONG\n", 5); reply_len = 5; } else if (os_strncmp(buf, "INTERFACE_ADD ", 14) == 0) { if (wpa_supplicant_global_iface_add(global, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) { if (wpa_supplicant_global_iface_remove(global, buf + 17)) reply_len = -1; } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { reply_len = wpa_supplicant_global_iface_list( global, reply, reply_size); } else if (os_strcmp(buf, "INTERFACES") == 0) { reply_len = wpa_supplicant_global_iface_interfaces( global, reply, reply_size); #ifdef CONFIG_FST } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) { reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11, reply, reply_size); } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) { reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11, reply, reply_size); } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) { reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size); #endif /* CONFIG_FST */ } else if (os_strcmp(buf, "TERMINATE") == 0) { wpa_supplicant_terminate_proc(global); } else if (os_strcmp(buf, "SUSPEND") == 0) { wpas_notify_suspend(global); } else if (os_strcmp(buf, "RESUME") == 0) { wpas_notify_resume(global); } else if (os_strncmp(buf, "SET ", 4) == 0) { if (wpas_global_ctrl_iface_set(global, buf + 4)) { #ifdef CONFIG_P2P if (global->p2p_init_wpa_s) { os_free(reply); /* Check if P2P redirection would work for this * command. */ return wpa_supplicant_ctrl_iface_process( global->p2p_init_wpa_s, buf, resp_len); } #endif /* CONFIG_P2P */ reply_len = -1; } } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { if (wpas_global_ctrl_iface_dup_network(global, buf + 12)) reply_len = -1; #ifndef CONFIG_NO_CONFIG_WRITE } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { if (wpas_global_ctrl_iface_save_config(global)) reply_len = -1; #endif /* CONFIG_NO_CONFIG_WRITE */ } else if (os_strcmp(buf, "STATUS") == 0) { reply_len = wpas_global_ctrl_iface_status(global, reply, reply_size); #ifdef CONFIG_MODULE_TESTS } else if (os_strcmp(buf, "MODULE_TESTS") == 0) { int wpas_module_tests(void); if (wpas_module_tests() < 0) reply_len = -1; #endif /* CONFIG_MODULE_TESTS */ } else if (os_strncmp(buf, "RELOG", 5) == 0) { if (wpa_debug_reopen_file() < 0) reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; } if (reply_len < 0) { os_memcpy(reply, "FAIL\n", 5); reply_len = 5; } *resp_len = reply_len; return reply; } Index: releng/11.1/contrib/wpa/wpa_supplicant/events.c =================================================================== --- releng/11.1/contrib/wpa/wpa_supplicant/events.c (revision 324698) +++ releng/11.1/contrib/wpa/wpa_supplicant/events.c (revision 324699) @@ -1,3827 +1,3828 @@ /* * WPA Supplicant - Driver event processing * Copyright (c) 2003-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" #include "eloop.h" #include "config.h" #include "l2_packet/l2_packet.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "pcsc_funcs.h" #include "rsn_supp/preauth.h" #include "rsn_supp/pmksa_cache.h" #include "common/wpa_ctrl.h" #include "eap_peer/eap.h" #include "ap/hostapd.h" #include "p2p/p2p.h" #include "fst/fst.h" #include "wnm_sta.h" #include "notify.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "crypto/random.h" #include "blacklist.h" #include "wpas_glue.h" #include "wps_supplicant.h" #include "ibss_rsn.h" #include "sme.h" #include "gas_query.h" #include "p2p_supplicant.h" #include "bgscan.h" #include "autoscan.h" #include "ap.h" #include "bss.h" #include "scan.h" #include "offchannel.h" #include "interworking.h" #include "mesh.h" #include "mesh_mpm.h" #include "wmm_ac.h" #ifndef CONFIG_NO_SCAN_PROCESSING static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, int new_scan, int own_request); #endif /* CONFIG_NO_SCAN_PROCESSING */ static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct os_reltime now; if (ssid == NULL || ssid->disabled_until.sec == 0) return 0; os_get_reltime(&now); if (ssid->disabled_until.sec > now.sec) return ssid->disabled_until.sec - now.sec; wpas_clear_temp_disabled(wpa_s, ssid, 0); return 0; } /** * wpas_reenabled_network_time - Time until first network is re-enabled * @wpa_s: Pointer to wpa_supplicant data * Returns: If all enabled networks are temporarily disabled, returns the time * (in sec) until the first network is re-enabled. Otherwise returns 0. * * This function is used in case all enabled networks are temporarily disabled, * in which case it returns the time (in sec) that the first network will be * re-enabled. The function assumes that at least one network is enabled. */ static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid; int disabled_for, res = 0; #ifdef CONFIG_INTERWORKING if (wpa_s->conf->auto_interworking && wpa_s->conf->interworking && wpa_s->conf->cred) return 0; #endif /* CONFIG_INTERWORKING */ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (ssid->disabled) continue; disabled_for = wpas_temp_disabled(wpa_s, ssid); if (!disabled_for) return 0; if (!res || disabled_for < res) res = disabled_for; } return res; } void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (wpa_s->disconnected || wpa_s->wpa_state != WPA_SCANNING) return; wpa_dbg(wpa_s, MSG_DEBUG, "Try to associate due to network getting re-enabled"); if (wpa_supplicant_fast_associate(wpa_s) != 1) { wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } } static struct wpa_bss * wpa_supplicant_get_new_bss( struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wpa_bss *bss = NULL; struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid->ssid_len > 0) bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); if (!bss) bss = wpa_bss_get_bssid(wpa_s, bssid); return bss; } static void wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid); if (!bss) { wpa_supplicant_update_scan_results(wpa_s); /* Get the BSS from the new scan results */ bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid); } if (bss) wpa_s->current_bss = bss; } static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid, *old_ssid; u8 drv_ssid[SSID_MAX_LEN]; size_t drv_ssid_len; int res; if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) { wpa_supplicant_update_current_bss(wpa_s); if (wpa_s->current_ssid->ssid_len == 0) return 0; /* current profile still in use */ res = wpa_drv_get_ssid(wpa_s, drv_ssid); if (res < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to read SSID from driver"); return 0; /* try to use current profile */ } drv_ssid_len = res; if (drv_ssid_len == wpa_s->current_ssid->ssid_len && os_memcmp(drv_ssid, wpa_s->current_ssid->ssid, drv_ssid_len) == 0) return 0; /* current profile still in use */ wpa_msg(wpa_s, MSG_DEBUG, "Driver-initiated BSS selection changed the SSID to %s", wpa_ssid_txt(drv_ssid, drv_ssid_len)); /* continue selecting a new network profile */ } wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association " "information"); ssid = wpa_supplicant_get_ssid(wpa_s); if (ssid == NULL) { wpa_msg(wpa_s, MSG_INFO, "No network configuration found for the current AP"); return -1; } if (wpas_network_disabled(wpa_s, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled"); return -1; } if (disallowed_bssid(wpa_s, wpa_s->bssid) || disallowed_ssid(wpa_s, ssid->ssid, ssid->ssid_len)) { wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS is disallowed"); return -1; } res = wpas_temp_disabled(wpa_s, ssid); if (res > 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily " "disabled for %d second(s)", res); return -1; } wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the " "current AP"); if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { u8 wpa_ie[80]; size_t wpa_ie_len = sizeof(wpa_ie); if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_ie, &wpa_ie_len) < 0) wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites"); } else { wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); } if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) eapol_sm_invalidate_cached_session(wpa_s->eapol); old_ssid = wpa_s->current_ssid; wpa_s->current_ssid = ssid; wpa_supplicant_update_current_bss(wpa_s); wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); wpa_supplicant_initiate_eapol(wpa_s); if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); return 0; } void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (wpa_s->countermeasures) { wpa_s->countermeasures = 0; wpa_drv_set_countermeasures(wpa_s, 0); wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped"); /* * It is possible that the device is sched scanning, which means * that a connection attempt will be done only when we receive * scan results. However, in this case, it would be preferable * to scan and connect immediately, so cancel the sched_scan and * issue a regular scan flow. */ wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } } void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) { int bssid_changed; wnm_bss_keep_alive_deinit(wpa_s); #ifdef CONFIG_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); wpa_s->ibss_rsn = NULL; #endif /* CONFIG_IBSS_RSN */ #ifdef CONFIG_AP wpa_supplicant_ap_deinit(wpa_s); #endif /* CONFIG_AP */ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) return; wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); sme_clear_on_disassoc(wpa_s); wpa_s->current_bss = NULL; wpa_s->assoc_freq = 0; if (bssid_changed) wpas_notify_bssid_changed(wpa_s); eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); eapol_sm_notify_portValid(wpa_s->eapol, FALSE); if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); wpa_s->ap_ies_from_associnfo = 0; wpa_s->current_ssid = NULL; eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->key_mgmt = 0; wpas_rrm_reset(wpa_s); + wpa_s->wnmsleep_used = 0; } static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) { struct wpa_ie_data ie; int pmksa_set = -1; size_t i; if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 || ie.pmkid == NULL) return; for (i = 0; i < ie.num_pmkid; i++) { pmksa_set = pmksa_cache_set_current(wpa_s->wpa, ie.pmkid + i * PMKID_LEN, NULL, NULL, 0); if (pmksa_set == 0) { eapol_sm_notify_pmkid_attempt(wpa_s->eapol); break; } } wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from " "PMKSA cache", pmksa_set == 0 ? "" : "not "); } static void wpa_supplicant_event_pmkid_candidate(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (data == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, "RSN: No data in PMKID candidate " "event"); return; } wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR " index=%d preauth=%d", MAC2STR(data->pmkid_candidate.bssid), data->pmkid_candidate.index, data->pmkid_candidate.preauth); pmksa_candidate_add(wpa_s->wpa, data->pmkid_candidate.bssid, data->pmkid_candidate.index, data->pmkid_candidate.preauth); } static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s) { if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) return 0; #ifdef IEEE8021X_EAPOL if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA && wpa_s->current_ssid && !(wpa_s->current_ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { /* IEEE 802.1X, but not using dynamic WEP keys (i.e., either * plaintext or static WEP keys). */ return 0; } #endif /* IEEE8021X_EAPOL */ return 1; } /** * wpa_supplicant_scard_init - Initialize SIM/USIM access with PC/SC * @wpa_s: pointer to wpa_supplicant data * @ssid: Configuration data for the network * Returns: 0 on success, -1 on failure * * This function is called when starting authentication with a network that is * configured to use PC/SC for SIM/USIM access (EAP-SIM or EAP-AKA). */ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { #ifdef IEEE8021X_EAPOL #ifdef PCSC_FUNCS int aka = 0, sim = 0; if ((ssid != NULL && ssid->eap.pcsc == NULL) || wpa_s->scard != NULL || wpa_s->conf->external_sim) return 0; if (ssid == NULL || ssid->eap.eap_methods == NULL) { sim = 1; aka = 1; } else { struct eap_method_type *eap = ssid->eap.eap_methods; while (eap->vendor != EAP_VENDOR_IETF || eap->method != EAP_TYPE_NONE) { if (eap->vendor == EAP_VENDOR_IETF) { if (eap->method == EAP_TYPE_SIM) sim = 1; else if (eap->method == EAP_TYPE_AKA || eap->method == EAP_TYPE_AKA_PRIME) aka = 1; } eap++; } } if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL) sim = 0; if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL && eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME) == NULL) aka = 0; if (!sim && !aka) { wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to " "use SIM, but neither EAP-SIM nor EAP-AKA are " "enabled"); return 0; } wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM " "(sim=%d aka=%d) - initialize PCSC", sim, aka); wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader); if (wpa_s->scard == NULL) { wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM " "(pcsc-lite)"); return -1; } wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard); eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); #endif /* PCSC_FUNCS */ #endif /* IEEE8021X_EAPOL */ return 0; } #ifndef CONFIG_NO_SCAN_PROCESSING static int has_wep_key(struct wpa_ssid *ssid) { int i; for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) return 1; } return 0; } static int wpa_supplicant_match_privacy(struct wpa_bss *bss, struct wpa_ssid *ssid) { int privacy = 0; if (ssid->mixed_cell) return 1; #ifdef CONFIG_WPS if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) return 1; #endif /* CONFIG_WPS */ if (has_wep_key(ssid)) privacy = 1; #ifdef IEEE8021X_EAPOL if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) privacy = 1; #endif /* IEEE8021X_EAPOL */ if (wpa_key_mgmt_wpa(ssid->key_mgmt)) privacy = 1; if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) privacy = 1; if (bss->caps & IEEE80211_CAP_PRIVACY) return privacy; return !privacy; } static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss) { struct wpa_ie_data ie; int proto_match = 0; const u8 *rsn_ie, *wpa_ie; int ret; int wep_ok; ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss); if (ret >= 0) return ret; /* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */ wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) && (((ssid->key_mgmt & WPA_KEY_MGMT_NONE) && ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) || (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)); rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) { proto_match++; if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - parse " "failed"); break; } if (wep_ok && (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) { wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN " "in RSN IE"); return 1; } if (!(ie.proto & ssid->proto)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - proto " "mismatch"); break; } if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - PTK " "cipher mismatch"); break; } if (!(ie.group_cipher & ssid->group_cipher)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - GTK " "cipher mismatch"); break; } if (!(ie.key_mgmt & ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - key mgmt " "mismatch"); break; } #ifdef CONFIG_IEEE80211W if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt " "frame protection"); break; } #endif /* CONFIG_IEEE80211W */ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE"); return 1; } wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) { proto_match++; if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - parse " "failed"); break; } if (wep_ok && (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) { wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN " "in WPA IE"); return 1; } if (!(ie.proto & ssid->proto)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - proto " "mismatch"); break; } if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - PTK " "cipher mismatch"); break; } if (!(ie.group_cipher & ssid->group_cipher)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - GTK " "cipher mismatch"); break; } if (!(ie.key_mgmt & ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - key mgmt " "mismatch"); break; } wpa_dbg(wpa_s, MSG_DEBUG, " selected based on WPA IE"); return 1; } if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie && !rsn_ie) { wpa_dbg(wpa_s, MSG_DEBUG, " allow for non-WPA IEEE 802.1X"); return 1; } if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) && wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match"); return 0; } if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) { wpa_dbg(wpa_s, MSG_DEBUG, " allow in OSEN"); return 1; } if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2"); return 1; } wpa_dbg(wpa_s, MSG_DEBUG, " reject due to mismatch with " "WPA/WPA2"); return 0; } static int freq_allowed(int *freqs, int freq) { int i; if (freqs == NULL) return 1; for (i = 0; freqs[i]; i++) if (freqs[i] == freq) return 1; return 0; } static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { const struct hostapd_hw_modes *mode = NULL, *modes; const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES }; const u8 *rate_ie; int i, j, k; if (bss->freq == 0) return 1; /* Cannot do matching without knowing band */ modes = wpa_s->hw.modes; if (modes == NULL) { /* * The driver does not provide any additional information * about the utilized hardware, so allow the connection attempt * to continue. */ return 1; } for (i = 0; i < wpa_s->hw.num_modes; i++) { for (j = 0; j < modes[i].num_channels; j++) { int freq = modes[i].channels[j].freq; if (freq == bss->freq) { if (mode && mode->mode == HOSTAPD_MODE_IEEE80211G) break; /* do not allow 802.11b replace * 802.11g */ mode = &modes[i]; break; } } } if (mode == NULL) return 0; for (i = 0; i < (int) sizeof(scan_ie); i++) { rate_ie = wpa_bss_get_ie(bss, scan_ie[i]); if (rate_ie == NULL) continue; for (j = 2; j < rate_ie[1] + 2; j++) { int flagged = !!(rate_ie[j] & 0x80); int r = (rate_ie[j] & 0x7f) * 5; /* * IEEE Std 802.11n-2009 7.3.2.2: * The new BSS Membership selector value is encoded * like a legacy basic rate, but it is not a rate and * only indicates if the BSS members are required to * support the mandatory features of Clause 20 [HT PHY] * in order to join the BSS. */ if (flagged && ((rate_ie[j] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)) { if (!ht_supported(mode)) { wpa_dbg(wpa_s, MSG_DEBUG, " hardware does not support " "HT PHY"); return 0; } continue; } /* There's also a VHT selector for 802.11ac */ if (flagged && ((rate_ie[j] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) { if (!vht_supported(mode)) { wpa_dbg(wpa_s, MSG_DEBUG, " hardware does not support " "VHT PHY"); return 0; } continue; } if (!flagged) continue; /* check for legacy basic rates */ for (k = 0; k < mode->num_rates; k++) { if (mode->rates[k] == r) break; } if (k == mode->num_rates) { /* * IEEE Std 802.11-2007 7.3.2.2 demands that in * order to join a BSS all required rates * have to be supported by the hardware. */ wpa_dbg(wpa_s, MSG_DEBUG, " hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)", r / 10, r % 10, bss->freq, mode->mode, mode->num_rates); return 0; } } } return 1; } /* * Test whether BSS is in an ESS. * This is done differently in DMG (60 GHz) and non-DMG bands */ static int bss_is_ess(struct wpa_bss *bss) { if (bss_is_dmg(bss)) { return (bss->caps & IEEE80211_CAP_DMG_MASK) == IEEE80211_CAP_DMG_AP; } return ((bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) == IEEE80211_CAP_ESS); } static int match_mac_mask(const u8 *addr_a, const u8 *addr_b, const u8 *mask) { size_t i; for (i = 0; i < ETH_ALEN; i++) { if ((addr_a[i] & mask[i]) != (addr_b[i] & mask[i])) return 0; } return 1; } static int addr_in_list(const u8 *addr, const u8 *list, size_t num) { size_t i; for (i = 0; i < num; i++) { const u8 *a = list + i * ETH_ALEN * 2; const u8 *m = a + ETH_ALEN; if (match_mac_mask(a, addr, m)) return 1; } return 0; } static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int i, struct wpa_bss *bss, struct wpa_ssid *group, int only_first_ssid) { u8 wpa_ie_len, rsn_ie_len; int wpa; struct wpa_blacklist *e; const u8 *ie; struct wpa_ssid *ssid; int osen; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); rsn_ie_len = ie ? ie[1] : 0; ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); osen = ie != NULL; wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' " "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s", i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), wpa_ie_len, rsn_ie_len, bss->caps, bss->level, bss->freq, wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "", (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ? " p2p" : "", osen ? " osen=1" : ""); e = wpa_blacklist_get(wpa_s, bss->bssid); if (e) { int limit = 1; if (wpa_supplicant_enabled_networks(wpa_s) == 1) { /* * When only a single network is enabled, we can * trigger blacklisting on the first failure. This * should not be done with multiple enabled networks to * avoid getting forced to move into a worse ESS on * single error if there are no other BSSes of the * current ESS. */ limit = 0; } if (e->count > limit) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " "(count=%d limit=%d)", e->count, limit); return NULL; } } if (bss->ssid_len == 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known"); return NULL; } if (disallowed_bssid(wpa_s, bss->bssid)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID disallowed"); return NULL; } if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID disallowed"); return NULL; } wpa = wpa_ie_len > 0 || rsn_ie_len > 0; for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) { int check_ssid = wpa ? 1 : (ssid->ssid_len != 0); int res; if (wpas_network_disabled(wpa_s, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled"); continue; } res = wpas_temp_disabled(wpa_s, ssid); if (res > 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled " "temporarily for %d second(s)", res); continue; } #ifdef CONFIG_WPS if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " "(WPS)"); continue; } if (wpa && ssid->ssid_len == 0 && wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) check_ssid = 0; if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { /* Only allow wildcard SSID match if an AP * advertises active WPS operation that matches * with our mode. */ check_ssid = 1; if (ssid->ssid_len == 0 && wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) check_ssid = 0; } #endif /* CONFIG_WPS */ if (ssid->bssid_set && ssid->ssid_len == 0 && os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0) check_ssid = 0; if (check_ssid && (bss->ssid_len != ssid->ssid_len || os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch"); continue; } if (ssid->bssid_set && os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch"); continue; } /* check blacklist */ if (ssid->num_bssid_blacklist && addr_in_list(bss->bssid, ssid->bssid_blacklist, ssid->num_bssid_blacklist)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID blacklisted"); continue; } /* if there is a whitelist, only accept those APs */ if (ssid->num_bssid_whitelist && !addr_in_list(bss->bssid, ssid->bssid_whitelist, ssid->num_bssid_whitelist)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID not in whitelist"); continue; } if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) continue; if (!osen && !wpa && !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-WPA network " "not allowed"); continue; } if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) && has_wep_key(ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - ignore WPA/WPA2 AP for WEP network block"); continue; } if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-OSEN network " "not allowed"); continue; } if (!wpa_supplicant_match_privacy(bss, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy " "mismatch"); continue; } if (!bss_is_ess(bss)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - not ESS network"); continue; } if (!freq_allowed(ssid->freq_list, bss->freq)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not " "allowed"); continue; } if (!rate_match(wpa_s, bss)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - rate sets do " "not match"); continue; } #ifdef CONFIG_P2P if (ssid->p2p_group && !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) && !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P IE seen"); continue; } if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) { struct wpabuf *p2p_ie; u8 dev_addr[ETH_ALEN]; ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); if (ie == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P element"); continue; } p2p_ie = wpa_bss_get_vendor_ie_multi( bss, P2P_IE_VENDOR_TYPE); if (p2p_ie == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - could not fetch P2P element"); continue; } if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0 || os_memcmp(dev_addr, ssid->go_p2p_dev_addr, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - no matching GO P2P Device Address in P2P element"); wpabuf_free(p2p_ie); continue; } wpabuf_free(p2p_ie); } /* * TODO: skip the AP if its P2P IE has Group Formation * bit set in the P2P Group Capability Bitmap and we * are not in Group Formation with that device. */ #endif /* CONFIG_P2P */ if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time)) { struct os_reltime diff; os_reltime_sub(&wpa_s->scan_min_time, &bss->last_update, &diff); wpa_dbg(wpa_s, MSG_DEBUG, " skip - scan result not recent enough (%u.%06u seconds too old)", (unsigned int) diff.sec, (unsigned int) diff.usec); continue; } /* Matching configuration found */ return ssid; } /* No matching configuration found */ return NULL; } static struct wpa_bss * wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, struct wpa_ssid **selected_ssid, int only_first_ssid) { unsigned int i; if (only_first_ssid) wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d", group->id); else wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d", group->priority); for (i = 0; i < wpa_s->last_scan_res_used; i++) { struct wpa_bss *bss = wpa_s->last_scan_res[i]; *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group, only_first_ssid); if (!*selected_ssid) continue; wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR " ssid='%s'", MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len)); return bss; } return NULL; } struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, struct wpa_ssid **selected_ssid) { struct wpa_bss *selected = NULL; int prio; struct wpa_ssid *next_ssid = NULL; struct wpa_ssid *ssid; if (wpa_s->last_scan_res == NULL || wpa_s->last_scan_res_used == 0) return NULL; /* no scan results from last update */ if (wpa_s->next_ssid) { /* check that next_ssid is still valid */ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (ssid == wpa_s->next_ssid) break; } next_ssid = ssid; wpa_s->next_ssid = NULL; } while (selected == NULL) { for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { if (next_ssid && next_ssid->priority == wpa_s->conf->pssid[prio]->priority) { selected = wpa_supplicant_select_bss( wpa_s, next_ssid, selected_ssid, 1); if (selected) break; } selected = wpa_supplicant_select_bss( wpa_s, wpa_s->conf->pssid[prio], selected_ssid, 0); if (selected) break; } if (selected == NULL && wpa_s->blacklist && !wpa_s->countermeasures) { wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear " "blacklist and try again"); wpa_blacklist_clear(wpa_s); wpa_s->blacklist_cleared++; } else if (selected == NULL) break; } ssid = *selected_ssid; if (selected && ssid && ssid->mem_only_psk && !ssid->psk_set && !ssid->passphrase && !ssid->ext_psk) { const char *field_name, *txt = NULL; wpa_dbg(wpa_s, MSG_DEBUG, "PSK/passphrase not yet available for the selected network"); wpas_notify_network_request(wpa_s, ssid, WPA_CTRL_REQ_PSK_PASSPHRASE, NULL); field_name = wpa_supplicant_ctrl_req_to_string( WPA_CTRL_REQ_PSK_PASSPHRASE, NULL, &txt); if (field_name == NULL) return NULL; wpas_send_ctrl_req(wpa_s, ssid, field_name, txt); selected = NULL; } return selected; } static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s, int timeout_sec, int timeout_usec) { if (!wpa_supplicant_enabled_networks(wpa_s)) { /* * No networks are enabled; short-circuit request so * we don't wait timeout seconds before transitioning * to INACTIVE state. */ wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request " "since there are no enabled networks"); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); return; } wpa_s->scan_for_connection = 1; wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec); } int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid) { if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP "PBC session overlap"); wpas_notify_wps_event_pbc_overlap(wpa_s); #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT || wpa_s->p2p_in_provisioning) { eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s, NULL); return -1; } #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS wpas_wps_pbc_overlap(wpa_s); wpas_wps_cancel(wpa_s); #endif /* CONFIG_WPS */ return -1; } wpa_msg(wpa_s, MSG_DEBUG, "Considering connect request: reassociate: %d selected: " MACSTR " bssid: " MACSTR " pending: " MACSTR " wpa_state: %s ssid=%p current_ssid=%p", wpa_s->reassociate, MAC2STR(selected->bssid), MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), wpa_supplicant_state_txt(wpa_s->wpa_state), ssid, wpa_s->current_ssid); /* * Do not trigger new association unless the BSSID has changed or if * reassociation is requested. If we are in process of associating with * the selected BSSID, do not trigger new attempt. */ if (wpa_s->reassociate || (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 && ((wpa_s->wpa_state != WPA_ASSOCIATING && wpa_s->wpa_state != WPA_AUTHENTICATING) || (!is_zero_ether_addr(wpa_s->pending_bssid) && os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) != 0) || (is_zero_ether_addr(wpa_s->pending_bssid) && ssid != wpa_s->current_ssid)))) { if (wpa_supplicant_scard_init(wpa_s, ssid)) { wpa_supplicant_req_new_scan(wpa_s, 10, 0); return 0; } wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR, MAC2STR(selected->bssid)); wpa_supplicant_associate(wpa_s, selected, ssid); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Already associated or trying to " "connect with the selected AP"); } return 0; } static struct wpa_ssid * wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s) { int prio; struct wpa_ssid *ssid; for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext) { if (wpas_network_disabled(wpa_s, ssid)) continue; if (ssid->mode == IEEE80211_MODE_IBSS || ssid->mode == IEEE80211_MODE_AP || ssid->mode == IEEE80211_MODE_MESH) return ssid; } } return NULL; } /* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based * on BSS added and BSS changed events */ static void wpa_supplicant_rsn_preauth_scan_results( struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; if (rsn_preauth_scan_results(wpa_s->wpa) < 0) return; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { const u8 *ssid, *rsn; ssid = wpa_bss_get_ie(bss, WLAN_EID_SSID); if (ssid == NULL) continue; rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (rsn == NULL) continue; rsn_preauth_scan_result(wpa_s->wpa, bss->bssid, ssid, rsn); } } static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid) { struct wpa_bss *current_bss = NULL; #ifndef CONFIG_NO_ROAMING int min_diff; #endif /* CONFIG_NO_ROAMING */ if (wpa_s->reassociate) return 1; /* explicit request to reassociate */ if (wpa_s->wpa_state < WPA_ASSOCIATED) return 1; /* we are not associated; continue */ if (wpa_s->current_ssid == NULL) return 1; /* unknown current SSID */ if (wpa_s->current_ssid != ssid) return 1; /* different network block */ if (wpas_driver_bss_selection(wpa_s)) return 0; /* Driver-based roaming */ if (wpa_s->current_ssid->ssid) current_bss = wpa_bss_get(wpa_s, wpa_s->bssid, wpa_s->current_ssid->ssid, wpa_s->current_ssid->ssid_len); if (!current_bss) current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid); if (!current_bss) return 1; /* current BSS not seen in scan results */ if (current_bss == selected) return 0; if (selected->last_update_idx > current_bss->last_update_idx) return 1; /* current BSS not seen in the last scan */ #ifndef CONFIG_NO_ROAMING wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation"); wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d snr=%d est_throughput=%u", MAC2STR(current_bss->bssid), current_bss->level, current_bss->snr, current_bss->est_throughput); wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR " level=%d snr=%d est_throughput=%u", MAC2STR(selected->bssid), selected->level, selected->snr, selected->est_throughput); if (wpa_s->current_ssid->bssid_set && os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS " "has preferred BSSID"); return 1; } if (selected->est_throughput > current_bss->est_throughput + 5000) { wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS has better estimated throughput"); return 1; } if (current_bss->level < 0 && current_bss->level > selected->level) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better " "signal level"); return 0; } min_diff = 2; if (current_bss->level < 0) { if (current_bss->level < -85) min_diff = 1; else if (current_bss->level < -80) min_diff = 2; else if (current_bss->level < -75) min_diff = 3; else if (current_bss->level < -70) min_diff = 4; else min_diff = 5; } if (abs(current_bss->level - selected->level) < min_diff) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference " "in signal level"); return 0; } return 1; #else /* CONFIG_NO_ROAMING */ return 0; #endif /* CONFIG_NO_ROAMING */ } /* Return != 0 if no scan results could be fetched or if scan results should not * be shared with other virtual interfaces. */ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data, int own_request) { struct wpa_scan_results *scan_res = NULL; int ret = 0; int ap = 0; #ifndef CONFIG_NO_RANDOM_POOL size_t i, num; #endif /* CONFIG_NO_RANDOM_POOL */ #ifdef CONFIG_AP if (wpa_s->ap_iface) ap = 1; #endif /* CONFIG_AP */ wpa_supplicant_notify_scanning(wpa_s, 0); scan_res = wpa_supplicant_get_scan_results(wpa_s, data ? &data->scan_info : NULL, 1); if (scan_res == NULL) { if (wpa_s->conf->ap_scan == 2 || ap || wpa_s->scan_res_handler == scan_only_handler) return -1; if (!own_request) return -1; wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try " "scanning again"); wpa_supplicant_req_new_scan(wpa_s, 1, 0); ret = -1; goto scan_work_done; } #ifndef CONFIG_NO_RANDOM_POOL num = scan_res->num; if (num > 10) num = 10; for (i = 0; i < num; i++) { u8 buf[5]; struct wpa_scan_res *res = scan_res->res[i]; buf[0] = res->bssid[5]; buf[1] = res->qual & 0xff; buf[2] = res->noise & 0xff; buf[3] = res->level & 0xff; buf[4] = res->tsf & 0xff; random_add_randomness(buf, sizeof(buf)); } #endif /* CONFIG_NO_RANDOM_POOL */ if (own_request && wpa_s->scan_res_handler && (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) { void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); scan_res_handler = wpa_s->scan_res_handler; wpa_s->scan_res_handler = NULL; scan_res_handler(wpa_s, scan_res); ret = -2; goto scan_work_done; } if (ap) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan results in AP mode"); #ifdef CONFIG_AP if (wpa_s->ap_iface->scan_cb) wpa_s->ap_iface->scan_cb(wpa_s->ap_iface); #endif /* CONFIG_AP */ goto scan_work_done; } wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)", wpa_s->own_scan_running, wpa_s->radio->external_scan_running); if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_scan_use_id && wpa_s->own_scan_running) { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u", wpa_s->manual_scan_id); wpa_s->manual_scan_use_id = 0; } else { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); } wpas_notify_scan_results(wpa_s); wpas_notify_scan_done(wpa_s, 1); if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) { wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection"); wpa_scan_results_free(scan_res); return 0; } if (wnm_scan_process(wpa_s, 1) > 0) goto scan_work_done; if (sme_proc_obss_scan(wpa_s) > 0) goto scan_work_done; if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) goto scan_work_done; if (autoscan_notify_scan(wpa_s, scan_res)) goto scan_work_done; if (wpa_s->disconnected) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); goto scan_work_done; } if (!wpas_driver_bss_selection(wpa_s) && bgscan_notify_scan(wpa_s, scan_res) == 1) goto scan_work_done; wpas_wps_update_ap_info(wpa_s, scan_res); wpa_scan_results_free(scan_res); if (wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); } return wpas_select_network_from_last_scan(wpa_s, 1, own_request); scan_work_done: wpa_scan_results_free(scan_res); if (wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); } return ret; } static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, int new_scan, int own_request) { struct wpa_bss *selected; struct wpa_ssid *ssid = NULL; int time_to_reenable = wpas_reenabled_network_time(wpa_s); if (time_to_reenable > 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Postpone network selection by %d seconds since all networks are disabled", time_to_reenable); eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); eloop_register_timeout(time_to_reenable, 0, wpas_network_reenabled, wpa_s, NULL); return 0; } if (wpa_s->p2p_mgmt) return 0; /* no normal connection on p2p_mgmt interface */ selected = wpa_supplicant_pick_network(wpa_s, &ssid); if (selected) { int skip; skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid); if (skip) { if (new_scan) wpa_supplicant_rsn_preauth_scan_results(wpa_s); return 0; } if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed"); return -1; } if (new_scan) wpa_supplicant_rsn_preauth_scan_results(wpa_s); /* * Do not notify other virtual radios of scan results since we do not * want them to start other associations at the same time. */ return 1; } else { #ifdef CONFIG_MESH if (wpa_s->ifmsh) { wpa_msg(wpa_s, MSG_INFO, "Avoiding join because we already joined a mesh group"); return 0; } #endif /* CONFIG_MESH */ wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found"); ssid = wpa_supplicant_pick_new_network(wpa_s); if (ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network"); wpa_supplicant_associate(wpa_s, NULL, ssid); if (new_scan) wpa_supplicant_rsn_preauth_scan_results(wpa_s); } else if (own_request) { /* * No SSID found. If SCAN results are as a result of * own scan request and not due to a scan request on * another shared interface, try another scan. */ int timeout_sec = wpa_s->scan_interval; int timeout_usec = 0; #ifdef CONFIG_P2P int res; res = wpas_p2p_scan_no_go_seen(wpa_s); if (res == 2) return 2; if (res == 1) return 0; if (wpa_s->p2p_in_provisioning || wpa_s->show_group_started || wpa_s->p2p_in_invitation) { /* * Use shorter wait during P2P Provisioning * state and during P2P join-a-group operation * to speed up group formation. */ timeout_sec = 0; timeout_usec = 250000; wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); return 0; } #endif /* CONFIG_P2P */ #ifdef CONFIG_INTERWORKING if (wpa_s->conf->auto_interworking && wpa_s->conf->interworking && wpa_s->conf->cred) { wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: " "start ANQP fetch since no matching " "networks found"); wpa_s->network_select = 1; wpa_s->auto_network_select = 1; interworking_start_fetch_anqp(wpa_s); return 1; } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_WPS if (wpa_s->after_wps > 0 || wpas_wps_searching(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "Use shorter wait during WPS processing"); timeout_sec = 0; timeout_usec = 500000; wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); return 0; } #endif /* CONFIG_WPS */ if (wpa_supplicant_req_sched_scan(wpa_s)) wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_NETWORK_NOT_FOUND); } } return 0; } static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_supplicant *ifs; int res; res = _wpa_supplicant_event_scan_results(wpa_s, data, 1); if (res == 2) { /* * Interface may have been removed, so must not dereference * wpa_s after this. */ return 1; } if (res != 0) { /* * If no scan results could be fetched, then no need to * notify those interfaces that did not actually request * this scan. Similarly, if scan results started a new operation on this * interface, do not notify other interfaces to avoid concurrent * operations during a connection attempt. */ return 0; } /* * Check other interfaces to see if they share the same radio. If * so, they get updated with this same scan info. */ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { if (ifs != wpa_s) { wpa_printf(MSG_DEBUG, "%s: Updating scan results from " "sibling", ifs->ifname); _wpa_supplicant_event_scan_results(ifs, data, 0); } } return 0; } #endif /* CONFIG_NO_SCAN_PROCESSING */ int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_NO_SCAN_PROCESSING return -1; #else /* CONFIG_NO_SCAN_PROCESSING */ struct os_reltime now; if (wpa_s->last_scan_res_used == 0) return -1; os_get_reltime(&now); if (os_reltime_expired(&now, &wpa_s->last_scan, 5)) { wpa_printf(MSG_DEBUG, "Fast associate: Old scan results"); return -1; } return wpas_select_network_from_last_scan(wpa_s, 0, 1); #endif /* CONFIG_NO_SCAN_PROCESSING */ } #ifdef CONFIG_WNM static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (wpa_s->wpa_state < WPA_ASSOCIATED) return; if (!wpa_s->no_keep_alive) { wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR, MAC2STR(wpa_s->bssid)); /* TODO: could skip this if normal data traffic has been sent */ /* TODO: Consider using some more appropriate data frame for * this */ if (wpa_s->l2) l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800, (u8 *) "", 0); } #ifdef CONFIG_SME if (wpa_s->sme.bss_max_idle_period) { unsigned int msec; msec = wpa_s->sme.bss_max_idle_period * 1024; /* times 1000 */ if (msec > 100) msec -= 100; eloop_register_timeout(msec / 1000, msec % 1000 * 1000, wnm_bss_keep_alive, wpa_s, NULL); } #endif /* CONFIG_SME */ } static void wnm_process_assoc_resp(struct wpa_supplicant *wpa_s, const u8 *ies, size_t ies_len) { struct ieee802_11_elems elems; if (ies == NULL) return; if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) return; #ifdef CONFIG_SME if (elems.bss_max_idle_period) { unsigned int msec; wpa_s->sme.bss_max_idle_period = WPA_GET_LE16(elems.bss_max_idle_period); wpa_printf(MSG_DEBUG, "WNM: BSS Max Idle Period: %u (* 1000 " "TU)%s", wpa_s->sme.bss_max_idle_period, (elems.bss_max_idle_period[2] & 0x01) ? " (protected keep-live required)" : ""); if (wpa_s->sme.bss_max_idle_period == 0) wpa_s->sme.bss_max_idle_period = 1; if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) { eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL); /* msec times 1000 */ msec = wpa_s->sme.bss_max_idle_period * 1024; if (msec > 100) msec -= 100; eloop_register_timeout(msec / 1000, msec % 1000 * 1000, wnm_bss_keep_alive, wpa_s, NULL); } } #endif /* CONFIG_SME */ } #endif /* CONFIG_WNM */ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_WNM eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL); #endif /* CONFIG_WNM */ } #ifdef CONFIG_INTERWORKING static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map, size_t len) { int res; wpa_hexdump(MSG_DEBUG, "Interworking: QoS Map Set", qos_map, len); res = wpa_drv_set_qos_map(wpa_s, qos_map, len); if (res) { wpa_printf(MSG_DEBUG, "Interworking: Failed to configure QoS Map Set to the driver"); } return res; } static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s, const u8 *ies, size_t ies_len) { struct ieee802_11_elems elems; if (ies == NULL) return; if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) return; if (elems.qos_map_set) { wpas_qos_map_set(wpa_s, elems.qos_map_set, elems.qos_map_set_len); } } #endif /* CONFIG_INTERWORKING */ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { int l, len, found = 0, wpa_found, rsn_found; const u8 *p; #ifdef CONFIG_IEEE80211R u8 bssid[ETH_ALEN]; #endif /* CONFIG_IEEE80211R */ wpa_dbg(wpa_s, MSG_DEBUG, "Association info event"); if (data->assoc_info.req_ies) wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies, data->assoc_info.req_ies_len); if (data->assoc_info.resp_ies) { wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #ifdef CONFIG_TDLS wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_TDLS */ #ifdef CONFIG_WNM wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_WNM */ #ifdef CONFIG_INTERWORKING interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_INTERWORKING */ } if (data->assoc_info.beacon_ies) wpa_hexdump(MSG_DEBUG, "beacon_ies", data->assoc_info.beacon_ies, data->assoc_info.beacon_ies_len); if (data->assoc_info.freq) wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz", data->assoc_info.freq); p = data->assoc_info.req_ies; l = data->assoc_info.req_ies_len; /* Go through the IEs and make a copy of the WPA/RSN IE, if present. */ while (p && l >= 2) { len = p[1] + 2; if (len > l) { wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", p, l); break; } if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || (p[0] == WLAN_EID_RSN && p[1] >= 2)) { if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len)) break; found = 1; wpa_find_assoc_pmkid(wpa_s); break; } l -= len; p += len; } if (!found && data->assoc_info.req_ies) wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SME if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { if (wpa_drv_get_bssid(wpa_s, bssid) < 0 || wpa_ft_validate_reassoc_resp(wpa_s->wpa, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, bssid) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of " "Reassociation Response failed"); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_INVALID_IE); return -1; } } p = data->assoc_info.resp_ies; l = data->assoc_info.resp_ies_len; #ifdef CONFIG_WPS_STRICT if (p && wpa_s->current_ssid && wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) { struct wpabuf *wps; wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE); if (wps == NULL) { wpa_msg(wpa_s, MSG_INFO, "WPS-STRICT: AP did not " "include WPS IE in (Re)Association Response"); return -1; } if (wps_validate_assoc_resp(wps) < 0) { wpabuf_free(wps); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_INVALID_IE); return -1; } wpabuf_free(wps); } #endif /* CONFIG_WPS_STRICT */ /* Go through the IEs and make a copy of the MDIE, if present. */ while (p && l >= 2) { len = p[1] + 2; if (len > l) { wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", p, l); break; } if (p[0] == WLAN_EID_MOBILITY_DOMAIN && p[1] >= MOBILITY_DOMAIN_ID_LEN) { wpa_s->sme.ft_used = 1; os_memcpy(wpa_s->sme.mobility_domain, p + 2, MOBILITY_DOMAIN_ID_LEN); break; } l -= len; p += len; } #endif /* CONFIG_SME */ /* Process FT when SME is in the driver */ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && wpa_ft_is_completed(wpa_s->wpa)) { if (wpa_drv_get_bssid(wpa_s, bssid) < 0 || wpa_ft_validate_reassoc_resp(wpa_s->wpa, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, bssid) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of " "Reassociation Response failed"); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_INVALID_IE); return -1; } wpa_dbg(wpa_s, MSG_DEBUG, "FT: Reassociation Response done"); } wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_IEEE80211R */ /* WPA/RSN IE from Beacon/ProbeResp */ p = data->assoc_info.beacon_ies; l = data->assoc_info.beacon_ies_len; /* Go through the IEs and make a copy of the WPA/RSN IEs, if present. */ wpa_found = rsn_found = 0; while (p && l >= 2) { len = p[1] + 2; if (len > l) { wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies", p, l); break; } if (!wpa_found && p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) { wpa_found = 1; wpa_sm_set_ap_wpa_ie(wpa_s->wpa, p, len); } if (!rsn_found && p[0] == WLAN_EID_RSN && p[1] >= 2) { rsn_found = 1; wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len); } l -= len; p += len; } if (!wpa_found && data->assoc_info.beacon_ies) wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0); if (!rsn_found && data->assoc_info.beacon_ies) wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); if (wpa_found || rsn_found) wpa_s->ap_ies_from_associnfo = 1; #ifdef CONFIG_FST wpabuf_free(wpa_s->received_mb_ies); wpa_s->received_mb_ies = NULL; if (wpa_s->fst) { struct mb_ies_info mb_ies; wpa_printf(MSG_DEBUG, "Looking for MB IE"); if (!mb_ies_info_by_ies(&mb_ies, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len)) wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies); } #endif /* CONFIG_FST */ if (wpa_s->assoc_freq && data->assoc_info.freq && wpa_s->assoc_freq != data->assoc_info.freq) { wpa_printf(MSG_DEBUG, "Operating frequency changed from " "%u to %u MHz", wpa_s->assoc_freq, data->assoc_info.freq); wpa_supplicant_update_scan_results(wpa_s); } wpa_s->assoc_freq = data->assoc_info.freq; return 0; } static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s) { const u8 *bss_wpa = NULL, *bss_rsn = NULL; if (!wpa_s->current_bss || !wpa_s->current_ssid) return -1; if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt)) return 0; bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss, WPA_IE_VENDOR_TYPE); bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN); if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa, bss_wpa ? 2 + bss_wpa[1] : 0) || wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn, bss_rsn ? 2 + bss_rsn[1] : 0)) return -1; return 0; } static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { u8 bssid[ETH_ALEN]; int ft_completed; int new_bss = 0; #ifdef CONFIG_AP if (wpa_s->ap_iface) { if (!data) return; hostapd_notif_assoc(wpa_s->ap_iface->bss[0], data->assoc_info.addr, data->assoc_info.req_ies, data->assoc_info.req_ies_len, data->assoc_info.reassoc); return; } #endif /* CONFIG_AP */ eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); ft_completed = wpa_ft_is_completed(wpa_s->wpa); if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) return; if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID"); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" MACSTR, MAC2STR(bssid)); new_bss = 1; random_add_randomness(bssid, ETH_ALEN); os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); wpas_notify_bssid_changed(wpa_s); if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) { wpa_clear_keys(wpa_s, bssid); } if (wpa_supplicant_select_config(wpa_s) < 0) { wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } } if (wpa_s->conf->ap_scan == 1 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) { if (wpa_supplicant_assoc_update_ie(wpa_s) < 0 && new_bss) wpa_msg(wpa_s, MSG_WARNING, "WPA/RSN IEs not updated"); } #ifdef CONFIG_SME os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN); wpa_s->sme.prev_bssid_set = 1; wpa_s->sme.last_unprot_disconnect.sec = 0; #endif /* CONFIG_SME */ wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid)); if (wpa_s->current_ssid) { /* When using scanning (ap_scan=1), SIM PC/SC interface can be * initialized before association, but for other modes, * initialize PC/SC here, if the current configuration needs * smartcard or SIM/USIM. */ wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid); } wpa_sm_notify_assoc(wpa_s->wpa, bssid); if (wpa_s->l2) l2_packet_notify_auth_start(wpa_s->l2); /* * Set portEnabled first to FALSE in order to get EAP state machine out * of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE * state machine may transit to AUTHENTICATING state based on obsolete * eapSuccess and then trigger BE_AUTH to SUCCESS and PAE to * AUTHENTICATED without ever giving chance to EAP state machine to * reset the state. */ if (!ft_completed) { eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); eapol_sm_notify_portValid(wpa_s->eapol, FALSE); } if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed) eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); /* 802.1X::portControl = Auto */ eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE); wpa_s->eapol_received = 0; if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE || (wpa_s->current_ssid && wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) { if (wpa_s->current_ssid && wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) { /* * Set the key after having received joined-IBSS event * from the driver. */ wpa_supplicant_set_wpa_none_key(wpa_s, wpa_s->current_ssid); } wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); } else if (!ft_completed) { /* Timeout for receiving the first EAPOL packet */ wpa_supplicant_req_auth_timeout(wpa_s, 10, 0); } wpa_supplicant_cancel_scan(wpa_s); if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { /* * We are done; the driver will take care of RSN 4-way * handshake. */ wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); eapol_sm_notify_portValid(wpa_s->eapol, TRUE); eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { /* * The driver will take care of RSN 4-way handshake, so we need * to allow EAPOL supplicant to complete its work without * waiting for WPA supplicant. */ eapol_sm_notify_portValid(wpa_s->eapol, TRUE); } else if (ft_completed) { /* * FT protocol completed - make sure EAPOL state machine ends * up in authenticated. */ wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); eapol_sm_notify_portValid(wpa_s->eapol, TRUE); eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); } wpa_s->last_eapol_matches_bssid = 0; if (wpa_s->pending_eapol_rx) { struct os_reltime now, age; os_get_reltime(&now); os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age); if (age.sec == 0 && age.usec < 100000 && os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL " "frame that was received just before " "association notification"); wpa_supplicant_rx_eapol( wpa_s, wpa_s->pending_eapol_rx_src, wpabuf_head(wpa_s->pending_eapol_rx), wpabuf_len(wpa_s->pending_eapol_rx)); } wpabuf_free(wpa_s->pending_eapol_rx); wpa_s->pending_eapol_rx = NULL; } if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) && wpa_s->current_ssid && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) { /* Set static WEP keys again */ wpa_set_wep_keys(wpa_s, wpa_s->current_ssid); } #ifdef CONFIG_IBSS_RSN if (wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_IBSS && wpa_s->key_mgmt != WPA_KEY_MGMT_NONE && wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE && wpa_s->ibss_rsn == NULL) { wpa_s->ibss_rsn = ibss_rsn_init(wpa_s); if (!wpa_s->ibss_rsn) { wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN"); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk); } #endif /* CONFIG_IBSS_RSN */ wpas_wps_notify_assoc(wpa_s, bssid); if (data) { wmm_ac_notify_assoc(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, &data->assoc_info.wmm_params); if (wpa_s->reassoc_same_bss) wmm_ac_restore_tspecs(wpa_s); } } static int disconnect_reason_recoverable(u16 reason_code) { return reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY || reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA || reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA; } static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, u16 reason_code, int locally_generated) { const u8 *bssid; if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { /* * At least Host AP driver and a Prism3 card seemed to be * generating streams of disconnected events when configuring * IBSS for WPA-None. Ignore them for now. */ return; } bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; if (!is_zero_ether_addr(bssid) || wpa_s->wpa_state >= WPA_AUTHENTICATING) { wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR " reason=%d%s", MAC2STR(bssid), reason_code, locally_generated ? " locally_generated=1" : ""); } } static int could_be_psk_mismatch(struct wpa_supplicant *wpa_s, u16 reason_code, int locally_generated) { if (wpa_s->wpa_state != WPA_4WAY_HANDSHAKE || !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) return 0; /* Not in 4-way handshake with PSK */ /* * It looks like connection was lost while trying to go through PSK * 4-way handshake. Filter out known disconnection cases that are caused * by something else than PSK mismatch to avoid confusing reports. */ if (locally_generated) { if (reason_code == WLAN_REASON_IE_IN_4WAY_DIFFERS) return 0; } return 1; } static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, u16 reason_code, int locally_generated) { const u8 *bssid; int authenticating; u8 prev_pending_bssid[ETH_ALEN]; struct wpa_bss *fast_reconnect = NULL; struct wpa_ssid *fast_reconnect_ssid = NULL; struct wpa_ssid *last_ssid; authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING; os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN); if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { /* * At least Host AP driver and a Prism3 card seemed to be * generating streams of disconnected events when configuring * IBSS for WPA-None. Ignore them for now. */ wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in " "IBSS/WPA-None mode"); return; } if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) { wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " "pre-shared key may be incorrect"); if (wpas_p2p_4way_hs_failed(wpa_s) > 0) return; /* P2P group removed */ wpas_auth_failed(wpa_s, "WRONG_KEY"); } if (!wpa_s->disconnected && (!wpa_s->auto_reconnect_disabled || wpa_s->key_mgmt == WPA_KEY_MGMT_WPS || wpas_wps_searching(wpa_s))) { wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to " "reconnect (wps=%d/%d wpa_state=%d)", wpa_s->key_mgmt == WPA_KEY_MGMT_WPS, wpas_wps_searching(wpa_s), wpa_s->wpa_state); if (wpa_s->wpa_state == WPA_COMPLETED && wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_INFRA && !locally_generated && disconnect_reason_recoverable(reason_code)) { /* * It looks like the AP has dropped association with * us, but could allow us to get back in. Try to * reconnect to the same BSS without full scan to save * time for some common cases. */ fast_reconnect = wpa_s->current_bss; fast_reconnect_ssid = wpa_s->current_ssid; } else if (wpa_s->wpa_state >= WPA_ASSOCIATING) wpa_supplicant_req_scan(wpa_s, 0, 100000); else wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new " "immediate scan"); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect disabled: do not " "try to re-connect"); wpa_s->reassociate = 0; wpa_s->disconnected = 1; if (!wpa_s->pno) wpa_supplicant_cancel_sched_scan(wpa_s); } bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; if (wpa_s->wpa_state >= WPA_AUTHENTICATING) wpas_connection_failed(wpa_s, bssid); wpa_sm_notify_disassoc(wpa_s->wpa); if (locally_generated) wpa_s->disconnect_reason = -reason_code; else wpa_s->disconnect_reason = reason_code; wpas_notify_disconnect_reason(wpa_s); if (wpa_supplicant_dynamic_keys(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys"); wpa_clear_keys(wpa_s, wpa_s->bssid); } last_ssid = wpa_s->current_ssid; wpa_supplicant_mark_disassoc(wpa_s); if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) { sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid); wpa_s->current_ssid = last_ssid; } if (fast_reconnect && !wpas_network_disabled(wpa_s, fast_reconnect_ssid) && !disallowed_bssid(wpa_s, fast_reconnect->bssid) && !disallowed_ssid(wpa_s, fast_reconnect->ssid, fast_reconnect->ssid_len) && !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) { #ifndef CONFIG_NO_SCAN_PROCESSING wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS"); if (wpa_supplicant_connect(wpa_s, fast_reconnect, fast_reconnect_ssid) < 0) { /* Recover through full scan */ wpa_supplicant_req_scan(wpa_s, 0, 100000); } #endif /* CONFIG_NO_SCAN_PROCESSING */ } else if (fast_reconnect) { /* * Could not reconnect to the same BSS due to network being * disabled. Use a new scan to match the alternative behavior * above, i.e., to continue automatic reconnection attempt in a * way that enforces disabled network rules. */ wpa_supplicant_req_scan(wpa_s, 0, 100000); } } #ifdef CONFIG_DELAYED_MIC_ERROR_REPORT void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (!wpa_s->pending_mic_error_report) return; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Sending pending MIC error report"); wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise); wpa_s->pending_mic_error_report = 0; } #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ static void wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { int pairwise; struct os_reltime t; wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected"); pairwise = (data && data->michael_mic_failure.unicast); os_get_reltime(&t); if ((wpa_s->last_michael_mic_error.sec && !os_reltime_expired(&t, &wpa_s->last_michael_mic_error, 60)) || wpa_s->pending_mic_error_report) { if (wpa_s->pending_mic_error_report) { /* * Send the pending MIC error report immediately since * we are going to start countermeasures and AP better * do the same. */ wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise); } /* Send the new MIC error report immediately since we are going * to start countermeasures and AP better do the same. */ wpa_sm_key_request(wpa_s->wpa, 1, pairwise); /* initialize countermeasures */ wpa_s->countermeasures = 1; wpa_blacklist_add(wpa_s, wpa_s->bssid); wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started"); /* * Need to wait for completion of request frame. We do not get * any callback for the message completion, so just wait a * short while and hope for the best. */ os_sleep(0, 10000); wpa_drv_set_countermeasures(wpa_s, 1); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_MICHAEL_MIC_FAILURE); eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL); eloop_register_timeout(60, 0, wpa_supplicant_stop_countermeasures, wpa_s, NULL); /* TODO: mark the AP rejected for 60 second. STA is * allowed to associate with another AP.. */ } else { #ifdef CONFIG_DELAYED_MIC_ERROR_REPORT if (wpa_s->mic_errors_seen) { /* * Reduce the effectiveness of Michael MIC error * reports as a means for attacking against TKIP if * more than one MIC failure is noticed with the same * PTK. We delay the transmission of the reports by a * random time between 0 and 60 seconds in order to * force the attacker wait 60 seconds before getting * the information on whether a frame resulted in a MIC * failure. */ u8 rval[4]; int sec; if (os_get_random(rval, sizeof(rval)) < 0) sec = os_random() % 60; else sec = WPA_GET_BE32(rval) % 60; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Delay MIC error " "report %d seconds", sec); wpa_s->pending_mic_error_report = 1; wpa_s->pending_mic_error_pairwise = pairwise; eloop_cancel_timeout( wpa_supplicant_delayed_mic_error_report, wpa_s, NULL); eloop_register_timeout( sec, os_random() % 1000000, wpa_supplicant_delayed_mic_error_report, wpa_s, NULL); } else { wpa_sm_key_request(wpa_s->wpa, 1, pairwise); } #else /* CONFIG_DELAYED_MIC_ERROR_REPORT */ wpa_sm_key_request(wpa_s->wpa, 1, pairwise); #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ } wpa_s->last_michael_mic_error = t; wpa_s->mic_errors_seen++; } #ifdef CONFIG_TERMINATE_ONLASTIF static int any_interfaces(struct wpa_supplicant *head) { struct wpa_supplicant *wpa_s; for (wpa_s = head; wpa_s != NULL; wpa_s = wpa_s->next) if (!wpa_s->interface_removed) return 1; return 0; } #endif /* CONFIG_TERMINATE_ONLASTIF */ static void wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (os_strcmp(wpa_s->ifname, data->interface_status.ifname) != 0) return; switch (data->interface_status.ievent) { case EVENT_INTERFACE_ADDED: if (!wpa_s->interface_removed) break; wpa_s->interface_removed = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was added"); if (wpa_supplicant_driver_init(wpa_s) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the " "driver after interface was added"); } #ifdef CONFIG_P2P if (!wpa_s->global->p2p && !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && wpas_p2p_add_p2pdev_interface( wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) { wpa_printf(MSG_INFO, "P2P: Failed to enable P2P Device interface"); /* Try to continue without. P2P will be disabled. */ } #endif /* CONFIG_P2P */ break; case EVENT_INTERFACE_REMOVED: wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed"); wpa_s->interface_removed = 1; wpa_supplicant_mark_disassoc(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); l2_packet_deinit(wpa_s->l2); wpa_s->l2 = NULL; #ifdef CONFIG_P2P if (wpa_s->global->p2p && wpa_s->global->p2p_init_wpa_s->parent == wpa_s && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) { wpa_dbg(wpa_s, MSG_DEBUG, "Removing P2P Device interface"); wpa_supplicant_remove_iface( wpa_s->global, wpa_s->global->p2p_init_wpa_s, 0); wpa_s->global->p2p_init_wpa_s = NULL; } #endif /* CONFIG_P2P */ #ifdef CONFIG_TERMINATE_ONLASTIF /* check if last interface */ if (!any_interfaces(wpa_s->global->ifaces)) eloop_terminate(); #endif /* CONFIG_TERMINATE_ONLASTIF */ break; } } #ifdef CONFIG_PEERKEY static void wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (data == NULL) return; wpa_sm_stkstart(wpa_s->wpa, data->stkstart.peer); } #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_TDLS static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (data == NULL) return; switch (data->tdls.oper) { case TDLS_REQUEST_SETUP: wpa_tdls_remove(wpa_s->wpa, data->tdls.peer); if (wpa_tdls_is_external_setup(wpa_s->wpa)) wpa_tdls_start(wpa_s->wpa, data->tdls.peer); else wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, data->tdls.peer); break; case TDLS_REQUEST_TEARDOWN: if (wpa_tdls_is_external_setup(wpa_s->wpa)) wpa_tdls_teardown_link(wpa_s->wpa, data->tdls.peer, data->tdls.reason_code); else wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, data->tdls.peer); break; case TDLS_REQUEST_DISCOVER: wpa_tdls_send_discovery_request(wpa_s->wpa, data->tdls.peer); break; } } #endif /* CONFIG_TDLS */ #ifdef CONFIG_WNM static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (data == NULL) return; switch (data->wnm.oper) { case WNM_OPER_SLEEP: wpa_printf(MSG_DEBUG, "Start sending WNM-Sleep Request " "(action=%d, intval=%d)", data->wnm.sleep_action, data->wnm.sleep_intval); ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action, data->wnm.sleep_intval, NULL); break; } } #endif /* CONFIG_WNM */ #ifdef CONFIG_IEEE80211R static void wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (data == NULL) return; if (wpa_ft_process_response(wpa_s->wpa, data->ft_ies.ies, data->ft_ies.ies_len, data->ft_ies.ft_action, data->ft_ies.target_ap, data->ft_ies.ric_ies, data->ft_ies.ric_ies_len) < 0) { /* TODO: prevent MLME/driver from trying to associate? */ } } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IBSS_RSN static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_ssid *ssid; if (wpa_s->wpa_state < WPA_ASSOCIATED) return; if (data == NULL) return; ssid = wpa_s->current_ssid; if (ssid == NULL) return; if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt)) return; ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer); } static void wpa_supplicant_event_ibss_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid == NULL) return; /* check if the ssid is correctly configured as IBSS/RSN */ if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt)) return; ibss_rsn_handle_auth(wpa_s->ibss_rsn, data->rx_mgmt.frame, data->rx_mgmt.frame_len); } #endif /* CONFIG_IBSS_RSN */ #ifdef CONFIG_IEEE80211R static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data, size_t len) { const u8 *sta_addr, *target_ap_addr; u16 status; wpa_hexdump(MSG_MSGDUMP, "FT: RX Action", data, len); if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) return; /* only SME case supported for now */ if (len < 1 + 2 * ETH_ALEN + 2) return; if (data[0] != 2) return; /* Only FT Action Response is supported for now */ sta_addr = data + 1; target_ap_addr = data + 1 + ETH_ALEN; status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN); wpa_dbg(wpa_s, MSG_DEBUG, "FT: Received FT Action Response: STA " MACSTR " TargetAP " MACSTR " status %u", MAC2STR(sta_addr), MAC2STR(target_ap_addr), status); if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR " in FT Action Response", MAC2STR(sta_addr)); return; } if (status) { wpa_dbg(wpa_s, MSG_DEBUG, "FT: FT Action Response indicates " "failure (status code %d)", status); /* TODO: report error to FT code(?) */ return; } if (wpa_ft_process_response(wpa_s->wpa, data + 1 + 2 * ETH_ALEN + 2, len - (1 + 2 * ETH_ALEN + 2), 1, target_ap_addr, NULL, 0) < 0) return; #ifdef CONFIG_SME { struct wpa_bss *bss; bss = wpa_bss_get_bssid(wpa_s, target_ap_addr); if (bss) wpa_s->sme.freq = bss->freq; wpa_s->sme.auth_alg = WPA_AUTH_ALG_FT; sme_associate(wpa_s, WPAS_MODE_INFRA, target_ap_addr, WLAN_AUTH_FT); } #endif /* CONFIG_SME */ } #endif /* CONFIG_IEEE80211R */ static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s, struct unprot_deauth *e) { #ifdef CONFIG_IEEE80211W wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame " "dropped: " MACSTR " -> " MACSTR " (reason code %u)", MAC2STR(e->sa), MAC2STR(e->da), e->reason_code); sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code); #endif /* CONFIG_IEEE80211W */ } static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s, struct unprot_disassoc *e) { #ifdef CONFIG_IEEE80211W wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame " "dropped: " MACSTR " -> " MACSTR " (reason code %u)", MAC2STR(e->sa), MAC2STR(e->da), e->reason_code); sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code); #endif /* CONFIG_IEEE80211W */ } static void wpas_event_disconnect(struct wpa_supplicant *wpa_s, const u8 *addr, u16 reason_code, int locally_generated, const u8 *ie, size_t ie_len, int deauth) { #ifdef CONFIG_AP if (wpa_s->ap_iface && addr) { hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], addr); return; } if (wpa_s->ap_iface) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in AP mode"); return; } #endif /* CONFIG_AP */ if (!locally_generated) wpa_s->own_disconnect_req = 0; wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated); if (((reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED || ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) && eapol_sm_failed(wpa_s->eapol))) && !wpa_s->eap_expected_failure)) wpas_auth_failed(wpa_s, "AUTH_FAILED"); #ifdef CONFIG_P2P if (deauth && reason_code > 0) { if (wpas_p2p_deauth_notif(wpa_s, addr, reason_code, ie, ie_len, locally_generated) > 0) { /* * The interface was removed, so cannot continue * processing any additional operations after this. */ return; } } #endif /* CONFIG_P2P */ wpa_supplicant_event_disassoc_finish(wpa_s, reason_code, locally_generated); } static void wpas_event_disassoc(struct wpa_supplicant *wpa_s, struct disassoc_info *info) { u16 reason_code = 0; int locally_generated = 0; const u8 *addr = NULL; const u8 *ie = NULL; size_t ie_len = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification"); if (info) { addr = info->addr; ie = info->ie; ie_len = info->ie_len; reason_code = info->reason_code; locally_generated = info->locally_generated; wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", reason_code, locally_generated ? " (locally generated)" : ""); if (addr) wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, MAC2STR(addr)); wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)", ie, ie_len); } #ifdef CONFIG_AP if (wpa_s->ap_iface && info && info->addr) { hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], info->addr); return; } if (wpa_s->ap_iface) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in AP mode"); return; } #endif /* CONFIG_AP */ #ifdef CONFIG_P2P if (info) { wpas_p2p_disassoc_notif( wpa_s, info->addr, reason_code, info->ie, info->ie_len, locally_generated); } #endif /* CONFIG_P2P */ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_disassoc(wpa_s, info); wpas_event_disconnect(wpa_s, addr, reason_code, locally_generated, ie, ie_len, 0); } static void wpas_event_deauth(struct wpa_supplicant *wpa_s, struct deauth_info *info) { u16 reason_code = 0; int locally_generated = 0; const u8 *addr = NULL; const u8 *ie = NULL; size_t ie_len = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Deauthentication notification"); if (info) { addr = info->addr; ie = info->ie; ie_len = info->ie_len; reason_code = info->reason_code; locally_generated = info->locally_generated; wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", reason_code, locally_generated ? " (locally generated)" : ""); if (addr) { wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, MAC2STR(addr)); } wpa_hexdump(MSG_DEBUG, "Deauthentication frame IE(s)", ie, ie_len); } wpa_reset_ft_completed(wpa_s->wpa); wpas_event_disconnect(wpa_s, addr, reason_code, locally_generated, ie, ie_len, 1); } static const char * reg_init_str(enum reg_change_initiator init) { switch (init) { case REGDOM_SET_BY_CORE: return "CORE"; case REGDOM_SET_BY_USER: return "USER"; case REGDOM_SET_BY_DRIVER: return "DRIVER"; case REGDOM_SET_BY_COUNTRY_IE: return "COUNTRY_IE"; case REGDOM_BEACON_HINT: return "BEACON_HINT"; } return "?"; } static const char * reg_type_str(enum reg_type type) { switch (type) { case REGDOM_TYPE_UNKNOWN: return "UNKNOWN"; case REGDOM_TYPE_COUNTRY: return "COUNTRY"; case REGDOM_TYPE_WORLD: return "WORLD"; case REGDOM_TYPE_CUSTOM_WORLD: return "CUSTOM_WORLD"; case REGDOM_TYPE_INTERSECTION: return "INTERSECTION"; } return "?"; } static void wpa_supplicant_update_channel_list( struct wpa_supplicant *wpa_s, struct channel_list_changed *info) { struct wpa_supplicant *ifs; wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s", reg_init_str(info->initiator), reg_type_str(info->type), info->alpha2[0] ? " alpha2=" : "", info->alpha2[0] ? info->alpha2 : ""); if (wpa_s->drv_priv == NULL) return; /* Ignore event during drv initialization */ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { wpa_printf(MSG_DEBUG, "%s: Updating hw mode", ifs->ifname); free_hw_features(ifs); ifs->hw.modes = wpa_drv_get_hw_feature_data( ifs, &ifs->hw.num_modes, &ifs->hw.flags); } /* Restart sched_scan with updated channel list */ if (wpa_s->sched_scanning) { wpa_dbg(wpa_s, MSG_DEBUG, "Channel list changed restart sched scan."); wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER); } static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, const u8 *frame, size_t len, int freq, int rssi) { const struct ieee80211_mgmt *mgmt; const u8 *payload; size_t plen; u8 category; if (len < IEEE80211_HDRLEN + 2) return; mgmt = (const struct ieee80211_mgmt *) frame; payload = frame + IEEE80211_HDRLEN; category = *payload++; plen = len - IEEE80211_HDRLEN - 1; wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR " Category=%u DataLen=%d freq=%d MHz", MAC2STR(mgmt->sa), category, (int) plen, freq); if (category == WLAN_ACTION_WMM) { wmm_ac_rx_action(wpa_s, mgmt->da, mgmt->sa, payload, plen); return; } #ifdef CONFIG_IEEE80211R if (category == WLAN_ACTION_FT) { ft_rx_action(wpa_s, payload, plen); return; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W #ifdef CONFIG_SME if (category == WLAN_ACTION_SA_QUERY) { sme_sa_query_rx(wpa_s, mgmt->sa, payload, plen); return; } #endif /* CONFIG_SME */ #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM if (mgmt->u.action.category == WLAN_ACTION_WNM) { ieee802_11_rx_wnm_action(wpa_s, mgmt, len); return; } #endif /* CONFIG_WNM */ #ifdef CONFIG_GAS if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC || mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) && gas_query_rx(wpa_s->gas, mgmt->da, mgmt->sa, mgmt->bssid, mgmt->u.action.category, payload, plen, freq) == 0) return; #endif /* CONFIG_GAS */ #ifdef CONFIG_TDLS if (category == WLAN_ACTION_PUBLIC && plen >= 4 && payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) { wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery Response from " MACSTR, MAC2STR(mgmt->sa)); return; } #endif /* CONFIG_TDLS */ #ifdef CONFIG_INTERWORKING if (category == WLAN_ACTION_QOS && plen >= 1 && payload[0] == QOS_QOS_MAP_CONFIG) { const u8 *pos = payload + 1; size_t qlen = plen - 1; wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from " MACSTR, MAC2STR(mgmt->sa)); if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) == 0 && qlen > 2 && pos[0] == WLAN_EID_QOS_MAP_SET && pos[1] <= qlen - 2 && pos[1] >= 16) wpas_qos_map_set(wpa_s, pos + 2, pos[1]); return; } #endif /* CONFIG_INTERWORKING */ if (category == WLAN_ACTION_RADIO_MEASUREMENT && payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) { wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1); return; } if (category == WLAN_ACTION_RADIO_MEASUREMENT && payload[0] == WLAN_RRM_LINK_MEASUREMENT_REQUEST) { wpas_rrm_handle_link_measurement_request(wpa_s, mgmt->sa, payload + 1, plen - 1, rssi); return; } #ifdef CONFIG_FST if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) { fst_rx_action(wpa_s->fst, mgmt, len); return; } #endif /* CONFIG_FST */ wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, category, payload, plen, freq); if (wpa_s->ifmsh) mesh_mpm_action_rx(wpa_s, mgmt, len); } static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s, union wpa_event_data *event) { struct wpa_freq_range_list *list; char *str = NULL; list = &event->freq_range; if (list->num) str = freq_range_list_str(list); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AVOID_FREQ "ranges=%s", str ? str : ""); #ifdef CONFIG_P2P if (freq_range_list_parse(&wpa_s->global->p2p_go_avoid_freq, str)) { wpa_dbg(wpa_s, MSG_ERROR, "%s: Failed to parse freq range", __func__); } else { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event"); /* * The update channel flow will also take care of moving a GO * from the unsafe frequency if needed. */ wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_AVOID); } #endif /* CONFIG_P2P */ os_free(str); } static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { wpa_dbg(wpa_s, MSG_DEBUG, "Connection authorized by device, previous state %d", wpa_s->wpa_state); if (wpa_s->wpa_state == WPA_ASSOCIATED) { wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); eapol_sm_notify_portValid(wpa_s->eapol, TRUE); eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); } wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr); wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck, data->assoc_info.ptk_kck_len, data->assoc_info.ptk_kek, data->assoc_info.ptk_kek_len); } void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct wpa_supplicant *wpa_s = ctx; int resched; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && event != EVENT_INTERFACE_ENABLED && event != EVENT_INTERFACE_STATUS && event != EVENT_SCHED_SCAN_STOPPED) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore event %s (%d) while interface is disabled", event_to_string(event), event); return; } #ifndef CONFIG_NO_STDOUT_DEBUG { int level = MSG_DEBUG; if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) { const struct ieee80211_hdr *hdr; u16 fc; hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame; fc = le_to_host16(hdr->frame_control); if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) level = MSG_EXCESSIVE; } wpa_dbg(wpa_s, level, "Event %s (%d) received", event_to_string(event), event); } #endif /* CONFIG_NO_STDOUT_DEBUG */ switch (event) { case EVENT_AUTH: sme_event_auth(wpa_s, data); break; case EVENT_ASSOC: wpa_supplicant_event_assoc(wpa_s, data); if (data && data->assoc_info.authorized) wpa_supplicant_event_assoc_auth(wpa_s, data); break; case EVENT_DISASSOC: wpas_event_disassoc(wpa_s, data ? &data->disassoc_info : NULL); break; case EVENT_DEAUTH: wpas_event_deauth(wpa_s, data ? &data->deauth_info : NULL); break; case EVENT_MICHAEL_MIC_FAILURE: wpa_supplicant_event_michael_mic_failure(wpa_s, data); break; #ifndef CONFIG_NO_SCAN_PROCESSING case EVENT_SCAN_STARTED: os_get_reltime(&wpa_s->scan_start_time); if (wpa_s->own_scan_requested) { struct os_reltime diff; os_reltime_sub(&wpa_s->scan_start_time, &wpa_s->scan_trigger_time, &diff); wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds", diff.sec, diff.usec); wpa_s->own_scan_requested = 0; wpa_s->own_scan_running = 1; if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_scan_use_id) { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED "id=%u", wpa_s->manual_scan_id); } else { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED); } } else { wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan"); wpa_s->radio->external_scan_running = 1; wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED); } break; case EVENT_SCAN_RESULTS: if (os_reltime_initialized(&wpa_s->scan_start_time)) { struct os_reltime now, diff; os_get_reltime(&now); os_reltime_sub(&now, &wpa_s->scan_start_time, &diff); wpa_s->scan_start_time.sec = 0; wpa_s->scan_start_time.usec = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds", diff.sec, diff.usec); } if (wpa_supplicant_event_scan_results(wpa_s, data)) break; /* interface may have been removed */ wpa_s->own_scan_running = 0; wpa_s->radio->external_scan_running = 0; radio_work_check_next(wpa_s); break; #endif /* CONFIG_NO_SCAN_PROCESSING */ case EVENT_ASSOCINFO: wpa_supplicant_event_associnfo(wpa_s, data); break; case EVENT_INTERFACE_STATUS: wpa_supplicant_event_interface_status(wpa_s, data); break; case EVENT_PMKID_CANDIDATE: wpa_supplicant_event_pmkid_candidate(wpa_s, data); break; #ifdef CONFIG_PEERKEY case EVENT_STKSTART: wpa_supplicant_event_stkstart(wpa_s, data); break; #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_TDLS case EVENT_TDLS: wpa_supplicant_event_tdls(wpa_s, data); break; #endif /* CONFIG_TDLS */ #ifdef CONFIG_WNM case EVENT_WNM: wpa_supplicant_event_wnm(wpa_s, data); break; #endif /* CONFIG_WNM */ #ifdef CONFIG_IEEE80211R case EVENT_FT_RESPONSE: wpa_supplicant_event_ft_response(wpa_s, data); break; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IBSS_RSN case EVENT_IBSS_RSN_START: wpa_supplicant_event_ibss_rsn_start(wpa_s, data); break; #endif /* CONFIG_IBSS_RSN */ case EVENT_ASSOC_REJECT: if (data->assoc_reject.bssid) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT "bssid=" MACSTR " status_code=%u", MAC2STR(data->assoc_reject.bssid), data->assoc_reject.status_code); else wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT "status_code=%u", data->assoc_reject.status_code); if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_assoc_reject(wpa_s, data); else { const u8 *bssid = data->assoc_reject.bssid; if (bssid == NULL || is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; wpas_connection_failed(wpa_s, bssid); wpa_supplicant_mark_disassoc(wpa_s); } break; case EVENT_AUTH_TIMED_OUT: /* It is possible to get this event from earlier connection */ if (wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_MESH) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore AUTH_TIMED_OUT in mesh configuration"); break; } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_auth_timed_out(wpa_s, data); break; case EVENT_ASSOC_TIMED_OUT: /* It is possible to get this event from earlier connection */ if (wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_MESH) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore ASSOC_TIMED_OUT in mesh configuration"); break; } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_assoc_timed_out(wpa_s, data); break; case EVENT_TX_STATUS: wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR " type=%d stype=%d", MAC2STR(data->tx_status.dst), data->tx_status.type, data->tx_status.stype); #ifdef CONFIG_AP if (wpa_s->ap_iface == NULL) { #ifdef CONFIG_OFFCHANNEL if (data->tx_status.type == WLAN_FC_TYPE_MGMT && data->tx_status.stype == WLAN_FC_STYPE_ACTION) offchannel_send_action_tx_status( wpa_s, data->tx_status.dst, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack ? OFFCHANNEL_SEND_ACTION_SUCCESS : OFFCHANNEL_SEND_ACTION_NO_ACK); #endif /* CONFIG_OFFCHANNEL */ break; } #endif /* CONFIG_AP */ #ifdef CONFIG_OFFCHANNEL wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst=" MACSTR, MAC2STR(wpa_s->parent->pending_action_dst)); /* * Catch TX status events for Action frames we sent via group * interface in GO mode. */ if (data->tx_status.type == WLAN_FC_TYPE_MGMT && data->tx_status.stype == WLAN_FC_STYPE_ACTION && os_memcmp(wpa_s->parent->pending_action_dst, data->tx_status.dst, ETH_ALEN) == 0) { offchannel_send_action_tx_status( wpa_s->parent, data->tx_status.dst, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack ? OFFCHANNEL_SEND_ACTION_SUCCESS : OFFCHANNEL_SEND_ACTION_NO_ACK); break; } #endif /* CONFIG_OFFCHANNEL */ #ifdef CONFIG_AP switch (data->tx_status.type) { case WLAN_FC_TYPE_MGMT: ap_mgmt_tx_cb(wpa_s, data->tx_status.data, data->tx_status.data_len, data->tx_status.stype, data->tx_status.ack); break; case WLAN_FC_TYPE_DATA: ap_tx_status(wpa_s, data->tx_status.dst, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack); break; } #endif /* CONFIG_AP */ break; #ifdef CONFIG_AP case EVENT_EAPOL_TX_STATUS: ap_eapol_tx_status(wpa_s, data->eapol_tx_status.dst, data->eapol_tx_status.data, data->eapol_tx_status.data_len, data->eapol_tx_status.ack); break; case EVENT_DRIVER_CLIENT_POLL_OK: ap_client_poll_ok(wpa_s, data->client_poll.addr); break; case EVENT_RX_FROM_UNKNOWN: if (wpa_s->ap_iface == NULL) break; ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr, data->rx_from_unknown.wds); break; case EVENT_CH_SWITCH: if (!data) break; if (!wpa_s->ap_iface) { wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch " "event in non-AP mode"); break; } wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, data->ch_switch.ht_enabled, data->ch_switch.ch_offset, data->ch_switch.ch_width, data->ch_switch.cf1, data->ch_switch.cf2); break; #ifdef NEED_AP_MLME case EVENT_DFS_RADAR_DETECTED: if (data) wpas_event_dfs_radar_detected(wpa_s, &data->dfs_event); break; case EVENT_DFS_CAC_STARTED: if (data) wpas_event_dfs_cac_started(wpa_s, &data->dfs_event); break; case EVENT_DFS_CAC_FINISHED: if (data) wpas_event_dfs_cac_finished(wpa_s, &data->dfs_event); break; case EVENT_DFS_CAC_ABORTED: if (data) wpas_event_dfs_cac_aborted(wpa_s, &data->dfs_event); break; case EVENT_DFS_NOP_FINISHED: if (data) wpas_event_dfs_cac_nop_finished(wpa_s, &data->dfs_event); break; #endif /* NEED_AP_MLME */ #endif /* CONFIG_AP */ case EVENT_RX_MGMT: { u16 fc, stype; const struct ieee80211_mgmt *mgmt; #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->ext_mgmt_frame_handling) { struct rx_mgmt *rx = &data->rx_mgmt; size_t hex_len = 2 * rx->frame_len + 1; char *hex = os_malloc(hex_len); if (hex) { wpa_snprintf_hex(hex, hex_len, rx->frame, rx->frame_len); wpa_msg(wpa_s, MSG_INFO, "MGMT-RX freq=%d datarate=%u ssi_signal=%d %s", rx->freq, rx->datarate, rx->ssi_signal, hex); os_free(hex); } break; } #endif /* CONFIG_TESTING_OPTIONS */ mgmt = (const struct ieee80211_mgmt *) data->rx_mgmt.frame; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); #ifdef CONFIG_AP if (wpa_s->ap_iface == NULL) { #endif /* CONFIG_AP */ #ifdef CONFIG_P2P if (stype == WLAN_FC_STYPE_PROBE_REQ && data->rx_mgmt.frame_len > 24) { const u8 *src = mgmt->sa; const u8 *ie = mgmt->u.probe_req.variable; size_t ie_len = data->rx_mgmt.frame_len - (mgmt->u.probe_req.variable - data->rx_mgmt.frame); wpas_p2p_probe_req_rx( wpa_s, src, mgmt->da, mgmt->bssid, ie, ie_len, data->rx_mgmt.freq, data->rx_mgmt.ssi_signal); break; } #endif /* CONFIG_P2P */ #ifdef CONFIG_IBSS_RSN if (wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_IBSS && stype == WLAN_FC_STYPE_AUTH && data->rx_mgmt.frame_len >= 30) { wpa_supplicant_event_ibss_auth(wpa_s, data); break; } #endif /* CONFIG_IBSS_RSN */ if (stype == WLAN_FC_STYPE_ACTION) { wpas_event_rx_mgmt_action( wpa_s, data->rx_mgmt.frame, data->rx_mgmt.frame_len, data->rx_mgmt.freq, data->rx_mgmt.ssi_signal); break; } if (wpa_s->ifmsh) { mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt); break; } wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received " "management frame in non-AP mode"); break; #ifdef CONFIG_AP } if (stype == WLAN_FC_STYPE_PROBE_REQ && data->rx_mgmt.frame_len > 24) { const u8 *ie = mgmt->u.probe_req.variable; size_t ie_len = data->rx_mgmt.frame_len - (mgmt->u.probe_req.variable - data->rx_mgmt.frame); wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da, mgmt->bssid, ie, ie_len, data->rx_mgmt.ssi_signal); } ap_mgmt_rx(wpa_s, &data->rx_mgmt); #endif /* CONFIG_AP */ break; } case EVENT_RX_PROBE_REQ: if (data->rx_probe_req.sa == NULL || data->rx_probe_req.ie == NULL) break; #ifdef CONFIG_AP if (wpa_s->ap_iface) { hostapd_probe_req_rx(wpa_s->ap_iface->bss[0], data->rx_probe_req.sa, data->rx_probe_req.da, data->rx_probe_req.bssid, data->rx_probe_req.ie, data->rx_probe_req.ie_len, data->rx_probe_req.ssi_signal); break; } #endif /* CONFIG_AP */ wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa, data->rx_probe_req.da, data->rx_probe_req.bssid, data->rx_probe_req.ie, data->rx_probe_req.ie_len, 0, data->rx_probe_req.ssi_signal); break; case EVENT_REMAIN_ON_CHANNEL: #ifdef CONFIG_OFFCHANNEL offchannel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq, data->remain_on_channel.duration); #endif /* CONFIG_OFFCHANNEL */ wpas_p2p_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq, data->remain_on_channel.duration); break; case EVENT_CANCEL_REMAIN_ON_CHANNEL: #ifdef CONFIG_OFFCHANNEL offchannel_cancel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq); #endif /* CONFIG_OFFCHANNEL */ wpas_p2p_cancel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq); break; case EVENT_EAPOL_RX: wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src, data->eapol_rx.data, data->eapol_rx.data_len); break; case EVENT_SIGNAL_CHANGE: wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE "above=%d signal=%d noise=%d txrate=%d", data->signal_change.above_threshold, data->signal_change.current_signal, data->signal_change.current_noise, data->signal_change.current_txrate); wpa_bss_update_level(wpa_s->current_bss, data->signal_change.current_signal); bgscan_notify_signal_change( wpa_s, data->signal_change.above_threshold, data->signal_change.current_signal, data->signal_change.current_noise, data->signal_change.current_txrate); break; case EVENT_INTERFACE_ENABLED: wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled"); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_supplicant_update_mac_addr(wpa_s); if (wpa_s->p2p_mgmt) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); break; } #ifdef CONFIG_AP if (!wpa_s->ap_iface) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); } else wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); #else /* CONFIG_AP */ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); wpa_supplicant_req_scan(wpa_s, 0, 0); #endif /* CONFIG_AP */ } break; case EVENT_INTERFACE_DISABLED: wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled"); #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO || (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group && wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)) { /* * Mark interface disabled if this happens to end up not * being removed as a separate P2P group interface. */ wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); /* * The interface was externally disabled. Remove * it assuming an external entity will start a * new session if needed. */ if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group) wpas_p2p_interface_unavailable(wpa_s); else wpas_p2p_disconnect(wpa_s); /* * wpa_s instance may have been freed, so must not use * it here anymore. */ break; } if (wpa_s->p2p_scan_work && wpa_s->global->p2p && p2p_in_progress(wpa_s->global->p2p) > 1) { /* This radio work will be cancelled, so clear P2P * state as well. */ p2p_stop_find(wpa_s->global->p2p); } #endif /* CONFIG_P2P */ if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { /* * Indicate disconnection to keep ctrl_iface events * consistent. */ wpa_supplicant_event_disassoc( wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1); } wpa_supplicant_mark_disassoc(wpa_s); radio_remove_works(wpa_s, NULL, 0); wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); break; case EVENT_CHANNEL_LIST_CHANGED: wpa_supplicant_update_channel_list( wpa_s, &data->channel_list_changed); break; case EVENT_INTERFACE_UNAVAILABLE: wpas_p2p_interface_unavailable(wpa_s); break; case EVENT_BEST_CHANNEL: wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received " "(%d %d %d)", data->best_chan.freq_24, data->best_chan.freq_5, data->best_chan.freq_overall); wpa_s->best_24_freq = data->best_chan.freq_24; wpa_s->best_5_freq = data->best_chan.freq_5; wpa_s->best_overall_freq = data->best_chan.freq_overall; wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24, data->best_chan.freq_5, data->best_chan.freq_overall); break; case EVENT_UNPROT_DEAUTH: wpa_supplicant_event_unprot_deauth(wpa_s, &data->unprot_deauth); break; case EVENT_UNPROT_DISASSOC: wpa_supplicant_event_unprot_disassoc(wpa_s, &data->unprot_disassoc); break; case EVENT_STATION_LOW_ACK: #ifdef CONFIG_AP if (wpa_s->ap_iface && data) hostapd_event_sta_low_ack(wpa_s->ap_iface->bss[0], data->low_ack.addr); #endif /* CONFIG_AP */ #ifdef CONFIG_TDLS if (data) wpa_tdls_disable_unreachable_link(wpa_s->wpa, data->low_ack.addr); #endif /* CONFIG_TDLS */ break; case EVENT_IBSS_PEER_LOST: #ifdef CONFIG_IBSS_RSN ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer); #endif /* CONFIG_IBSS_RSN */ break; case EVENT_DRIVER_GTK_REKEY: if (os_memcmp(data->driver_gtk_rekey.bssid, wpa_s->bssid, ETH_ALEN)) break; if (!wpa_s->wpa) break; wpa_sm_update_replay_ctr(wpa_s->wpa, data->driver_gtk_rekey.replay_ctr); break; case EVENT_SCHED_SCAN_STOPPED: wpa_s->pno = 0; wpa_s->sched_scanning = 0; resched = wpa_s->scanning; wpa_supplicant_notify_scanning(wpa_s, 0); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) break; /* * Start a new sched scan to continue searching for more SSIDs * either if timed out or PNO schedule scan is pending. */ if (wpa_s->sched_scan_timed_out) { wpa_supplicant_req_sched_scan(wpa_s); } else if (wpa_s->pno_sched_pending) { wpa_s->pno_sched_pending = 0; wpas_start_pno(wpa_s); } else if (resched) { wpa_supplicant_req_scan(wpa_s, 0, 0); } break; case EVENT_WPS_BUTTON_PUSHED: #ifdef CONFIG_WPS wpas_wps_start_pbc(wpa_s, NULL, 0); #endif /* CONFIG_WPS */ break; case EVENT_AVOID_FREQUENCIES: wpa_supplicant_notify_avoid_freq(wpa_s, data); break; case EVENT_CONNECT_FAILED_REASON: #ifdef CONFIG_AP if (!wpa_s->ap_iface || !data) break; hostapd_event_connect_failed_reason( wpa_s->ap_iface->bss[0], data->connect_failed_reason.addr, data->connect_failed_reason.code); #endif /* CONFIG_AP */ break; case EVENT_NEW_PEER_CANDIDATE: #ifdef CONFIG_MESH if (!wpa_s->ifmsh || !data) break; wpa_mesh_notify_peer(wpa_s, data->mesh_peer.peer, data->mesh_peer.ies, data->mesh_peer.ie_len); #endif /* CONFIG_MESH */ break; default: wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; } } Index: releng/11.1/contrib/wpa/wpa_supplicant/wnm_sta.c =================================================================== --- releng/11.1/contrib/wpa/wpa_supplicant/wnm_sta.c (revision 324698) +++ releng/11.1/contrib/wpa/wpa_supplicant/wnm_sta.c (revision 324699) @@ -1,1148 +1,1164 @@ /* * wpa_supplicant - WNM * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "scan.h" #include "ctrl_iface.h" #include "bss.h" #include "wnm_sta.h" #include "hs20_supplicant.h" #define MAX_TFS_IE_LEN 1024 #define WNM_MAX_NEIGHBOR_REPORT 10 /* get the TFS IE from driver */ static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, u16 *buf_len, enum wnm_oper oper) { wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len); } /* set the TFS IE to driver */ static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, const u8 *addr, u8 *buf, u16 *buf_len, enum wnm_oper oper) { wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len); } /* MLME-SLEEPMODE.request */ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, u8 action, u16 intval, struct wpabuf *tfs_req) { struct ieee80211_mgmt *mgmt; int res; size_t len; struct wnm_sleep_element *wnmsleep_ie; u8 *wnmtfs_ie; u8 wnmsleep_ie_len; u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : WNM_SLEEP_TFS_REQ_IE_NONE; wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request " "action=%s to " MACSTR, action == 0 ? "enter" : "exit", MAC2STR(wpa_s->bssid)); /* WNM-Sleep Mode IE */ wnmsleep_ie_len = sizeof(struct wnm_sleep_element); wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element)); if (wnmsleep_ie == NULL) return -1; wnmsleep_ie->eid = WLAN_EID_WNMSLEEP; wnmsleep_ie->len = wnmsleep_ie_len - 2; wnmsleep_ie->action_type = action; wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT; wnmsleep_ie->intval = host_to_le16(intval); wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element", (u8 *) wnmsleep_ie, wnmsleep_ie_len); /* TFS IE(s) */ if (tfs_req) { wnmtfs_ie_len = wpabuf_len(tfs_req); wnmtfs_ie = os_malloc(wnmtfs_ie_len); if (wnmtfs_ie == NULL) { os_free(wnmsleep_ie); return -1; } os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len); } else { wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); if (wnmtfs_ie == NULL) { os_free(wnmsleep_ie); return -1; } if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len, tfs_oper)) { wnmtfs_ie_len = 0; os_free(wnmtfs_ie); wnmtfs_ie = NULL; } } wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element", (u8 *) wnmtfs_ie, wnmtfs_ie_len); mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len); if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Request action frame"); os_free(wnmsleep_ie); os_free(wnmtfs_ie); return -1; } os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); mgmt->u.action.category = WLAN_ACTION_WNM; mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ; mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1; os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie, wnmsleep_ie_len); /* copy TFS IE here */ if (wnmtfs_ie_len > 0) { os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); } len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + wnmtfs_ie_len; res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, &mgmt->u.action.category, len, 0); if (res < 0) wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " "(action=%d, intval=%d)", action, intval); + else + wpa_s->wnmsleep_used = 1; os_free(wnmsleep_ie); os_free(wnmtfs_ie); os_free(mgmt); return res; } static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, u8 *tfsresp_ie_start, u8 *tfsresp_ie_end) { wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, wpa_s->bssid, NULL, NULL); /* remove GTK/IGTK ?? */ /* set the TFS Resp IE(s) */ if (tfsresp_ie_start && tfsresp_ie_end && tfsresp_ie_end - tfsresp_ie_start >= 0) { u16 tfsresp_ie_len; tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) - tfsresp_ie_start; wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); /* pass the TFS Resp IE(s) to driver for processing */ if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, tfsresp_ie_start, &tfsresp_ie_len, WNM_SLEEP_TFS_RESP_IE_SET)) wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); } } static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, const u8 *frm, u16 key_len_total) { u8 *ptr, *end; u8 gtk_len; wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid, NULL, NULL); /* Install GTK/IGTK */ /* point to key data field */ ptr = (u8 *) frm + 1 + 2; end = ptr + key_len_total; wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total); + if (key_len_total && !wpa_sm_pmf_enabled(wpa_s->wpa)) { + wpa_msg(wpa_s, MSG_INFO, + "WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled"); + return; + } + while (ptr + 1 < end) { if (ptr + 2 + ptr[1] > end) { wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element " "length"); if (end > ptr) { wpa_hexdump(MSG_DEBUG, "WNM: Remaining data", ptr, end - ptr); } break; } if (*ptr == WNM_SLEEP_SUBELEM_GTK) { if (ptr[1] < 11 + 5) { wpa_printf(MSG_DEBUG, "WNM: Too short GTK " "subelem"); break; } gtk_len = *(ptr + 4); if (ptr[1] < 11 + gtk_len || gtk_len < 5 || gtk_len > 32) { wpa_printf(MSG_DEBUG, "WNM: Invalid GTK " "subelem"); break; } wpa_wnmsleep_install_key( wpa_s->wpa, WNM_SLEEP_SUBELEM_GTK, ptr); ptr += 13 + gtk_len; #ifdef CONFIG_IEEE80211W } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) { if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) { wpa_printf(MSG_DEBUG, "WNM: Too short IGTK " "subelem"); break; } wpa_wnmsleep_install_key(wpa_s->wpa, WNM_SLEEP_SUBELEM_IGTK, ptr); ptr += 10 + WPA_IGTK_LEN; #endif /* CONFIG_IEEE80211W */ } else break; /* skip the loop */ } } static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, const u8 *frm, int len) { /* * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data | * WNM-Sleep Mode IE | TFS Response IE */ u8 *pos = (u8 *) frm; /* point to payload after the action field */ u16 key_len_total; struct wnm_sleep_element *wnmsleep_ie = NULL; /* multiple TFS Resp IE (assuming consecutive) */ u8 *tfsresp_ie_start = NULL; u8 *tfsresp_ie_end = NULL; size_t left; + if (!wpa_s->wnmsleep_used) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested"); + return; + } + if (len < 3) return; key_len_total = WPA_GET_LE16(frm + 1); wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d", frm[0], key_len_total); left = len - 3; if (key_len_total > left) { wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); return; } pos += 3 + key_len_total; while (pos - frm < len) { u8 ie_len = *(pos + 1); if (pos + 2 + ie_len > frm + len) { wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len); break; } wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len); if (*pos == WLAN_EID_WNMSLEEP) wnmsleep_ie = (struct wnm_sleep_element *) pos; else if (*pos == WLAN_EID_TFS_RESP) { if (!tfsresp_ie_start) tfsresp_ie_start = pos; tfsresp_ie_end = pos; } else wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); pos += ie_len + 2; } if (!wnmsleep_ie) { wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); return; } + + wpa_s->wnmsleep_used = 0; if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " "frame (action=%d, intval=%d)", wnmsleep_ie->action_type, wnmsleep_ie->intval); if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) { wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start, tfsresp_ie_end); } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total); } } else { wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame " "(action=%d, intval=%d)", wnmsleep_ie->action_type, wnmsleep_ie->intval); if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL, wpa_s->bssid, NULL, NULL); else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL, wpa_s->bssid, NULL, NULL); } } void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) { int i; for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot); os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid); } wpa_s->wnm_num_neighbor_report = 0; os_free(wpa_s->wnm_neighbor_report_elements); wpa_s->wnm_neighbor_report_elements = NULL; } static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, u8 id, u8 elen, const u8 *pos) { switch (id) { case WNM_NEIGHBOR_TSF: if (elen < 2 + 2) { wpa_printf(MSG_DEBUG, "WNM: Too short TSF"); break; } rep->tsf_offset = WPA_GET_LE16(pos); rep->beacon_int = WPA_GET_LE16(pos + 2); rep->tsf_present = 1; break; case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING: if (elen < 2) { wpa_printf(MSG_DEBUG, "WNM: Too short condensed " "country string"); break; } os_memcpy(rep->country, pos, 2); rep->country_present = 1; break; case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE: if (elen < 1) { wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition " "candidate"); break; } rep->preference = pos[0]; rep->preference_present = 1; break; case WNM_NEIGHBOR_BSS_TERMINATION_DURATION: rep->bss_term_tsf = WPA_GET_LE64(pos); rep->bss_term_dur = WPA_GET_LE16(pos + 8); rep->bss_term_present = 1; break; case WNM_NEIGHBOR_BEARING: if (elen < 8) { wpa_printf(MSG_DEBUG, "WNM: Too short neighbor " "bearing"); break; } rep->bearing = WPA_GET_LE16(pos); rep->distance = WPA_GET_LE32(pos + 2); rep->rel_height = WPA_GET_LE16(pos + 2 + 4); rep->bearing_present = 1; break; case WNM_NEIGHBOR_MEASUREMENT_PILOT: if (elen < 1) { wpa_printf(MSG_DEBUG, "WNM: Too short measurement " "pilot"); break; } os_free(rep->meas_pilot); rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot)); if (rep->meas_pilot == NULL) break; rep->meas_pilot->measurement_pilot = pos[0]; rep->meas_pilot->subelem_len = elen - 1; os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1); break; case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES: if (elen < 5) { wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled " "capabilities"); break; } os_memcpy(rep->rm_capab, pos, 5); rep->rm_capab_present = 1; break; case WNM_NEIGHBOR_MULTIPLE_BSSID: if (elen < 1) { wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID"); break; } os_free(rep->mul_bssid); rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid)); if (rep->mul_bssid == NULL) break; rep->mul_bssid->max_bssid_indicator = pos[0]; rep->mul_bssid->subelem_len = elen - 1; os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1); break; } } static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) { struct wpa_bss *bss = wpa_s->current_bss; const char *country = NULL; if (bss) { const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY); if (elem && elem[1] >= 2) country = (const char *) (elem + 2); } return ieee80211_chan_to_freq(country, op_class, chan); } static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, const u8 *pos, u8 len, struct neighbor_report *rep) { u8 left = len; if (left < 13) { wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report"); return; } os_memcpy(rep->bssid, pos, ETH_ALEN); rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN); rep->regulatory_class = *(pos + 10); rep->channel_number = *(pos + 11); rep->phy_type = *(pos + 12); pos += 13; left -= 13; while (left >= 2) { u8 id, elen; id = *pos++; elen = *pos++; wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen); left -= 2; if (elen > left) { wpa_printf(MSG_DEBUG, "WNM: Truncated neighbor report subelement"); break; } wnm_parse_neighbor_report_elem(rep, id, elen, pos); left -= elen; pos += elen; } rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class, rep->channel_number); } static struct wpa_bss * compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) { u8 i; struct wpa_bss *bss = wpa_s->current_bss; struct wpa_bss *target; if (!bss) return 0; wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", MAC2STR(wpa_s->bssid), bss->level); for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; nei = &wpa_s->wnm_neighbor_report_elements[i]; if (nei->preference_present && nei->preference == 0) { wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR, MAC2STR(nei->bssid)); continue; } target = wpa_bss_get_bssid(wpa_s, nei->bssid); if (!target) { wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR " (pref %d) not found in scan results", MAC2STR(nei->bssid), nei->preference_present ? nei->preference : -1); continue; } if (bss->ssid_len != target->ssid_len || os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) { /* * TODO: Could consider allowing transition to another * ESS if PMF was enabled for the association. */ wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR " (pref %d) in different ESS", MAC2STR(nei->bssid), nei->preference_present ? nei->preference : -1); continue; } if (target->level < bss->level && target->level < -80) { wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR " (pref %d) does not have sufficient signal level (%d)", MAC2STR(nei->bssid), nei->preference_present ? nei->preference : -1, target->level); continue; } wpa_printf(MSG_DEBUG, "WNM: Found an acceptable preferred transition candidate BSS " MACSTR " (RSSI %d)", MAC2STR(nei->bssid), target->level); return target; } return NULL; } static void wnm_send_bss_transition_mgmt_resp( struct wpa_supplicant *wpa_s, u8 dialog_token, enum bss_trans_mgmt_status_code status, u8 delay, const u8 *target_bssid) { u8 buf[1000], *pos; struct ieee80211_mgmt *mgmt; size_t len; int res; wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response " "to " MACSTR " dialog_token=%u status=%u delay=%d", MAC2STR(wpa_s->bssid), dialog_token, status, delay); if (!wpa_s->current_bss) { wpa_printf(MSG_DEBUG, "WNM: Current BSS not known - drop response"); return; } mgmt = (struct ieee80211_mgmt *) buf; os_memset(&buf, 0, sizeof(buf)); os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); mgmt->u.action.category = WLAN_ACTION_WNM; mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP; mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token; mgmt->u.action.u.bss_tm_resp.status_code = status; mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay; pos = mgmt->u.action.u.bss_tm_resp.variable; if (target_bssid) { os_memcpy(pos, target_bssid, ETH_ALEN); pos += ETH_ALEN; } else if (status == WNM_BSS_TM_ACCEPT) { /* * P802.11-REVmc clarifies that the Target BSSID field is always * present when status code is zero, so use a fake value here if * no BSSID is yet known. */ os_memset(pos, 0, ETH_ALEN); pos += ETH_ALEN; } len = pos - (u8 *) &mgmt->u.action.category; res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, &mgmt->u.action.category, len, 0); if (res < 0) { wpa_printf(MSG_DEBUG, "WNM: Failed to send BSS Transition Management Response"); } } int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) { struct wpa_bss *bss; struct wpa_ssid *ssid = wpa_s->current_ssid; enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED; if (!wpa_s->wnm_neighbor_report_elements) return 0; if (os_reltime_before(&wpa_s->wnm_cand_valid_until, &wpa_s->scan_trigger_time)) { wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it"); wnm_deallocate_memory(wpa_s); return 0; } if (!wpa_s->current_bss || os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it"); return 0; } /* Compare the Neighbor Report and scan results */ bss = compare_scan_neighbor_results(wpa_s); if (!bss) { wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; goto send_bss_resp_fail; } /* Associate to the network */ /* Send the BSS Management Response - Accept */ if (wpa_s->wnm_reply) { wpa_s->wnm_reply = 0; wnm_send_bss_transition_mgmt_resp(wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT, 0, bss->bssid); } if (bss == wpa_s->current_bss) { wpa_printf(MSG_DEBUG, "WNM: Already associated with the preferred candidate"); return 1; } wpa_s->reassociate = 1; wpa_supplicant_connect(wpa_s, bss, ssid); wnm_deallocate_memory(wpa_s); return 1; send_bss_resp_fail: if (!reply_on_fail) return 0; /* Send reject response for all the failures */ if (wpa_s->wnm_reply) { wpa_s->wnm_reply = 0; wnm_send_bss_transition_mgmt_resp(wpa_s, wpa_s->wnm_dialog_token, status, 0, NULL); } wnm_deallocate_memory(wpa_s); return 0; } static int cand_pref_compar(const void *a, const void *b) { const struct neighbor_report *aa = a; const struct neighbor_report *bb = b; if (!aa->preference_present && !bb->preference_present) return 0; if (!aa->preference_present) return 1; if (!bb->preference_present) return -1; if (bb->preference > aa->preference) return 1; if (bb->preference < aa->preference) return -1; return 0; } static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s) { if (!wpa_s->wnm_neighbor_report_elements) return; qsort(wpa_s->wnm_neighbor_report_elements, wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report), cand_pref_compar); } static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s) { unsigned int i; wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List"); if (!wpa_s->wnm_neighbor_report_elements) return; for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; nei = &wpa_s->wnm_neighbor_report_elements[i]; wpa_printf(MSG_DEBUG, "%u: " MACSTR " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d", i, MAC2STR(nei->bssid), nei->bssid_info, nei->regulatory_class, nei->channel_number, nei->phy_type, nei->preference_present ? nei->preference : -1, nei->freq); } } static int chan_supported(struct wpa_supplicant *wpa_s, int freq) { unsigned int i; for (i = 0; i < wpa_s->hw.num_modes; i++) { struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; int j; for (j = 0; j < mode->num_channels; j++) { struct hostapd_channel_data *chan; chan = &mode->channels[j]; if (chan->freq == freq && !(chan->flag & HOSTAPD_CHAN_DISABLED)) return 1; } } return 0; } static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) { int *freqs; int num_freqs = 0; unsigned int i; if (!wpa_s->wnm_neighbor_report_elements) return; if (wpa_s->hw.modes == NULL) return; os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int)); if (freqs == NULL) return; for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; nei = &wpa_s->wnm_neighbor_report_elements[i]; if (nei->freq <= 0) { wpa_printf(MSG_DEBUG, "WNM: Unknown neighbor operating frequency for " MACSTR " - scan all channels", MAC2STR(nei->bssid)); os_free(freqs); return; } if (chan_supported(wpa_s, nei->freq)) add_freq(freqs, &num_freqs, nei->freq); } if (num_freqs == 0) { os_free(freqs); return; } wpa_printf(MSG_DEBUG, "WNM: Scan %d frequencies based on transition candidate list", num_freqs); wpa_s->next_scan_freqs = freqs; } static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end, int reply) { unsigned int beacon_int; u8 valid_int; if (pos + 5 > end) return; if (wpa_s->current_bss) beacon_int = wpa_s->current_bss->beacon_int; else beacon_int = 100; /* best guess */ wpa_s->wnm_dialog_token = pos[0]; wpa_s->wnm_mode = pos[1]; wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); valid_int = pos[4]; wpa_s->wnm_reply = reply; wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " "dialog_token=%u request_mode=0x%x " "disassoc_timer=%u validity_interval=%u", wpa_s->wnm_dialog_token, wpa_s->wnm_mode, wpa_s->wnm_dissoc_timer, valid_int); pos += 5; if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { if (pos + 12 > end) { wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); return; } os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); pos += 12; /* BSS Termination Duration */ } if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { char url[256]; if (pos + 1 > end || pos + 1 + pos[0] > end) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " "Management Request (URL)"); return; } os_memcpy(url, pos + 1, pos[0]); url[pos[0]] = '\0'; pos += 1 + pos[0]; wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", wpa_sm_pmf_enabled(wpa_s->wpa), wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); } if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) { /* TODO: mark current BSS less preferred for * selection */ wpa_printf(MSG_DEBUG, "Trying to find another BSS"); wpa_supplicant_req_scan(wpa_s, 0, 0); } } if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { unsigned int valid_ms; wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); wnm_deallocate_memory(wpa_s); wpa_s->wnm_neighbor_report_elements = os_calloc( WNM_MAX_NEIGHBOR_REPORT, sizeof(struct neighbor_report)); if (wpa_s->wnm_neighbor_report_elements == NULL) return; while (pos + 2 <= end && wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) { u8 tag = *pos++; u8 len = *pos++; wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", tag); if (pos + len > end) { wpa_printf(MSG_DEBUG, "WNM: Truncated request"); return; } if (tag == WLAN_EID_NEIGHBOR_REPORT) { struct neighbor_report *rep; rep = &wpa_s->wnm_neighbor_report_elements[ wpa_s->wnm_num_neighbor_report]; wnm_parse_neighbor_report(wpa_s, pos, len, rep); } pos += len; wpa_s->wnm_num_neighbor_report++; } wnm_sort_cand_list(wpa_s); wnm_dump_cand_list(wpa_s); valid_ms = valid_int * beacon_int * 128 / 125; wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", valid_ms); os_get_reltime(&wpa_s->wnm_cand_valid_until); wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; wpa_s->wnm_cand_valid_until.sec += wpa_s->wnm_cand_valid_until.usec / 1000000; wpa_s->wnm_cand_valid_until.usec %= 1000000; os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN); if (wpa_s->last_scan_res_used > 0) { struct os_reltime now; os_get_reltime(&now); if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) { wpa_printf(MSG_DEBUG, "WNM: Try to use recent scan results"); if (wnm_scan_process(wpa_s, 0) > 0) return; wpa_printf(MSG_DEBUG, "WNM: No match in previous scan results - try a new scan"); } } wnm_set_scan_freqs(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } else if (reply) { enum bss_trans_mgmt_status_code status; if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) status = WNM_BSS_TM_ACCEPT; else { wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); status = WNM_BSS_TM_REJECT_UNSPECIFIED; } wnm_send_bss_transition_mgmt_resp(wpa_s, wpa_s->wnm_dialog_token, status, 0, NULL); } } int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, u8 query_reason) { u8 buf[1000], *pos; struct ieee80211_mgmt *mgmt; size_t len; int ret; wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " MACSTR " query_reason=%u", MAC2STR(wpa_s->bssid), query_reason); mgmt = (struct ieee80211_mgmt *) buf; os_memset(&buf, 0, sizeof(buf)); os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); mgmt->u.action.category = WLAN_ACTION_WNM; mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY; mgmt->u.action.u.bss_tm_query.dialog_token = 1; mgmt->u.action.u.bss_tm_query.query_reason = query_reason; pos = mgmt->u.action.u.bss_tm_query.variable; len = pos - (u8 *) &mgmt->u.action.category; ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, &mgmt->u.action.category, len, 0); return ret; } static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *data, int len) { const u8 *pos, *end, *next; u8 ie, ie_len; pos = data; end = data + len; while (pos + 1 < end) { ie = *pos++; ie_len = *pos++; wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u", ie, ie_len); if (ie_len > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Not enough room for " "subelement"); break; } next = pos + ie_len; if (ie_len < 4) { pos = next; continue; } wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u", WPA_GET_BE24(pos), pos[3]); #ifdef CONFIG_HS20 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && WPA_GET_BE24(pos) == OUI_WFA && pos[3] == HS20_WNM_SUB_REM_NEEDED) { /* Subscription Remediation subelement */ const u8 *ie_end; u8 url_len; char *url; u8 osu_method; wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation " "subelement"); ie_end = pos + ie_len; pos += 4; url_len = *pos++; if (url_len == 0) { wpa_printf(MSG_DEBUG, "WNM: No Server URL included"); url = NULL; osu_method = 1; } else { if (pos + url_len + 1 > ie_end) { wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)", url_len, (int) (ie_end - pos)); break; } url = os_malloc(url_len + 1); if (url == NULL) break; os_memcpy(url, pos, url_len); url[url_len] = '\0'; osu_method = pos[url_len]; } hs20_rx_subscription_remediation(wpa_s, url, osu_method); os_free(url); pos = next; continue; } if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 && WPA_GET_BE24(pos) == OUI_WFA && pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) { const u8 *ie_end; u8 url_len; char *url; u8 code; u16 reauth_delay; ie_end = pos + ie_len; pos += 4; code = *pos++; reauth_delay = WPA_GET_LE16(pos); pos += 2; url_len = *pos++; wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication " "Imminent - Reason Code %u " "Re-Auth Delay %u URL Length %u", code, reauth_delay, url_len); if (pos + url_len > ie_end) break; url = os_malloc(url_len + 1); if (url == NULL) break; os_memcpy(url, pos, url_len); url[url_len] = '\0'; hs20_rx_deauth_imminent_notice(wpa_s, code, reauth_delay, url); os_free(url); pos = next; continue; } #endif /* CONFIG_HS20 */ pos = next; } } static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *frm, int len) { const u8 *pos, *end; u8 dialog_token, type; /* Dialog Token [1] | Type [1] | Subelements */ if (len < 2 || sa == NULL) return; end = frm + len; pos = frm; dialog_token = *pos++; type = *pos++; wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request " "(dialog_token %u type %u sa " MACSTR ")", dialog_token, type, MAC2STR(sa)); wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements", pos, end - pos); if (wpa_s->wpa_state != WPA_COMPLETED || os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not " "from our AP - ignore it"); return; } switch (type) { case 1: ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos); break; default: wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown " "WNM-Notification type %u", type); break; } } void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len) { const u8 *pos, *end; u8 act; if (len < IEEE80211_HDRLEN + 2) return; pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; act = *pos++; end = ((const u8 *) mgmt) + len; wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, act, MAC2STR(mgmt->sa)); if (wpa_s->wpa_state < WPA_ASSOCIATED || os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action " "frame"); return; } switch (act) { case WNM_BSS_TRANS_MGMT_REQ: ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end, !(mgmt->da[0] & 0x01)); break; case WNM_SLEEP_MODE_RESP: ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos); break; case WNM_NOTIFICATION_REQ: ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); break; default: wpa_printf(MSG_ERROR, "WNM: Unknown request"); break; } } Index: releng/11.1/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h =================================================================== --- releng/11.1/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h (revision 324698) +++ releng/11.1/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h (revision 324699) @@ -1,1170 +1,1171 @@ /* * wpa_supplicant - Internal definitions * Copyright (c) 2003-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_SUPPLICANT_I_H #define WPA_SUPPLICANT_I_H #include "utils/list.h" #include "common/defs.h" #include "common/sae.h" #include "common/wpa_ctrl.h" #include "wps/wps_defs.h" #include "config_ssid.h" #include "wmm_ac.h" extern const char *const wpa_supplicant_version; extern const char *const wpa_supplicant_license; #ifndef CONFIG_NO_STDOUT_DEBUG extern const char *const wpa_supplicant_full_license1; extern const char *const wpa_supplicant_full_license2; extern const char *const wpa_supplicant_full_license3; extern const char *const wpa_supplicant_full_license4; extern const char *const wpa_supplicant_full_license5; #endif /* CONFIG_NO_STDOUT_DEBUG */ struct wpa_sm; struct wpa_supplicant; struct ibss_rsn; struct scan_info; struct wpa_bss; struct wpa_scan_results; struct hostapd_hw_modes; struct wpa_driver_associate_params; /* * Forward declarations of private structures used within the ctrl_iface * backends. Other parts of wpa_supplicant do not have access to data stored in * these structures. */ struct ctrl_iface_priv; struct ctrl_iface_global_priv; struct wpas_dbus_priv; /** * struct wpa_interface - Parameters for wpa_supplicant_add_iface() */ struct wpa_interface { /** * confname - Configuration name (file or profile) name * * This can also be %NULL when a configuration file is not used. In * that case, ctrl_interface must be set to allow the interface to be * configured. */ const char *confname; /** * confanother - Additional configuration name (file or profile) name * * This can also be %NULL when the additional configuration file is not * used. */ const char *confanother; /** * ctrl_interface - Control interface parameter * * If a configuration file is not used, this variable can be used to * set the ctrl_interface parameter that would have otherwise been read * from the configuration file. If both confname and ctrl_interface are * set, ctrl_interface is used to override the value from configuration * file. */ const char *ctrl_interface; /** * driver - Driver interface name, or %NULL to use the default driver */ const char *driver; /** * driver_param - Driver interface parameters * * If a configuration file is not used, this variable can be used to * set the driver_param parameters that would have otherwise been read * from the configuration file. If both confname and driver_param are * set, driver_param is used to override the value from configuration * file. */ const char *driver_param; /** * ifname - Interface name */ const char *ifname; /** * bridge_ifname - Optional bridge interface name * * If the driver interface (ifname) is included in a Linux bridge * device, the bridge interface may need to be used for receiving EAPOL * frames. This can be enabled by setting this variable to enable * receiving of EAPOL frames from an additional interface. */ const char *bridge_ifname; /** * p2p_mgmt - Interface used for P2P management (P2P Device operations) * * Indicates whether wpas_p2p_init() must be called for this interface. * This is used only when the driver supports a dedicated P2P Device * interface that is not a network interface. */ int p2p_mgmt; }; /** * struct wpa_params - Parameters for wpa_supplicant_init() */ struct wpa_params { /** * daemonize - Run %wpa_supplicant in the background */ int daemonize; /** * wait_for_monitor - Wait for a monitor program before starting */ int wait_for_monitor; /** * pid_file - Path to a PID (process ID) file * * If this and daemonize are set, process ID of the background process * will be written to the specified file. */ char *pid_file; /** * wpa_debug_level - Debugging verbosity level (e.g., MSG_INFO) */ int wpa_debug_level; /** * wpa_debug_show_keys - Whether keying material is included in debug * * This parameter can be used to allow keying material to be included * in debug messages. This is a security risk and this option should * not be enabled in normal configuration. If needed during * development or while troubleshooting, this option can provide more * details for figuring out what is happening. */ int wpa_debug_show_keys; /** * wpa_debug_timestamp - Whether to include timestamp in debug messages */ int wpa_debug_timestamp; /** * ctrl_interface - Global ctrl_iface path/parameter */ char *ctrl_interface; /** * ctrl_interface_group - Global ctrl_iface group */ char *ctrl_interface_group; /** * dbus_ctrl_interface - Enable the DBus control interface */ int dbus_ctrl_interface; /** * wpa_debug_file_path - Path of debug file or %NULL to use stdout */ const char *wpa_debug_file_path; /** * wpa_debug_syslog - Enable log output through syslog */ int wpa_debug_syslog; /** * wpa_debug_tracing - Enable log output through Linux tracing */ int wpa_debug_tracing; /** * override_driver - Optional driver parameter override * * This parameter can be used to override the driver parameter in * dynamic interface addition to force a specific driver wrapper to be * used instead. */ char *override_driver; /** * override_ctrl_interface - Optional ctrl_interface override * * This parameter can be used to override the ctrl_interface parameter * in dynamic interface addition to force a control interface to be * created. */ char *override_ctrl_interface; /** * entropy_file - Optional entropy file * * This parameter can be used to configure wpa_supplicant to maintain * its internal entropy store over restarts. */ char *entropy_file; #ifdef CONFIG_P2P /** * conf_p2p_dev - Configuration file used to hold the * P2P Device configuration parameters. * * This can also be %NULL. In such a case, if a P2P Device dedicated * interfaces is created, the main configuration file will be used. */ char *conf_p2p_dev; #endif /* CONFIG_P2P */ }; struct p2p_srv_bonjour { struct dl_list list; struct wpabuf *query; struct wpabuf *resp; }; struct p2p_srv_upnp { struct dl_list list; u8 version; char *service; }; /** * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces * * This structure is initialized by calling wpa_supplicant_init() when starting * %wpa_supplicant. */ struct wpa_global { struct wpa_supplicant *ifaces; struct wpa_params params; struct ctrl_iface_global_priv *ctrl_iface; struct wpas_dbus_priv *dbus; void **drv_priv; size_t drv_count; struct os_time suspend_time; struct p2p_data *p2p; struct wpa_supplicant *p2p_init_wpa_s; struct wpa_supplicant *p2p_group_formation; struct wpa_supplicant *p2p_invite_group; u8 p2p_dev_addr[ETH_ALEN]; struct os_reltime p2p_go_wait_client; struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */ struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */ int p2p_disabled; int cross_connection; struct wpa_freq_range_list p2p_disallow_freq; struct wpa_freq_range_list p2p_go_avoid_freq; enum wpa_conc_pref { WPA_CONC_PREF_NOT_SET, WPA_CONC_PREF_STA, WPA_CONC_PREF_P2P } conc_pref; unsigned int p2p_per_sta_psk:1; unsigned int p2p_fail_on_wps_complete:1; unsigned int p2p_24ghz_social_channels:1; unsigned int pending_p2ps_group:1; unsigned int pending_group_iface_for_p2ps:1; #ifdef CONFIG_WIFI_DISPLAY int wifi_display; #define MAX_WFD_SUBELEMS 10 struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS]; #endif /* CONFIG_WIFI_DISPLAY */ struct psk_list_entry *add_psk; /* From group formation */ }; /** * struct wpa_radio - Internal data for per-radio information * * This structure is used to share data about configured interfaces * (struct wpa_supplicant) that share the same physical radio, e.g., to allow * better coordination of offchannel operations. */ struct wpa_radio { char name[16]; /* from driver_ops get_radio_name() or empty if not * available */ unsigned int external_scan_running:1; struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */ struct dl_list work; /* struct wpa_radio_work::list entries */ }; /** * struct wpa_radio_work - Radio work item */ struct wpa_radio_work { struct dl_list list; unsigned int freq; /* known frequency (MHz) or 0 for multiple/unknown */ const char *type; struct wpa_supplicant *wpa_s; void (*cb)(struct wpa_radio_work *work, int deinit); void *ctx; unsigned int started:1; struct os_reltime time; }; int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, const char *type, int next, void (*cb)(struct wpa_radio_work *work, int deinit), void *ctx); void radio_work_done(struct wpa_radio_work *work); void radio_remove_works(struct wpa_supplicant *wpa_s, const char *type, int remove_all); void radio_work_check_next(struct wpa_supplicant *wpa_s); struct wpa_radio_work * radio_work_pending(struct wpa_supplicant *wpa_s, const char *type); struct wpa_connect_work { unsigned int sme:1; unsigned int bss_removed:1; struct wpa_bss *bss; struct wpa_ssid *ssid; }; int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss, struct wpa_ssid *test_ssid); void wpas_connect_work_free(struct wpa_connect_work *cwork); void wpas_connect_work_done(struct wpa_supplicant *wpa_s); struct wpa_external_work { unsigned int id; char type[100]; unsigned int timeout; }; /** * offchannel_send_action_result - Result of offchannel send Action frame */ enum offchannel_send_action_result { OFFCHANNEL_SEND_ACTION_SUCCESS /**< Frame was send and acknowledged */, OFFCHANNEL_SEND_ACTION_NO_ACK /**< Frame was sent, but not acknowledged */, OFFCHANNEL_SEND_ACTION_FAILED /**< Frame was not sent due to a failure */ }; struct wps_ap_info { u8 bssid[ETH_ALEN]; enum wps_ap_info_type { WPS_AP_NOT_SEL_REG, WPS_AP_SEL_REG, WPS_AP_SEL_REG_OUR } type; unsigned int tries; struct os_reltime last_attempt; unsigned int pbc_active; u8 uuid[WPS_UUID_LEN]; }; struct wpa_ssid_value { u8 ssid[SSID_MAX_LEN]; size_t ssid_len; }; #define WPA_FREQ_USED_BY_INFRA_STATION BIT(0) #define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1) struct wpa_used_freq_data { int freq; unsigned int flags; }; #define RRM_NEIGHBOR_REPORT_TIMEOUT 1 /* 1 second for AP to send a report */ /* * struct rrm_data - Data used for managing RRM features */ struct rrm_data { /* rrm_used - indication regarding the current connection */ unsigned int rrm_used:1; /* * notify_neighbor_rep - Callback for notifying report requester */ void (*notify_neighbor_rep)(void *ctx, struct wpabuf *neighbor_rep); /* * neighbor_rep_cb_ctx - Callback context * Received in the callback registration, and sent to the callback * function as a parameter. */ void *neighbor_rep_cb_ctx; /* next_neighbor_rep_token - Next request's dialog token */ u8 next_neighbor_rep_token; }; enum wpa_supplicant_test_failure { WPAS_TEST_FAILURE_NONE, WPAS_TEST_FAILURE_SCAN_TRIGGER, }; /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * * This structure contains the internal data for core wpa_supplicant code. This * should be only used directly from the core code. However, a pointer to this * data is used from other files as an arbitrary context pointer in calls to * core functions. */ struct wpa_supplicant { struct wpa_global *global; struct wpa_radio *radio; /* shared radio context */ struct dl_list radio_list; /* list head: struct wpa_radio::ifaces */ struct wpa_supplicant *parent; struct wpa_supplicant *next; struct l2_packet_data *l2; struct l2_packet_data *l2_br; unsigned char own_addr[ETH_ALEN]; unsigned char perm_addr[ETH_ALEN]; char ifname[100]; #ifdef CONFIG_CTRL_IFACE_DBUS char *dbus_path; #endif /* CONFIG_CTRL_IFACE_DBUS */ #ifdef CONFIG_CTRL_IFACE_DBUS_NEW char *dbus_new_path; char *dbus_groupobj_path; #ifdef CONFIG_AP char *preq_notify_peer; #endif /* CONFIG_AP */ #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ char bridge_ifname[16]; char *confname; char *confanother; struct wpa_config *conf; int countermeasures; struct os_reltime last_michael_mic_error; u8 bssid[ETH_ALEN]; u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this * field contains the target BSSID. */ int reassociate; /* reassociation requested */ int reassoc_same_bss; /* reassociating to the same bss */ int disconnected; /* all connections disabled; i.e., do no reassociate * before this has been cleared */ struct wpa_ssid *current_ssid; struct wpa_ssid *last_ssid; struct wpa_bss *current_bss; int ap_ies_from_associnfo; unsigned int assoc_freq; /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ int pairwise_cipher; int group_cipher; int key_mgmt; int wpa_proto; int mgmt_group_cipher; void *drv_priv; /* private data used by driver_ops */ void *global_drv_priv; u8 *bssid_filter; size_t bssid_filter_count; u8 *disallow_aps_bssid; size_t disallow_aps_bssid_count; struct wpa_ssid_value *disallow_aps_ssid; size_t disallow_aps_ssid_count; enum set_band setband; /* Preferred network for the next connection attempt */ struct wpa_ssid *next_ssid; /* previous scan was wildcard when interleaving between * wildcard scans and specific SSID scan when max_ssids=1 */ int prev_scan_wildcard; struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID; * NULL = not yet initialized (start * with wildcard SSID) * WILDCARD_SSID_SCAN = wildcard * SSID was used in the previous scan */ #define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1) struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */ int sched_scan_timeout; int sched_scan_interval; int first_sched_scan; int sched_scan_timed_out; void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); struct dl_list bss; /* struct wpa_bss::list */ struct dl_list bss_id; /* struct wpa_bss::list_id */ size_t num_bss; unsigned int bss_update_idx; unsigned int bss_next_id; /* * Pointers to BSS entries in the order they were in the last scan * results. */ struct wpa_bss **last_scan_res; unsigned int last_scan_res_used; unsigned int last_scan_res_size; struct os_reltime last_scan; const struct wpa_driver_ops *driver; int interface_removed; /* whether the network interface has been * removed */ struct wpa_sm *wpa; struct eapol_sm *eapol; struct ctrl_iface_priv *ctrl_iface; enum wpa_states wpa_state; struct wpa_radio_work *scan_work; int scanning; int sched_scanning; int new_connection; int eapol_received; /* number of EAPOL packets received after the * previous association event */ struct scard_data *scard; char imsi[20]; int mnc_len; unsigned char last_eapol_src[ETH_ALEN]; unsigned int keys_cleared; /* bitfield of key indexes that the driver is * known not to be configured with a key */ struct wpa_blacklist *blacklist; /** * extra_blacklist_count - Sum of blacklist counts after last connection * * This variable is used to maintain a count of temporary blacklisting * failures (maximum number for any BSS) over blacklist clear * operations. This is needed for figuring out whether there has been * failures prior to the last blacklist clear operation which happens * whenever no other not-blacklisted BSS candidates are available. This * gets cleared whenever a connection has been established successfully. */ int extra_blacklist_count; /** * scan_req - Type of the scan request */ enum scan_req_type { /** * NORMAL_SCAN_REQ - Normal scan request * * This is used for scans initiated by wpa_supplicant to find an * AP for a connection. */ NORMAL_SCAN_REQ, /** * INITIAL_SCAN_REQ - Initial scan request * * This is used for the first scan on an interface to force at * least one scan to be run even if the configuration does not * include any enabled networks. */ INITIAL_SCAN_REQ, /** * MANUAL_SCAN_REQ - Manual scan request * * This is used for scans where the user request a scan or * a specific wpa_supplicant operation (e.g., WPS) requires scan * to be run. */ MANUAL_SCAN_REQ } scan_req, last_scan_req; enum wpa_states scan_prev_wpa_state; struct os_reltime scan_trigger_time, scan_start_time; /* Minimum freshness requirement for connection purposes */ struct os_reltime scan_min_time; int scan_runs; /* number of scan runs since WPS was started */ int *next_scan_freqs; int *manual_scan_freqs; int *manual_sched_scan_freqs; unsigned int manual_scan_passive:1; unsigned int manual_scan_use_id:1; unsigned int manual_scan_only_new:1; unsigned int own_scan_requested:1; unsigned int own_scan_running:1; unsigned int clear_driver_scan_cache:1; unsigned int manual_scan_id; int scan_interval; /* time in sec between scans to find suitable AP */ int normal_scans; /* normal scans run before sched_scan */ int scan_for_connection; /* whether the scan request was triggered for * finding a connection */ #define MAX_SCAN_ID 16 int scan_id[MAX_SCAN_ID]; unsigned int scan_id_count; struct wpa_ssid_value *ssids_from_scan_req; unsigned int num_ssids_from_scan_req; u64 drv_flags; unsigned int drv_enc; unsigned int drv_smps_modes; unsigned int drv_rrm_flags; /* * A bitmap of supported protocols for probe response offload. See * struct wpa_driver_capa in driver.h */ unsigned int probe_resp_offloads; /* extended capabilities supported by the driver */ const u8 *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; int max_scan_ssids; int max_sched_scan_ssids; int sched_scan_supported; unsigned int max_match_sets; unsigned int max_remain_on_chan; unsigned int max_stations; int pending_mic_error_report; int pending_mic_error_pairwise; int mic_errors_seen; /* Michael MIC errors with the current PTK */ struct wps_context *wps; int wps_success; /* WPS success event received */ struct wps_er *wps_er; unsigned int wps_run; struct os_reltime wps_pin_start_time; int blacklist_cleared; struct wpabuf *pending_eapol_rx; struct os_reltime pending_eapol_rx_time; u8 pending_eapol_rx_src[ETH_ALEN]; unsigned int last_eapol_matches_bssid:1; unsigned int eap_expected_failure:1; unsigned int reattach:1; /* reassociation to the same BSS requested */ unsigned int mac_addr_changed:1; unsigned int added_vif:1; + unsigned int wnmsleep_used:1; struct os_reltime last_mac_addr_change; int last_mac_addr_style; struct ibss_rsn *ibss_rsn; int set_sta_uapsd; int sta_uapsd; int set_ap_uapsd; int ap_uapsd; #ifdef CONFIG_SME struct { u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int freq; u8 assoc_req_ie[200]; size_t assoc_req_ie_len; int mfp; int ft_used; u8 mobility_domain[2]; u8 *ft_ies; size_t ft_ies_len; u8 prev_bssid[ETH_ALEN]; int prev_bssid_set; int auth_alg; int proto; int sa_query_count; /* number of pending SA Query requests; * 0 = no SA Query in progress */ int sa_query_timed_out; u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * * sa_query_count octets of pending * SA Query transaction identifiers */ struct os_reltime sa_query_start; struct os_reltime last_unprot_disconnect; enum { HT_SEC_CHAN_UNKNOWN, HT_SEC_CHAN_ABOVE, HT_SEC_CHAN_BELOW } ht_sec_chan; u8 sched_obss_scan; u16 obss_scan_int; u16 bss_max_idle_period; #ifdef CONFIG_SAE struct sae_data sae; struct wpabuf *sae_token; int sae_group_index; unsigned int sae_pmksa_caching:1; #endif /* CONFIG_SAE */ } sme; #endif /* CONFIG_SME */ #ifdef CONFIG_AP struct hostapd_iface *ap_iface; void (*ap_configured_cb)(void *ctx, void *data); void *ap_configured_cb_ctx; void *ap_configured_cb_data; #endif /* CONFIG_AP */ struct hostapd_iface *ifmsh; #ifdef CONFIG_MESH struct mesh_rsn *mesh_rsn; int mesh_if_idx; unsigned int mesh_if_created:1; unsigned int mesh_ht_enabled:1; int mesh_auth_block_duration; /* sec */ #endif /* CONFIG_MESH */ unsigned int off_channel_freq; struct wpabuf *pending_action_tx; u8 pending_action_src[ETH_ALEN]; u8 pending_action_dst[ETH_ALEN]; u8 pending_action_bssid[ETH_ALEN]; unsigned int pending_action_freq; int pending_action_no_cck; int pending_action_without_roc; unsigned int pending_action_tx_done:1; void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, enum offchannel_send_action_result result); unsigned int roc_waiting_drv_freq; int action_tx_wait_time; int p2p_mgmt; #ifdef CONFIG_P2P struct p2p_go_neg_results *go_params; int create_p2p_iface; u8 pending_interface_addr[ETH_ALEN]; char pending_interface_name[100]; int pending_interface_type; int p2p_group_idx; unsigned int pending_listen_freq; unsigned int pending_listen_duration; enum { NOT_P2P_GROUP_INTERFACE, P2P_GROUP_INTERFACE_PENDING, P2P_GROUP_INTERFACE_GO, P2P_GROUP_INTERFACE_CLIENT } p2p_group_interface; struct p2p_group *p2p_group; int p2p_long_listen; /* remaining time in long Listen state in ms */ char p2p_pin[10]; int p2p_wps_method; u8 p2p_auth_invite[ETH_ALEN]; int p2p_sd_over_ctrl_iface; int p2p_in_provisioning; int p2p_in_invitation; int p2p_invite_go_freq; int pending_invite_ssid_id; int show_group_started; u8 go_dev_addr[ETH_ALEN]; int pending_pd_before_join; u8 pending_join_iface_addr[ETH_ALEN]; u8 pending_join_dev_addr[ETH_ALEN]; int pending_join_wps_method; u8 p2p_join_ssid[SSID_MAX_LEN]; size_t p2p_join_ssid_len; int p2p_join_scan_count; int auto_pd_scan_retry; int force_long_sd; u16 pending_pd_config_methods; enum { NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN, AUTO_PD_ASP } pending_pd_use; /* * Whether cross connection is disallowed by the AP to which this * interface is associated (only valid if there is an association). */ int cross_connect_disallowed; /* * Whether this P2P group is configured to use cross connection (only * valid if this is P2P GO interface). The actual cross connect packet * forwarding may not be configured depending on the uplink status. */ int cross_connect_enabled; /* Whether cross connection forwarding is in use at the moment. */ int cross_connect_in_use; /* * Uplink interface name for cross connection */ char cross_connect_uplink[100]; unsigned int p2p_auto_join:1; unsigned int p2p_auto_pd:1; unsigned int p2p_persistent_group:1; unsigned int p2p_fallback_to_go_neg:1; unsigned int p2p_pd_before_go_neg:1; unsigned int p2p_go_ht40:1; unsigned int p2p_go_vht:1; unsigned int user_initiated_pd:1; unsigned int p2p_go_group_formation_completed:1; unsigned int group_formation_reported:1; unsigned int waiting_presence_resp; int p2p_first_connection_timeout; unsigned int p2p_nfc_tag_enabled:1; unsigned int p2p_peer_oob_pk_hash_known:1; unsigned int p2p_disable_ip_addr_req:1; unsigned int p2ps_method_config_any:1; unsigned int p2p_cli_probe:1; int p2p_persistent_go_freq; int p2p_persistent_id; int p2p_go_intent; int p2p_connect_freq; struct os_reltime p2p_auto_started; struct wpa_ssid *p2p_last_4way_hs_fail; struct wpa_radio_work *p2p_scan_work; struct wpa_radio_work *p2p_listen_work; struct wpa_radio_work *p2p_send_action_work; u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */ struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group * formation */ u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; u8 p2p_ip_addr_info[3 * 4]; /* group common frequencies */ int *p2p_group_common_freqs; unsigned int p2p_group_common_freqs_num; u8 p2ps_join_addr[ETH_ALEN]; #endif /* CONFIG_P2P */ struct wpa_ssid *bgscan_ssid; const struct bgscan_ops *bgscan; void *bgscan_priv; const struct autoscan_ops *autoscan; struct wpa_driver_scan_params *autoscan_params; void *autoscan_priv; struct wpa_ssid *connect_without_scan; struct wps_ap_info *wps_ap; size_t num_wps_ap; int wps_ap_iter; int after_wps; int known_wps_freq; unsigned int wps_freq; int wps_fragment_size; int auto_reconnect_disabled; /* Channel preferences for AP/P2P GO use */ int best_24_freq; int best_5_freq; int best_overall_freq; struct gas_query *gas; #ifdef CONFIG_INTERWORKING unsigned int fetch_anqp_in_progress:1; unsigned int network_select:1; unsigned int auto_select:1; unsigned int auto_network_select:1; unsigned int interworking_fast_assoc_tried:1; unsigned int fetch_all_anqp:1; unsigned int fetch_osu_info:1; unsigned int fetch_osu_waiting_scan:1; unsigned int fetch_osu_icon_in_progress:1; struct wpa_bss *interworking_gas_bss; unsigned int osu_icon_id; struct osu_provider *osu_prov; size_t osu_prov_count; struct os_reltime osu_icon_fetch_start; unsigned int num_osu_scans; unsigned int num_prov_found; #endif /* CONFIG_INTERWORKING */ unsigned int drv_capa_known; struct { struct hostapd_hw_modes *modes; u16 num_modes; u16 flags; } hw; enum local_hw_capab { CAPAB_NO_HT_VHT, CAPAB_HT, CAPAB_HT40, CAPAB_VHT, } hw_capab; #ifdef CONFIG_MACSEC struct ieee802_1x_kay *kay; #endif /* CONFIG_MACSEC */ int pno; int pno_sched_pending; /* WLAN_REASON_* reason codes. Negative if locally generated. */ int disconnect_reason; struct ext_password_data *ext_pw; struct wpabuf *last_gas_resp, *prev_gas_resp; u8 last_gas_addr[ETH_ALEN], prev_gas_addr[ETH_ALEN]; u8 last_gas_dialog_token, prev_gas_dialog_token; unsigned int no_keep_alive:1; unsigned int ext_mgmt_frame_handling:1; unsigned int ext_eapol_frame_io:1; unsigned int wmm_ac_supported:1; unsigned int ext_work_in_progress:1; unsigned int own_disconnect_req:1; #define MAC_ADDR_RAND_SCAN BIT(0) #define MAC_ADDR_RAND_SCHED_SCAN BIT(1) #define MAC_ADDR_RAND_PNO BIT(2) #define MAC_ADDR_RAND_ALL (MAC_ADDR_RAND_SCAN | \ MAC_ADDR_RAND_SCHED_SCAN | \ MAC_ADDR_RAND_PNO) unsigned int mac_addr_rand_supported; unsigned int mac_addr_rand_enable; /* MAC Address followed by mask (2 * ETH_ALEN) */ u8 *mac_addr_scan; u8 *mac_addr_sched_scan; u8 *mac_addr_pno; #ifdef CONFIG_WNM u8 wnm_dialog_token; u8 wnm_reply; u8 wnm_num_neighbor_report; u8 wnm_mode; u16 wnm_dissoc_timer; u8 wnm_bss_termination_duration[12]; struct neighbor_report *wnm_neighbor_report_elements; struct os_reltime wnm_cand_valid_until; u8 wnm_cand_from_bss[ETH_ALEN]; #endif /* CONFIG_WNM */ #ifdef CONFIG_TESTING_GET_GTK u8 last_gtk[32]; size_t last_gtk_len; #endif /* CONFIG_TESTING_GET_GTK */ unsigned int num_multichan_concurrent; struct wpa_radio_work *connect_work; unsigned int ext_work_id; struct wpabuf *vendor_elem[NUM_VENDOR_ELEM_FRAMES]; #ifdef CONFIG_TESTING_OPTIONS struct l2_packet_data *l2_test; unsigned int extra_roc_dur; enum wpa_supplicant_test_failure test_failure; #endif /* CONFIG_TESTING_OPTIONS */ struct wmm_ac_assoc_data *wmm_ac_assoc_info; struct wmm_tspec_element *tspecs[WMM_AC_NUM][TS_DIR_IDX_COUNT]; struct wmm_ac_addts_request *addts_request; u8 wmm_ac_last_dialog_token; struct wmm_tspec_element *last_tspecs; u8 last_tspecs_count; struct rrm_data rrm; #ifdef CONFIG_FST struct fst_iface *fst; const struct wpabuf *fst_ies; struct wpabuf *received_mb_ies; #endif /* CONFIG_FST */ }; /* wpa_supplicant.c */ void wpa_supplicant_apply_ht_overrides( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params); void wpa_supplicant_apply_vht_overrides( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params); int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s); const char * wpa_supplicant_state_txt(enum wpa_states state); int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s); int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s); int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, u8 *wpa_ie, size_t *wpa_ie_len); void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid); void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s); void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr); void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, int sec, int usec); void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s); void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, enum wpa_states state); struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s); const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s); void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s); void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, int reason_code); void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s, const char *pkcs11_engine_path, const char *pkcs11_module_path); int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan); int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s, unsigned int expire_age); int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s, unsigned int expire_count); int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s, int scan_interval); int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, int debug_timestamp, int debug_show_keys); void free_hw_features(struct wpa_supplicant *wpa_s); void wpa_show_license(void); struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, struct wpa_interface *iface, struct wpa_supplicant *parent); int wpa_supplicant_remove_iface(struct wpa_global *global, struct wpa_supplicant *wpa_s, int terminate); struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global, const char *ifname); struct wpa_global * wpa_supplicant_init(struct wpa_params *params); int wpa_supplicant_run(struct wpa_global *global); void wpa_supplicant_deinit(struct wpa_global *global); int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_terminate_proc(struct wpa_global *global); void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, const u8 *buf, size_t len); void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s); void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s); void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid); int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s); int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s); void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason); void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int clear_failures); int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid); int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len); void wpas_request_connection(struct wpa_supplicant *wpa_s); int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen); int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style); int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s); void add_freq(int *freqs, int *num_freqs, int freq); void wpas_rrm_reset(struct wpa_supplicant *wpa_s); void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, const u8 *report, size_t report_len); int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, void (*cb)(void *ctx, struct wpabuf *neighbor_rep), void *cb_ctx); void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *frame, size_t len, int rssi); /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response * @wpa_s: Pointer to wpa_supplicant data * @ssid: Pointer to the network block the reply is for * @field: field the response is a reply for * @value: value (ie, password, etc) for @field * Returns: 0 on success, non-zero on error * * Helper function to handle replies to control interface requests. */ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const char *field, const char *value); void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, struct hostapd_freq_params *freq); /* events.c */ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s); int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid); void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx); void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx); void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s); int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s); struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, struct wpa_ssid **selected_ssid); /* eap_register.c */ int eap_register_methods(void); /** * Utility method to tell if a given network is for persistent group storage * @ssid: Network object * Returns: 1 if network is a persistent group, 0 otherwise */ static inline int network_is_persistent_group(struct wpa_ssid *ssid) { return ssid->disabled == 2 && ssid->p2p_persistent_group; } int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_init_ext_pw(struct wpa_supplicant *wpa_s); void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title, struct wpa_used_freq_data *freqs_data, unsigned int len); int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, struct wpa_used_freq_data *freqs_data, unsigned int len); int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, int *freq_array, unsigned int len); void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx); #ifdef CONFIG_FST struct fst_wpa_obj; void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s, struct fst_wpa_obj *iface_obj); #endif /* CONFIG_FST */ #endif /* WPA_SUPPLICANT_I_H */ Index: releng/11.1/sys/conf/newvers.sh =================================================================== --- releng/11.1/sys/conf/newvers.sh (revision 324698) +++ releng/11.1/sys/conf/newvers.sh (revision 324699) @@ -1,291 +1,291 @@ #!/bin/sh - # # Copyright (c) 1984, 1986, 1990, 1993 # The Regents of the University of California. All rights reserved. # # 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. # 3. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. # # @(#)newvers.sh 8.1 (Berkeley) 4/20/94 # $FreeBSD$ # Command line options: # # -r Reproducible build. Do not embed directory names, user # names, time stamps or other dynamic information into # the output file. This is intended to allow two builds # done at different times and even by different people on # different hosts to produce identical output. # # -R Reproducible build if the tree represents an unmodified # checkout from a version control system. Metadata is # included if the tree is modified. TYPE="FreeBSD" REVISION="11.1" -BRANCH="RELEASE-p1" +BRANCH="RELEASE-p2" if [ -n "${BRANCH_OVERRIDE}" ]; then BRANCH=${BRANCH_OVERRIDE} fi RELEASE="${REVISION}-${BRANCH}" VERSION="${TYPE} ${RELEASE}" if [ -z "${SYSDIR}" ]; then SYSDIR=$(dirname $0)/.. fi if [ -n "${PARAMFILE}" ]; then RELDATE=$(awk '/__FreeBSD_version.*propagated to newvers/ {print $3}' \ ${PARAMFILE}) else RELDATE=$(awk '/__FreeBSD_version.*propagated to newvers/ {print $3}' \ ${SYSDIR}/sys/param.h) fi b=share/examples/etc/bsd-style-copyright if [ -r "${SYSDIR}/../COPYRIGHT" ]; then year=$(sed -Ee '/^Copyright .* The FreeBSD Project/!d;s/^.*1992-([0-9]*) .*$/\1/g' ${SYSDIR}/../COPYRIGHT) else year=$(date +%Y) fi # look for copyright template for bsd_copyright in ../$b ../../$b ../../../$b /usr/src/$b /usr/$b do if [ -r "$bsd_copyright" ]; then COPYRIGHT=`sed \ -e "s/\[year\]/1992-$year/" \ -e 's/\[your name here\]\.* /The FreeBSD Project./' \ -e 's/\[your name\]\.*/The FreeBSD Project./' \ -e '/\[id for your version control system, if any\]/d' \ $bsd_copyright` break fi done # no copyright found, use a dummy if [ -z "$COPYRIGHT" ]; then COPYRIGHT="/*- * Copyright (c) 1992-$year The FreeBSD Project. * All rights reserved. * */" fi # add newline COPYRIGHT="$COPYRIGHT " # VARS_ONLY means no files should be generated, this is just being # included. if [ -n "$VARS_ONLY" ]; then return 0 fi LC_ALL=C; export LC_ALL if [ ! -r version ] then echo 0 > version fi touch version v=`cat version` u=${USER:-root} d=`pwd` h=${HOSTNAME:-`hostname`} if [ -n "$SOURCE_DATE_EPOCH" ]; then if ! t=`date -r $SOURCE_DATE_EPOCH 2>/dev/null`; then echo "Invalid SOURCE_DATE_EPOCH" >&2 exit 1 fi else t=`date` fi i=`${MAKE:-make} -V KERN_IDENT` compiler_v=$($(${MAKE:-make} -V CC) -v 2>&1 | grep -w 'version') for dir in /usr/bin /usr/local/bin; do if [ ! -z "${svnversion}" ] ; then break fi if [ -x "${dir}/svnversion" ] && [ -z ${svnversion} ] ; then # Run svnversion from ${dir} on this script; if return code # is not zero, the checkout might not be compatible with the # svnversion being used. ${dir}/svnversion $(realpath ${0}) >/dev/null 2>&1 if [ $? -eq 0 ]; then svnversion=${dir}/svnversion break fi fi done if [ -z "${svnversion}" ] && [ -x /usr/bin/svnliteversion ] ; then /usr/bin/svnliteversion $(realpath ${0}) >/dev/null 2>&1 if [ $? -eq 0 ]; then svnversion=/usr/bin/svnliteversion else svnversion= fi fi for dir in /usr/bin /usr/local/bin; do if [ -x "${dir}/p4" ] && [ -z ${p4_cmd} ] ; then p4_cmd=${dir}/p4 fi done if [ -d "${SYSDIR}/../.git" ] ; then for dir in /usr/bin /usr/local/bin; do if [ -x "${dir}/git" ] ; then git_cmd="${dir}/git --git-dir=${SYSDIR}/../.git" break fi done fi if [ -d "${SYSDIR}/../.hg" ] ; then for dir in /usr/bin /usr/local/bin; do if [ -x "${dir}/hg" ] ; then hg_cmd="${dir}/hg -R ${SYSDIR}/.." break fi done fi if [ -n "$svnversion" ] ; then svn=`cd ${SYSDIR} && $svnversion 2>/dev/null` case "$svn" in [0-9]*[MSP]|*:*) svn=" r${svn}" modified=true ;; [0-9]*) svn=" r${svn}" ;; *) unset svn ;; esac fi if [ -n "$git_cmd" ] ; then git=`$git_cmd rev-parse --verify --short HEAD 2>/dev/null` svn=`$git_cmd svn find-rev $git 2>/dev/null` if [ -n "$svn" ] ; then svn=" r${svn}" git="=${git}" else svn=`$git_cmd log | fgrep 'git-svn-id:' | head -1 | \ sed -n 's/^.*@\([0-9][0-9]*\).*$/\1/p'` if [ -z "$svn" ] ; then svn=`$git_cmd log --format='format:%N' | \ grep '^svn ' | head -1 | \ sed -n 's/^.*revision=\([0-9][0-9]*\).*$/\1/p'` fi if [ -n "$svn" ] ; then svn=" r${svn}" git="+${git}" else git=" ${git}" fi fi git_b=`$git_cmd rev-parse --abbrev-ref HEAD` if [ -n "$git_b" ] ; then git="${git}(${git_b})" fi if $git_cmd --work-tree=${SYSDIR}/.. diff-index \ --name-only HEAD | read dummy; then git="${git}-dirty" modified=true fi fi if [ -n "$p4_cmd" ] ; then p4version=`cd ${SYSDIR} && $p4_cmd changes -m1 "./...#have" 2>&1 | \ awk '{ print $2 }'` case "$p4version" in [0-9]*) p4version=" ${p4version}" p4opened=`cd ${SYSDIR} && $p4_cmd opened ./... 2>&1` case "$p4opened" in File*) ;; //*) p4version="${p4version}+edit" modified=true ;; esac ;; *) unset p4version ;; esac fi if [ -n "$hg_cmd" ] ; then hg=`$hg_cmd id 2>/dev/null` svn=`$hg_cmd svn info 2>/dev/null | \ awk -F': ' '/Revision/ { print $2 }'` if [ -n "$svn" ] ; then svn=" r${svn}" fi if [ -n "$hg" ] ; then hg=" ${hg}" fi fi include_metadata=true while getopts rR opt; do case "$opt" in r) include_metadata= ;; R) if [ -z "${modified}" ]; then include_metadata= fi esac done shift $((OPTIND - 1)) if [ -z "${include_metadata}" ]; then VERINFO="${VERSION} ${svn}${git}${hg}${p4version}" VERSTR="${VERINFO}\\n" else VERINFO="${VERSION} #${v}${svn}${git}${hg}${p4version}: ${t}" VERSTR="${VERINFO}\\n ${u}@${h}:${d}\\n" fi cat << EOF > vers.c $COPYRIGHT #define SCCSSTR "@(#)${VERINFO}" #define VERSTR "${VERSTR}" #define RELSTR "${RELEASE}" char sccs[sizeof(SCCSSTR) > 128 ? sizeof(SCCSSTR) : 128] = SCCSSTR; char version[sizeof(VERSTR) > 256 ? sizeof(VERSTR) : 256] = VERSTR; char compiler_version[] = "${compiler_v}"; char ostype[] = "${TYPE}"; char osrelease[sizeof(RELSTR) > 32 ? sizeof(RELSTR) : 32] = RELSTR; int osreldate = ${RELDATE}; char kern_ident[] = "${i}"; EOF echo $((v + 1)) > version