Index: releng/10.3/UPDATING =================================================================== --- releng/10.3/UPDATING (revision 324739) +++ releng/10.3/UPDATING (revision 324740) @@ -1,2416 +1,2420 @@ 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: http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/makeworld.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 to bootstrap to the tip of stable/10, and then rebuild without this option. The bootstrap process from older version of current is a bit fragile. +20171017 p22 FreeBSD-SA-17:07.wpa + + Fix WPA2 protocol vulnerability. [SA-17:07] + 20170810 p21 FreeBSD-SA-17:06.openssh Fix OpenSSH Denial of Service vulnerability. [SA-17:06] 20170712 p20 FreeBSD-SA-17:05.heimdal FreeBSD-EN-17:06.hyperv Fix heimdal KDC-REP service name validation vulnerability [SA-17:05] Boot compatibility improvements with Azure VMs. [EN-17:06] 20170427 p19 FreeBSD-SA-17:04.ipfilter Fix ipfilter(4) fragment handling panic. [SA-17:04] 20170412 p18 FreeBSD-SA-17:03.ntp FreeBSD-EN-17:05.xen Fix multiple vulnerabilities of ntp. [SA-17:03] Xen migration enhancements. [EN-17:05] 20170223 p17 FreeBSD-SA-17:02.openssl Fix OpenSSL RC4_MD5 cipher vulnerability. 20170111 p16 FreeBSD-SA-17:01.openssh Fix multiple vulnerabilities of OpenSSH. 20161222 p15 FreeBSD-SA-16:39.ntp Fix multiple vulnerabilities of ntp. 20161208 p14 FreeBSD-SA-16:37.libc [revised] Fix regressions introduced by SA-16:37.libc. 20161206 p13 FreeBSD-SA-16:36.telnetd FreeBSD-SA-16:37.libc FreeBSD-SA-16:38.bhyve FreeBSD-EN-16:19.tzcode FreeBSD-EN-16:20.tzdata Fix possible login(1) argument injection in telnetd(8). [SA-16:36] Fix link_ntoa(3) buffer overflow in libc. [SA-16:37] Fix possible escape from bhyve(8) virtual machine. [SA-16:38] Fix warnings about valid time zone abbreviations. [EN-16:19] Update timezone database information. [EN-16:20] 20161102 p12 FreeBSD-SA-16:33.openssh FreeBSD-SA-16:35.openssl Fix OpenSSH remote Denial of Service vulnerability. [SA-16:33] Fix OpenSSL remote DoS vulnerability. [SA-16:35] 20161025 p11 FreeBSD-SA-16:15.sysarch [revised] FreeBSD-EN-16:17.vm Fix incorrect argument validation in sysarch(2). [SA-16:15] Fix virtual memory subsystem bugs. [EN-16:17] 20161010 p10 FreeBSD-SA-16:29.bspatch FreeBSD-SA-16:30.portsnap FreeBSD-SA-16:31.libarchive Fix bspatch heap overflow vulnerability. [SA-16:29] Fix multiple portsnap vulnerabilities. [SA-16:30] Fix multiple libarchive vulnerabilities. [SA-16:31] 20160926 p9 FreeBSD-SA-16:26.openssl [revised] Fix OpenSSL regression introduced in SA-16:26. 20160923 p8 FreeBSD-SA-16:26.openssl Fix multiple OpenSSL vulnerabilitites. 20160811 p7 FreeBSD-EN-16:10.dhclient FreeBSD-EN-16:11.vmbus FreeBSD-EN-16:12.hv_storvsc FreeBSD-EN-16:13.vmbus FreeBSD-EN-16:14.hv_storvsc FreeBSD-EN-16:15.vmbus FreeBSD-EN-16:16.hv_storvsc Fix handling of unknown options from a DHCP server. [EN-16:10] Fix a panic in hv_vmbus(4). [EN-16:11] Fix missing hotplugged disk in hv_storvsc(4). [EN-16:12] Fix the timecounter emulation in hv_vmbus(4). [EN-16:13] Fix callout(9) handling in hv_storvsc(4). [EN-16:14] Fix memory allocation issues in hv_vmbus(4). [EN-16:15] Fix SCSI command handling in hv_storvsc(4). [EN-16:16] 20160725 p6 FreeBSD-SA-16:25.bspatch FreeBSD-EN-16:09.freebsd-update Fix bspatch heap overflow vulnerability. [SA-16:25] Fix freebsd-update(8) support of FreeBSD 11.0 release distribution. [EN-16:09] 20160604 p5 FreeBSD-SA-16:24.ntp Fix multiple vulnerabilities of ntp. 20160531 p4 FreeBSD-SA-16:20.linux FreeBSD-SA-16:21.43bsd FreeBSD-SA-16:22.libarchive Fix kernel stack disclosure in Linux compatibility layer. [SA-16:20] Fix kernel stack disclosure in 4.3BSD compatibility layer. [SA-16:21] Fix directory traversal in cpio(1). [SA-16:22] 20160517 p3 FreeBSD-SA-16:18.atkbd FreeBSD-SA-16:19.sendmsg Fix buffer overflow in keyboard driver. [SA-16:18] Fix incorrect argument handling in sendmsg(2). [SA-16:19] 20160504 p2 FreeBSD-SA-16:17.openssl FreeBSD-EN-16:06.libc FreeBSD-EN-16:07.ipi FreeBSD-EN-16:08.zfs Fix multiple OpenSSL vulnerabilitites. [SA-16:17] Fix performance regression in libc hash(3). [EN-16:06] Fix excessive latency in x86 IPI delivery. [EN-16:07] Fix memory leak in ZFS. [EN-16:08] 20160429 p1 FreeBSD-SA-16:16.ntp Fix multiple vulnerabilities of ntp. 20160329: 10.3-RELEASE. 20160124: The NONE and HPN patches has been removed from OpenSSH. They are still available in the security/openssh-portable port. 20151214: r292223 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. 20151113: 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. 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. 20151026: NTP has been upgraded to 4.2.8p4. 20151025: 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. 20150823: 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. 20150822: 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. 20150813: 10.2-RELEASE. 20150731: 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. 20150703: 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. 20150624: An additional fix for the issue described in the 20150614 sendmail entry below has been been committed in revision 284786. 20150615: The fix for the issue described in the 20150614 sendmail entry below has been been committed in revision 284485. 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: 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. 20150601: chmod, chflags, chown and chgrp now affect symlinks in -R mode as defined in symlink(7); previously symlinks were silently ignored. 20150430: The const qualifier has been removed from iconv(3) to comply with POSIX. The ports tree is aware of this from r384038 onwards. 20141215: At svn r275807, 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. See ports/UPDATING 20141209 and 20141215 on migration to CentOS 6 ports. 20141205: pjdfstest has been integrated into kyua as an opt-in test suite. Please see share/doc/pjdfstest/README for a more details on how to execute it. 20141118: 10.1-RELEASE. 20140904: The ofwfb driver, used to provide a graphics console on PowerPC when using vt(4), no longer allows mmap() of all of 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. 20140831: 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. 20140814: The ixgbe tunables now match their sysctl counterparts, for example: hw.ixgbe.enable_aim => hw.ix.enable_aim Anyone using ixgbe tunables should ensure they update /boot/loader.conf. 20140801: The NFSv4.1 server committed by r269398 changes the internal function call interfaces used between the NFS and krpc modules. As such, __FreeBSD_version was bumped. 20140729: 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. 20140717: It is no longer necessary to include the dwarf version in your DEBUG options in your kernel config file. The bug that required it to be placed in the config file has bene fixed. DEBUG should now just contain -g. The build system will automatically update things to do the right thing. 20140715: Several ABI breaking changes were merged to CTL and new iSCSI code. All CTL and iSCSI-related tools, such as ctladm, ctld, iscsid and iscsictl need to be rebuilt to work with a new kernel. 20140708: 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.) 20140608: On i386 and amd64 systems, the onifconsole flag is now set by default in /etc/ttys for ttyu0. This causes ttyu0 to be automatically enabled as a login TTY if it is set in the bootloader as an active kernel console. No changes in behavior should result otherwise. To revert to the previous behavior, set ttyu0 to "off" in /etc/ttys. 20140512: Clang and llvm have been upgraded to 3.4.1 release. 20140321: Clang and llvm have been upgraded to 3.4 release. 20140306: 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 20140303: 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. 20140227: 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. 20140216: The nve(4) driver for NVIDIA nForce MCP Ethernet adapters has been deprecated and will not be part of FreeBSD 11.0 and later releases. If you use this driver, please consider switching to the nfe(4) driver instead. 20140120: 10.0-RELEASE. 20131216: 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 1000701. 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. 20131031: 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 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 compatability. 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. 20110913: This commit modifies vfs_register() so that it uses a hash calculation to set vfc_typenum, which is enabled by default. The first time a system is booted after this change, the vfc_typenum values will change for all file systems. The main effect of this is a change to the NFS server file handles for file systems that use vfc_typenum in their fsid, such as ZFS. It will, however, prevent vfc_typenum from changing when file systems are loaded in a different order for subsequent reboots. To disable this, you can set vfs.typenumhash=0 in /boot/loader.conf until you are ready to remount all NFS clients after a reboot. 20110828: Bump the shared library version numbers for libraries that do not use symbol versioning, have changed the ABI compared to stable/8 and which shared library version was not bumped. Done as part of 9.0-RELEASE cycle. 20110815: During the merge of Capsicum features, the fget(9) KPI was modified. This may require the rebuilding of out-of-tree device drivers -- issues have been reported specifically with the nVidia device driver. __FreeBSD_version is bumped to 900041. Also, there is a period between 20110811 and 20110814 where the special devices /dev/{stdin,stdout,stderr} did not work correctly. Building world from a kernel during that window may not work. 20110628: The packet filter (pf) code has been updated to OpenBSD 4.5. You need to update userland tools to be in sync with kernel. This update breaks backward compatibility with earlier pfsync(4) versions. Care must be taken when updating redundant firewall setups. 20110608: The following sysctls and tunables are retired on x86 platforms: machdep.hlt_cpus machdep.hlt_logical_cpus The following sysctl is retired: machdep.hyperthreading_allowed The sysctls were supposed to provide a way to dynamically offline and online selected CPUs on x86 platforms, but the implementation has not been reliable especially with SCHED_ULE scheduler. machdep.hyperthreading_allowed tunable is still available to ignore hyperthreading CPUs at OS level. Individual CPUs can be disabled using hint.lapic.X.disabled tunable, where X is an APIC ID of a CPU. Be advised, though, that disabling CPUs in non-uniform fashion will result in non-uniform topology and may lead to sub-optimal system performance with SCHED_ULE, which is a default scheduler. 20110607: cpumask_t type is retired and cpuset_t is used in order to describe a mask of CPUs. 20110531: Changes to ifconfig(8) for dynamic address family detection mandate that you are running a kernel of 20110525 or later. Make sure to follow the update procedure to boot a new kernel before installing world. 20110513: Support for sun4v architecture is officially dropped 20110503: Several KPI breaking changes have been committed to the mii(4) layer, the PHY drivers and consequently some Ethernet drivers using mii(4). This means that miibus.ko and the modules of the affected Ethernet drivers need to be recompiled. Note to kernel developers: Given that the OUI bit reversion problem was fixed as part of these changes all mii(4) commits related to OUIs, i.e. to sys/dev/mii/miidevs, PHY driver probing and vendor specific handling, no longer can be merged verbatim to stable/8 and previous branches. 20110430: Users of the Atheros AR71xx SoC code now need to add 'device ar71xx_pci' into their kernel configurations along with 'device pci'. 20110427: The default NFS client is now the new NFS client, so fstype "newnfs" is now "nfs" and the regular/old NFS client is now fstype "oldnfs". Although mounts via fstype "nfs" will usually work without userland changes, it is recommended that the mount(8) and mount_nfs(8) commands be rebuilt from sources and that a link to mount_nfs called mount_oldnfs be created. The new client is compiled into the kernel with "options NFSCL" and this is needed for diskless root file systems. The GENERIC kernel configs have been changed to use NFSCL and NFSD (the new server) instead of NFSCLIENT and NFSSERVER. To use the regular/old client, you can "mount -t oldnfs ...". For a diskless root file system, you must also include a line like: vfs.root.mountfrom="oldnfs:" in the boot/loader.conf on the root fs on the NFS server to make a diskless root fs use the old client. 20110424: The GENERIC kernels for all architectures now default to the new CAM-based ATA stack. It means that all legacy ATA drivers were removed and replaced by respective CAM drivers. If you are using ATA device names in /etc/fstab or other places, make sure to update them respectively (adX -> adaY, acdX -> cdY, afdX -> daY, astX -> saY, where 'Y's are the sequential numbers starting from zero for each type in order of detection, unless configured otherwise with tunables, see cam(4)). There will be symbolic links created in /dev/ to map old adX devices to the respective adaY. They should provide basic compatibility for file systems mounting in most cases, but they do not support old user-level APIs and do not have respective providers in GEOM. Consider using updated management tools with new device names. It is possible to load devices ahci, ata, siis and mvs as modules, but option ATA_CAM should remain in kernel configuration to make ata module work as CAM driver supporting legacy ATA controllers. Device ata still can be used in modular fashion (atacore + ...). Modules atadisk and atapi* are not used and won't affect operation in ATA_CAM mode. Note that to use CAM-based ATA kernel should include CAM devices scbus, pass, da (or explicitly ada), cd and optionally others. All of them are parts of the cam module. ataraid(4) functionality is now supported by the RAID GEOM class. To use it you can load geom_raid kernel module and use graid(8) tool for management. Instead of /dev/arX device names, use /dev/raid/rX. No kernel config options or code have been removed, so if a problem arises, please report it and optionally revert to the old ATA stack. In order to do it you can remove from the kernel config: options ATA_CAM device ahci device mvs device siis , and instead add back: device atadisk # ATA disk drives device ataraid # ATA RAID drives device atapicd # ATAPI CDROM drives device atapifd # ATAPI floppy drives device atapist # ATAPI tape drives 20110423: The default NFS server has been changed to the new server, which was referred to as the experimental server. If you need to switch back to the old NFS server, you must now put the "-o" option on both the mountd and nfsd commands. This can be done using the mountd_flags and nfs_server_flags rc.conf variables until an update to the rc scripts is committed, which is coming soon. 20110418: The GNU Objective-C runtime library (libobjc), and other Objective-C related components have been removed from the base system. If you require an Objective-C library, please use one of the available ports. 20110331: ath(4) has been split into bus- and device- modules. if_ath contains the HAL, the TX rate control and the network device code. if_ath_pci contains the PCI bus glue. For Atheros MIPS embedded systems, if_ath_ahb contains the AHB glue. Users need to load both if_ath_pci and if_ath in order to use ath on everything else. TO REPEAT: if_ath_ahb is not needed for normal users. Normal users only need to load if_ath and if_ath_pci for ath(4) operation. 20110314: As part of the replacement of sysinstall, the process of building release media has changed significantly. For details, please re-read release(7), which has been updated to reflect the new build process. 20110218: GNU binutils 2.17.50 (as of 2007-07-03) has been merged to -HEAD. This is the last available version under GPLv2. It brings a number of new features, such as support for newer x86 CPU's (with SSE-3, SSSE-3, SSE 4.1 and SSE 4.2), better support for powerpc64, a number of new directives, and lots of other small improvements. See the ChangeLog file in contrib/binutils for the full details. 20110218: IPsec's HMAC_SHA256-512 support has been fixed to be RFC4868 compliant, and will now use half of hash for authentication. This will break interoperability with all stacks (including all actual FreeBSD versions) who implement draft-ietf-ipsec-ciph-sha-256-00 (they use 96 bits of hash for authentication). The only workaround with such peers is to use another HMAC algorithm for IPsec ("phase 2") authentication. 20110207: Remove the uio_yield prototype and symbol. This function has been misnamed since it was introduced and should not be globally exposed with this name. The equivalent functionality is now available using kern_yield(curthread->td_user_pri). The function remains undocumented. 20110112: A SYSCTL_[ADD_]UQUAD was added for unsigned uint64_t pointers, symmetric with the existing SYSCTL_[ADD_]QUAD. Type checking for scalar sysctls is defined but disabled. Code that needs UQUAD to pass the type checking that must compile on older systems where the define is not present can check against __FreeBSD_version >= 900030. The system dialog(1) has been replaced with a new version previously in ports as devel/cdialog. dialog(1) is mostly command-line compatible with the previous version, but the libdialog associated with it has a largely incompatible API. As such, the original version of libdialog will be kept temporarily as libodialog, until its base system consumers are replaced or updated. Bump __FreeBSD_version to 900030. 20110103: If you are trying to run make universe on a -stable system, and you get the following warning: "Makefile", line 356: "Target architecture for i386/conf/GENERIC unknown. config(8) likely too old." or something similar to it, then you must upgrade your -stable system to 8.2-Release or newer (really, any time after r210146 7/15/2010 in stable/8) or build the config from the latest stable/8 branch and install it on your system. Prior to this date, building a current universe on 8-stable system from between 7/15/2010 and 1/2/2011 would result in a weird shell parsing error in the first kernel build phase. A new config on those old systems will fix that problem for older versions of -current. 20101228: The TCP stack has been modified to allow Khelp modules to interact with it via helper hook points and store per-connection data in the TCP control block. Bump __FreeBSD_version to 900029. User space tools that rely on the size of struct tcpcb in tcp_var.h (e.g. sockstat) need to be recompiled. 20101114: Generic IEEE 802.3 annex 31B full duplex flow control support has been added to mii(4) and bge(4), bce(4), msk(4), nfe(4) and stge(4) along with brgphy(4), e1000phy(4) as well as ip1000phy() have been converted to take advantage of it instead of using custom implementations. This means that these drivers now no longer unconditionally advertise support for flow control but only do so if flow control is a selected media option. This was implemented in the generic support that way in order to allow flow control to be switched on and off via ifconfig(8) with the PHY specific default to typically off in order to protect from unwanted effects. Consequently, if you used flow control with one of the above mentioned drivers you now need to explicitly enable it, for example via: ifconfig bge0 media auto mediaopt flowcontrol Along with the above mentioned changes generic support for setting 1000baseT master mode also has been added and brgphy(4), ciphy(4), e1000phy(4) as well as ip1000phy(4) have been converted to take advantage of it. This means that these drivers now no longer take the link0 parameter for selecting master mode but the master media option has to be used instead, for example like in the following: ifconfig bge0 media 1000baseT mediaopt full-duplex,master Selection of master mode now is also available with all other PHY drivers supporting 1000baseT. 20101111: The TCP stack has received a significant update to add support for modularised congestion control and generally improve the clarity of congestion control decisions. Bump __FreeBSD_version to 900025. User space tools that rely on the size of struct tcpcb in tcp_var.h (e.g. sockstat) need to be recompiled. 20101002: The man(1) utility has been replaced by a new version that no longer uses /etc/manpath.config. Please consult man.conf(5) for how to migrate local entries to the new format. 20100928: The copyright strings printed by login(1) and sshd(8) at the time of a new connection have been removed to follow other operating systems and upstream sshd. 20100915: A workaround for a fixed ld bug has been removed in kernel code, so make sure that your system ld is built from sources after revision 210245 from 2010-07-19 (r211583 if building head kernel on stable/8, r211584 for stable/7; both from 2010-08-21). A symptom of incorrect ld version is different addresses for set_pcpu section and __start_set_pcpu symbol in kernel and/or modules. 20100913: The $ipv6_prefer variable in rc.conf(5) has been split into $ip6addrctl_policy and $ipv6_activate_all_interfaces. The $ip6addrctl_policy is a variable to choose a pre-defined address selection policy set by ip6addrctl(8). A value "ipv4_prefer", "ipv6_prefer" or "AUTO" can be specified. The default is "AUTO". The $ipv6_activate_all_interfaces specifies whether IFDISABLED flag (see an entry of 20090926) is set on an interface with no corresponding $ifconfig_IF_ipv6 line. The default is "NO" for security reason. If you want IPv6 link-local address on all interfaces by default, set this to "YES". The old ipv6_prefer="YES" is equivalent to ipv6_activate_all_interfaces="YES" and ip6addrctl_policy="ipv6_prefer". 20100913: DTrace has grown support for userland tracing. Due to this, DTrace is now i386 and amd64 only. dtruss(1) is now installed by default on those systems and a new kernel module is needed for userland tracing: fasttrap. No changes to your kernel config file are necessary to enable userland tracing, but you might consider adding 'STRIP=' and 'CFLAGS+=-fno-omit-frame-pointer' to your make.conf if you want to have informative userland stack traces in DTrace (ustack). 20100725: The acpi_aiboost(4) driver has been removed in favor of the new aibs(4) driver. You should update your kernel configuration file. 20100722: BSD grep has been imported to the base system and it is built by default. It is completely BSD licensed, highly GNU-compatible, uses less memory than its GNU counterpart and has a small codebase. However, it is slower than its GNU counterpart, which is mostly noticeable for larger searches, for smaller ones it is measurable but not significant. The reason is complex, the most important factor is that we lack a modern and efficient regex library and GNU overcomes this by optimizing the searches internally. Future work on improving the regex performance is planned, for the meantime, users that need better performance, can build GNU grep instead by setting the WITH_GNU_GREP knob. 20100713: Due to the import of powerpc64 support, all existing powerpc kernel configuration files must be updated with a machine directive like this: machine powerpc powerpc In addition, an updated config(8) is required to build powerpc kernels after this change. 20100713: A new version of ZFS (version 15) has been merged to -HEAD. This version uses a python library for the following subcommands: zfs allow, zfs unallow, zfs groupspace, zfs userspace. For full functionality of these commands the following port must be installed: sysutils/py-zfs 20100429: 'vm_page's are now hashed by physical address to an array of mutexes. Currently this is only used to serialize access to hold_count. Over time the page queue mutex will be peeled away. This changes the size of pmap on every architecture. And requires all callers of vm_page_hold and vm_page_unhold to be updated. 20100402: WITH_CTF can now be specified in src.conf (not recommended, there are some problems with static executables), make.conf (would also affect ports which do not use GNU make and do not override the compile targets) or in the kernel config (via "makeoptions WITH_CTF=yes"). When WITH_CTF was specified there before this was silently ignored, so make sure that WITH_CTF is not used in places which could lead to unwanted behavior. 20100311: The kernel option COMPAT_IA32 has been replaced with COMPAT_FREEBSD32 to allow 32-bit compatibility on non-x86 platforms. All kernel configurations on amd64 and ia64 platforms using these options must be modified accordingly. 20100113: The utmp user accounting database has been replaced with utmpx, the user accounting interface standardized by POSIX. Unfortunately the semantics of utmp and utmpx don't match, making it practically impossible to support both interfaces. The user accounting database is used by tools like finger(1), last(1), talk(1), w(1) and ac(8). All applications in the base system use utmpx. This means only local binaries (e.g. from the ports tree) may still use these utmp database files. These applications must be rebuilt to make use of utmpx. After the system has been upgraded, it is safe to remove the old log files (/var/run/utmp, /var/log/lastlog and /var/log/wtmp*), assuming their contents is of no importance anymore. Old wtmp databases can only be used by last(1) and ac(8) after they have been converted to the new format using wtmpcvt(1). 20100108: Introduce the kernel thread "deadlock resolver" (which can be enabled via the DEADLKRES option, see NOTES for more details) and the sleepq_type() function for sleepqueues. 20091202: The rc.firewall and rc.firewall6 were unified, and rc.firewall6 and rc.d/ip6fw were removed. According to the removal of rc.d/ip6fw, ipv6_firewall_* rc variables are obsoleted. Instead, the following new rc variables are added to rc.d/ipfw: firewall_client_net_ipv6, firewall_simple_iif_ipv6, firewall_simple_inet_ipv6, firewall_simple_oif_ipv6, firewall_simple_onet_ipv6, firewall_trusted_ipv6 The meanings correspond to the relevant IPv4 variables. 20091125: 8.0-RELEASE. 20091113: The default terminal emulation for syscons(4) has been changed from cons25 to xterm on all platforms except pc98. This means that the /etc/ttys file needs to be updated to ensure correct operation of applications on the console. The terminal emulation style can be toggled per window by using vidcontrol(1)'s -T flag. The TEKEN_CONS25 kernel configuration options can be used to change the compile-time default back to cons25. To prevent graphical artifacts, make sure the TERM environment variable is set to match the terminal emulation that is being performed by syscons(4). 20091109: The layout of the structure ieee80211req_scan_result has changed. Applications that require wireless scan results (e.g. ifconfig(8)) from net80211 need to be recompiled. Applications such as wpa_supplicant(8) may require a full world build without using NO_CLEAN in order to get synchronized with the new structure. 20091025: The iwn(4) driver has been updated to support the 5000 and 5150 series. There's one kernel module for each firmware. Adding "device iwnfw" to the kernel configuration file means including all three firmware images inside the kernel. If you want to include just the one for your wireless card, use the devices iwn4965fw, iwn5000fw or iwn5150fw. 20090926: The rc.d/network_ipv6, IPv6 configuration script has been integrated into rc.d/netif. The changes are the following: 1. To use IPv6, simply define $ifconfig_IF_ipv6 like $ifconfig_IF for IPv4. For aliases, $ifconfig_IF_aliasN should be used. Note that both variables need the "inet6" keyword at the head. Do not set $ipv6_network_interfaces manually if you do not understand what you are doing. It is not needed in most cases. $ipv6_ifconfig_IF and $ipv6_ifconfig_IF_aliasN still work, but they are obsolete. 2. $ipv6_enable is obsolete. Use $ipv6_prefer and "inet6 accept_rtadv" keyword in ifconfig(8) instead. If you define $ipv6_enable=YES, it means $ipv6_prefer=YES and all configured interfaces have "inet6 accept_rtadv" in the $ifconfig_IF_ipv6. These are for backward compatibility. 3. A new variable $ipv6_prefer has been added. If NO, IPv6 functionality of interfaces with no corresponding $ifconfig_IF_ipv6 is disabled by using "inet6 ifdisabled" flag, and the default address selection policy of ip6addrctl(8) is the IPv4-preferred one (see rc.d/ip6addrctl for more details). Note that if you want to configure IPv6 functionality on the disabled interfaces after boot, first you need to clear the flag by using ifconfig(8) like: ifconfig em0 inet6 -ifdisabled If YES, the default address selection policy is set as IPv6-preferred. The default value of $ipv6_prefer is NO. 4. If your system need to receive Router Advertisement messages, define "inet6 accept_rtadv" in $ifconfig_IF_ipv6. The rc(8) scripts automatically invoke rtsol(8) when the interface becomes UP. The Router Advertisement messages are used for SLAAC (State-Less Address AutoConfiguration). 20090922: 802.11s D3.03 support was committed. This is incompatible with the previous code, which was based on D3.0. 20090912: A sysctl variable net.inet6.ip6.accept_rtadv now sets the default value of a per-interface flag ND6_IFF_ACCEPT_RTADV, not a global knob to control whether accepting Router Advertisement messages or not. Also, a per-interface flag ND6_IFF_AUTO_LINKLOCAL has been added and a sysctl variable net.inet6.ip6.auto_linklocal is its default value. The ifconfig(8) utility now supports these flags. 20090910: ZFS snapshots are now mounted with MNT_IGNORE flag. Use -v option for mount(8) and -a option for df(1) to see them. 20090825: The old tunable hw.bus.devctl_disable has been superseded by hw.bus.devctl_queue. hw.bus.devctl_disable=1 in loader.conf should be replaced by hw.bus.devctl_queue=0. The default for this new tunable is 1000. 20090813: Remove the option STOP_NMI. The default action is now to use NMI only for KDB via the newly introduced function stop_cpus_hard() and maintain stop_cpus() to just use a normal IPI_STOP on ia32 and amd64. 20090803: The stable/8 branch created in subversion. This corresponds to the RELENG_8 branch in CVS. 20090719: Bump the shared library version numbers for all libraries that do not use symbol versioning as part of the 8.0-RELEASE cycle. Bump __FreeBSD_version to 800105. 20090714: Due to changes in the implementation of virtual network stack support, all network-related kernel modules must be recompiled. As this change breaks the ABI, bump __FreeBSD_version to 800104. 20090713: The TOE interface to the TCP syncache has been modified to remove struct tcpopt () from the ABI of the network stack. The cxgb driver is the only TOE consumer affected by this change, and needs to be recompiled along with the kernel. As this change breaks the ABI, bump __FreeBSD_version to 800103. 20090712: Padding has been added to struct tcpcb, sackhint and tcpstat in to facilitate future MFCs and bug fixes whilst maintaining the ABI. However, this change breaks the ABI, so bump __FreeBSD_version to 800102. User space tools that rely on the size of any of these structs (e.g. sockstat) need to be recompiled. 20090630: The NFS_LEGACYRPC option has been removed along with the old kernel RPC implementation that this option selected. Kernel configurations may need to be adjusted. 20090629: The network interface device nodes at /dev/net/ have been removed. All ioctl operations can be performed the normal way using routing sockets. The kqueue functionality can generally be replaced with routing sockets. 20090628: The documentation from the FreeBSD Documentation Project (Handbook, FAQ, etc.) is now installed via packages by sysinstall(8) and under the /usr/local/share/doc/freebsd directory instead of /usr/share/doc. 20090624: The ABI of various structures related to the SYSV IPC API have been changed. As a result, the COMPAT_FREEBSD[456] and COMPAT_43 kernel options now all require COMPAT_FREEBSD7. Bump __FreeBSD_version to 800100. 20090622: Layout of struct vnet has changed as routing related variables were moved to their own Vimage module. Modules need to be recompiled. Bump __FreeBSD_version to 800099. 20090619: NGROUPS_MAX and NGROUPS have been increased from 16 to 1023 and 1024 respectively. As long as no more than 16 groups per process are used, no changes should be visible. When more than 16 groups are used, old binaries may fail if they call getgroups() or getgrouplist() with statically sized storage. Recompiling will work around this, but applications should be modified to use dynamically allocated storage for group arrays as POSIX.1-2008 does not cap an implementation's number of supported groups at NGROUPS_MAX+1 as previous versions did. NFS and portalfs mounts may also be affected as the list of groups is truncated to 16. Users of NFS who use more than 16 groups, should take care that negative group permissions are not used on the exported file systems as they will not be reliable unless a GSSAPI based authentication method is used. 20090616: The compiling option ADAPTIVE_LOCKMGRS has been introduced. This option compiles in the support for adaptive spinning for lockmgrs which want to enable it. The lockinit() function now accepts the flag LK_ADAPTIVE in order to make the lock object subject to adaptive spinning when both held in write and read mode. 20090613: The layout of the structure returned by IEEE80211_IOC_STA_INFO has changed. User applications that use this ioctl need to be rebuilt. 20090611: The layout of struct thread has changed. Kernel and modules need to be rebuilt. 20090608: The layout of structs ifnet, domain, protosw and vnet_net has changed. Kernel modules need to be rebuilt. Bump __FreeBSD_version to 800097. 20090602: window(1) has been removed from the base system. It can now be installed from ports. The port is called misc/window. 20090601: The way we are storing and accessing `routing table' entries has changed. Programs reading the FIB, like netstat, need to be re-compiled. 20090601: A new netisr implementation has been added for FreeBSD 8. Network file system modules, such as igmp, ipdivert, and others, should be rebuilt. Bump __FreeBSD_version to 800096. 20090530: Remove the tunable/sysctl debug.mpsafevfs as its initial purpose is no more valid. 20090530: Add VOP_ACCESSX(9). File system modules need to be rebuilt. Bump __FreeBSD_version to 800094. 20090529: Add mnt_xflag field to 'struct mount'. File system modules need to be rebuilt. Bump __FreeBSD_version to 800093. 20090528: The compiling option ADAPTIVE_SX has been retired while it has been introduced the option NO_ADAPTIVE_SX which handles the reversed logic. The KPI for sx_init_flags() changes as accepting flags: SX_ADAPTIVESPIN flag has been retired while the SX_NOADAPTIVE flag has been introduced in order to handle the reversed logic. Bump __FreeBSD_version to 800092. 20090527: Add support for hierarchical jails. Remove global securelevel. Bump __FreeBSD_version to 800091. 20090523: The layout of struct vnet_net has changed, therefore modules need to be rebuilt. Bump __FreeBSD_version to 800090. 20090523: The newly imported zic(8) produces a new format in the output. Please run tzsetup(8) to install the newly created data to /etc/localtime. 20090520: The sysctl tree for the usb stack has renamed from hw.usb2.* to hw.usb.* and is now consistent again with previous releases. 20090520: 802.11 monitor mode support was revised and driver api's were changed. Drivers dependent on net80211 now support DLT_IEEE802_11_RADIO instead of DLT_IEEE802_11. No user-visible data structures were changed but applications that use DLT_IEEE802_11 may require changes. Bump __FreeBSD_version to 800088. 20090430: The layout of the following structs has changed: sysctl_oid, socket, ifnet, inpcbinfo, tcpcb, syncache_head, vnet_inet, vnet_inet6 and vnet_ipfw. Most modules need to be rebuild or panics may be experienced. World rebuild is required for correctly checking networking state from userland. Bump __FreeBSD_version to 800085. 20090429: MLDv2 and Source-Specific Multicast (SSM) have been merged to the IPv6 stack. VIMAGE hooks are in but not yet used. The implementation of SSM within FreeBSD's IPv6 stack closely follows the IPv4 implementation. For kernel developers: * The most important changes are that the ip6_output() and ip6_input() paths no longer take the IN6_MULTI_LOCK, and this lock has been downgraded to a non-recursive mutex. * As with the changes to the IPv4 stack to support SSM, filtering of inbound multicast traffic must now be performed by transport protocols within the IPv6 stack. This does not apply to TCP and SCTP, however, it does apply to UDP in IPv6 and raw IPv6. * The KPIs used by IPv6 multicast are similar to those used by the IPv4 stack, with the following differences: * im6o_mc_filter() is analogous to imo_multicast_filter(). * The legacy KAME entry points in6_joingroup and in6_leavegroup() are shimmed to in6_mc_join() and in6_mc_leave() respectively. * IN6_LOOKUP_MULTI() has been deprecated and removed. * IPv6 relies on MLD for the DAD mechanism. KAME's internal KPIs for MLDv1 have an additional 'timer' argument which is used to jitter the initial membership report for the solicited-node multicast membership on-link. * This is not strictly needed for MLDv2, which already jitters its report transmissions. However, the 'timer' argument is preserved in case MLDv1 is active on the interface. * The KAME linked-list based IPv6 membership implementation has been refactored to use a vector similar to that used by the IPv4 stack. Code which maintains a list of its own multicast memberships internally, e.g. carp, has been updated to reflect the new semantics. * There is a known Lock Order Reversal (LOR) due to in6_setscope() acquiring the IF_AFDATA_LOCK and being called within ip6_output(). Whilst MLDv2 tries to avoid this otherwise benign LOR, it is an implementation constraint which needs to be addressed in HEAD. For application developers: * The changes are broadly similar to those made for the IPv4 stack. * The use of IPv4 and IPv6 multicast socket options on the same socket, using mapped addresses, HAS NOT been tested or supported. * There are a number of issues with the implementation of various IPv6 multicast APIs which need to be resolved in the API surface before the implementation is fully compatible with KAME userland use, and these are mostly to do with interface index treatment. * The literature available discusses the use of either the delta / ASM API with setsockopt(2)/getsockopt(2), or the full-state / ASM API using setsourcefilter(3)/getsourcefilter(3). For more information please refer to RFC 3768, 'Socket Interface Extensions for Multicast Source Filters'. * Applications which use the published RFC 3678 APIs should be fine. For systems administrators: * The mtest(8) utility has been refactored to support IPv6, in addition to IPv4. Interface addresses are no longer accepted as arguments, their names must be used instead. The utility will map the interface name to its first IPv4 address as returned by getifaddrs(3). * The ifmcstat(8) utility has also been updated to print the MLDv2 endpoint state and source filter lists via sysctl(3). * The net.inet6.ip6.mcast.loop sysctl may be tuned to 0 to disable loopback of IPv6 multicast datagrams by default; it defaults to 1 to preserve the existing behaviour. Disabling multicast loopback is recommended for optimal system performance. * The IPv6 MROUTING code has been changed to examine this sysctl instead of attempting to perform a group lookup before looping back forwarded datagrams. Bump __FreeBSD_version to 800084. 20090422: Implement low-level Bluetooth HCI API. Bump __FreeBSD_version to 800083. 20090419: The layout of struct malloc_type, used by modules to register new memory allocation types, has changed. Most modules will need to be rebuilt or panics may be experienced. Bump __FreeBSD_version to 800081. 20090415: Anticipate overflowing inp_flags - add inp_flags2. This changes most offsets in inpcb, so checking v4 connection state will require a world rebuild. Bump __FreeBSD_version to 800080. 20090415: Add an llentry to struct route and struct route_in6. Modules embedding a struct route will need to be recompiled. Bump __FreeBSD_version to 800079. 20090414: The size of rt_metrics_lite and by extension rtentry has changed. Networking administration apps will need to be recompiled. The route command now supports show as an alias for get, weighting of routes, sticky and nostick flags to alter the behavior of stateful load balancing. Bump __FreeBSD_version to 800078. 20090408: Do not use Giant for kbdmux(4) locking. This is wrong and apparently causing more problems than it solves. This will re-open the issue where interrupt handlers may race with kbdmux(4) in polling mode. Typical symptoms include (but not limited to) duplicated and/or missing characters when low level console functions (such as gets) are used while interrupts are enabled (for example geli password prompt, mountroot prompt etc.). Disabling kbdmux(4) may help. 20090407: The size of structs vnet_net, vnet_inet and vnet_ipfw has changed; kernel modules referencing any of the above need to be recompiled. Bump __FreeBSD_version to 800075. 20090320: GEOM_PART has become the default partition slicer for storage devices, replacing GEOM_MBR, GEOM_BSD, GEOM_PC98 and GEOM_GPT slicers. It introduces some changes: MSDOS/EBR: the devices created from MSDOS extended partition entries (EBR) can be named differently than with GEOM_MBR and are now symlinks to devices with offset-based names. fstabs may need to be modified. BSD: the "geometry does not match label" warning is harmless in most cases but it points to problems in file system misalignment with disk geometry. The "c" partition is now implicit, covers the whole top-level drive and cannot be (mis)used by users. General: Kernel dumps are now not allowed to be written to devices whose partition types indicate they are meant to be used for file systems (or, in case of MSDOS partitions, as something else than the "386BSD" type). Most of these changes date approximately from 200812. 20090319: The uscanner(4) driver has been removed from the kernel. This follows Linux removing theirs in 2.6 and making libusb the default interface (supported by sane). 20090319: The multicast forwarding code has been cleaned up. netstat(1) only relies on KVM now for printing bandwidth upcall meters. The IPv4 and IPv6 modules are split into ip_mroute_mod and ip6_mroute_mod respectively. The config(5) options for statically compiling this code remain the same, i.e. 'options MROUTING'. 20090315: Support for the IFF_NEEDSGIANT network interface flag has been removed, which means that non-MPSAFE network device drivers are no longer supported. In particular, if_ar, if_sr, and network device drivers from the old (legacy) USB stack can no longer be built or used. 20090313: POSIX.1 Native Language Support (NLS) has been enabled in libc and a bunch of new language catalog files have also been added. This means that some common libc messages are now localized and they depend on the LC_MESSAGES environmental variable. 20090313: The k8temp(4) driver has been renamed to amdtemp(4) since support for Family 10 and Family 11 CPU families was added. 20090309: IGMPv3 and Source-Specific Multicast (SSM) have been merged to the IPv4 stack. VIMAGE hooks are in but not yet used. For kernel developers, the most important changes are that the ip_output() and ip_input() paths no longer take the IN_MULTI_LOCK(), and this lock has been downgraded to a non-recursive mutex. Transport protocols (UDP, Raw IP) are now responsible for filtering inbound multicast traffic according to group membership and source filters. The imo_multicast_filter() KPI exists for this purpose. Transports which do not use multicast (SCTP, TCP) already reject multicast by default. Forwarding and receive performance may improve as a mutex acquisition is no longer needed in the ip_input() low-level input path. in_addmulti() and in_delmulti() are shimmed to new KPIs which exist to support SSM in-kernel. For application developers, it is recommended that loopback of multicast datagrams be disabled for best performance, as this will still cause the lock to be taken for each looped-back datagram transmission. The net.inet.ip.mcast.loop sysctl may be tuned to 0 to disable loopback by default; it defaults to 1 to preserve the existing behaviour. For systems administrators, to obtain best performance with multicast reception and multiple groups, it is always recommended that a card with a suitably precise hash filter is used. Hash collisions will still result in the lock being taken within the transport protocol input path to check group membership. If deploying FreeBSD in an environment with IGMP snooping switches, it is recommended that the net.inet.igmp.sendlocal sysctl remain enabled; this forces 224.0.0.0/24 group membership to be announced via IGMP. The size of 'struct igmpstat' has changed; netstat needs to be recompiled to reflect this. Bump __FreeBSD_version to 800070. 20090309: libusb20.so.1 is now installed as libusb.so.1 and the ports system updated to use it. This requires a buildworld/installworld in order to update the library and dependencies (usbconfig, etc). Its advisable to rebuild all ports which uses libusb. More specific directions are given in the ports collection UPDATING file. Any /etc/libmap.conf entries for libusb are no longer required and can be removed. 20090302: A workaround is committed to allow the creation of System V shared memory segment of size > 2 GB on the 64-bit architectures. Due to a limitation of the existing ABI, the shm_segsz member of the struct shmid_ds, returned by shmctl(IPC_STAT) call is wrong for large segments. Note that limits must be explicitly raised to allow such segments to be created. 20090301: The layout of struct ifnet has changed, requiring a rebuild of all network device driver modules. 20090227: The /dev handling for the new USB stack has changed, a buildworld/installworld is required for libusb20. 20090223: The new USB2 stack has now been permanently moved in and all kernel and module names reverted to their previous values (eg, usb, ehci, ohci, ums, ...). The old usb stack can be compiled in by prefixing the name with the letter 'o', the old usb modules have been removed. Updating entry 20090216 for xorg and 20090215 for libmap may still apply. 20090217: The rc.conf(5) option if_up_delay has been renamed to defaultroute_delay to better reflect its purpose. If you have customized this setting in /etc/rc.conf you need to update it to use the new name. 20090216: xorg 7.4 wants to configure its input devices via hald which does not yet work with USB2. If the keyboard/mouse does not work in xorg then add Option "AllowEmptyInput" "off" to your ServerLayout section. This will cause X to use the configured kbd and mouse sections from your xorg.conf. 20090215: The GENERIC kernels for all architectures now default to the new USB2 stack. No kernel config options or code have been removed so if a problem arises please report it and optionally revert to the old USB stack. If you are loading USB kernel modules or have a custom kernel that includes GENERIC then ensure that usb names are also changed over, eg uftdi -> usb2_serial_ftdi. Older programs linked against the ports libusb 0.1 need to be redirected to the new stack's libusb20. /etc/libmap.conf can be used for this: # Map old usb library to new one for usb2 stack libusb-0.1.so.8 libusb20.so.1 20090209: All USB ethernet devices now attach as interfaces under the name ueN (eg. ue0). This is to provide a predictable name as vendors often change usb chipsets in a product without notice. 20090203: The ichsmb(4) driver has been changed to require SMBus slave addresses be left-justified (xxxxxxx0b) rather than right-justified. All of the other SMBus controller drivers require left-justified slave addresses, so this change makes all the drivers provide the same interface. 20090201: INET6 statistics (struct ip6stat) was updated. netstat(1) needs to be recompiled. 20090119: NTFS has been removed from GENERIC kernel on amd64 to match GENERIC on i386. Should not cause any issues since mount_ntfs(8) will load ntfs.ko module automatically when NTFS support is actually needed, unless ntfs.ko is not installed or security level prohibits loading kernel modules. If either is the case, "options NTFS" has to be added into kernel config. 20090115: TCP Appropriate Byte Counting (RFC 3465) support added to kernel. New field in struct tcpcb breaks ABI, so bump __FreeBSD_version to 800061. User space tools that rely on the size of struct tcpcb in tcp_var.h (e.g. sockstat) need to be recompiled. 20081225: ng_tty(4) module updated to match the new TTY subsystem. Due to API change, user-level applications must be updated. New API support added to mpd5 CVS and expected to be present in next mpd5.3 release. 20081219: With __FreeBSD_version 800060 the makefs tool is part of the base system (it was a port). 20081216: The afdata and ifnet locks have been changed from mutexes to rwlocks, network modules will need to be re-compiled. 20081214: __FreeBSD_version 800059 incorporates the new arp-v2 rewrite. RTF_CLONING, RTF_LLINFO and RTF_WASCLONED flags are eliminated. The new code reduced struct rtentry{} by 16 bytes on 32-bit architecture and 40 bytes on 64-bit architecture. The userland applications "arp" and "ndp" have been updated accordingly. The output from "netstat -r" shows only routing entries and none of the L2 information. 20081130: __FreeBSD_version 800057 marks the switchover from the binary ath hal to source code. Users must add the line: options AH_SUPPORT_AR5416 to their kernel config files when specifying: device ath_hal The ath_hal module no longer exists; the code is now compiled together with the driver in the ath module. It is now possible to tailor chip support (i.e. reduce the set of chips and thereby the code size); consult ath_hal(4) for details. 20081121: __FreeBSD_version 800054 adds memory barriers to , new interfaces to ifnet to facilitate multiple hardware transmit queues for cards that support them, and a lock-less ring-buffer implementation to enable drivers to more efficiently manage queueing of packets. 20081117: A new version of ZFS (version 13) has been merged to -HEAD. This version has zpool attribute "listsnapshots" off by default, which means "zfs list" does not show snapshots, and is the same as Solaris behavior. 20081028: dummynet(4) ABI has changed. ipfw(8) needs to be recompiled. 20081009: The uhci, ohci, ehci and slhci USB Host controller drivers have been put into separate modules. If you load the usb module separately through loader.conf you will need to load the appropriate *hci module as well. E.g. for a UHCI-based USB 2.0 controller add the following to loader.conf: uhci_load="YES" ehci_load="YES" 20081009: The ABI used by the PMC toolset has changed. Please keep userland (libpmc(3)) and the kernel module (hwpmc(4)) in sync. 20081009: atapci kernel module now includes only generic PCI ATA driver. AHCI driver moved to ataahci kernel module. All vendor-specific code moved into separate kernel modules: ataacard, ataacerlabs, ataadaptec, ataamd, ataati, atacenatek, atacypress, atacyrix, atahighpoint, ataintel, ataite, atajmicron, atamarvell, atamicron, atanational, atanetcell, atanvidia, atapromise, ataserverworks, atasiliconimage, atasis, atavia 20080820: The TTY subsystem of the kernel has been replaced by a new implementation, which provides better scalability and an improved driver model. Most common drivers have been migrated to the new TTY subsystem, while others have not. The following drivers have not yet been ported to the new TTY layer: PCI/ISA: cy, digi, rc, rp, sio USB: ubser, ucycom Line disciplines: ng_h4, ng_tty, ppp, sl, snp Adding these drivers to your kernel configuration file shall cause compilation to fail. 20080818: ntpd has been upgraded to 4.2.4p5. 20080801: OpenSSH has been upgraded to 5.1p1. For many years, FreeBSD's version of OpenSSH preferred DSA over RSA for host and user authentication keys. With this upgrade, we've switched to the vendor's default of RSA over DSA. This may cause upgraded clients to warn about unknown host keys even for previously known hosts. Users should follow the usual procedure for verifying host keys before accepting the RSA key. This can be circumvented by setting the "HostKeyAlgorithms" option to "ssh-dss,ssh-rsa" in ~/.ssh/config or on the ssh command line. Please note that the sequence of keys offered for authentication has been changed as well. You may want to specify IdentityFile in a different order to revert this behavior. 20080713: The sio(4) driver has been removed from the i386 and amd64 kernel configuration files. This means uart(4) is now the default serial port driver on those platforms as well. To prevent collisions with the sio(4) driver, the uart(4) driver uses different names for its device nodes. This means the onboard serial port will now most likely be called "ttyu0" instead of "ttyd0". You may need to reconfigure applications to use the new device names. When using the serial port as a boot console, be sure to update /boot/device.hints and /etc/ttys before booting the new kernel. If you forget to do so, you can still manually specify the hints at the loader prompt: set hint.uart.0.at="isa" set hint.uart.0.port="0x3F8" set hint.uart.0.flags="0x10" set hint.uart.0.irq="4" boot -s 20080609: The gpt(8) utility has been removed. Use gpart(8) to partition disks instead. 20080603: The version that Linuxulator emulates was changed from 2.4.2 to 2.6.16. If you experience any problems with Linux binaries please try to set sysctl compat.linux.osrelease to 2.4.2 and if it fixes the problem contact emulation mailing list. 20080525: ISDN4BSD (I4B) was removed from the src tree. You may need to update a your kernel configuration and remove relevant entries. 20080509: I have checked in code to support multiple routing tables. See the man pages setfib(1) and setfib(2). This is a hopefully backwards compatible version, but to make use of it you need to compile your kernel with options ROUTETABLES=2 (or more up to 16). 20080420: The 802.11 wireless support was redone to enable multi-bss operation on devices that are capable. The underlying device is no longer used directly but instead wlanX devices are cloned with ifconfig. This requires changes to rc.conf files. For example, change: ifconfig_ath0="WPA DHCP" to wlans_ath0=wlan0 ifconfig_wlan0="WPA DHCP" see rc.conf(5) for more details. In addition, mergemaster of /etc/rc.d is highly recommended. Simultaneous update of userland and kernel wouldn't hurt either. As part of the multi-bss changes the wlan_scan_ap and wlan_scan_sta modules were merged into the base wlan module. All references to these modules (e.g. in kernel config files) must be removed. 20080408: psm(4) has gained write(2) support in native operation level. Arbitrary commands can be written to /dev/psm%d and status can be read back from it. Therefore, an application is responsible for status validation and error recovery. It is a no-op in other operation levels. 20080312: Support for KSE threading has been removed from the kernel. To run legacy applications linked against KSE libmap.conf may be used. The following libmap.conf may be used to ensure compatibility with any prior release: libpthread.so.1 libthr.so.1 libpthread.so.2 libthr.so.2 libkse.so.3 libthr.so.3 20080301: The layout of struct vmspace has changed. This affects libkvm and any executables that link against libkvm and use the kvm_getprocs() function. In particular, but not exclusively, it affects ps(1), fstat(1), pkill(1), systat(1), top(1) and w(1). The effects are minimal, but it's advisable to upgrade world nonetheless. 20080229: The latest em driver no longer has support in it for the 82575 adapter, this is now moved to the igb driver. The split was done to make new features that are incompatible with older hardware easier to do. 20080220: The new geom_lvm(4) geom class has been renamed to geom_linux_lvm(4), likewise the kernel option is now GEOM_LINUX_LVM. 20080211: The default NFS mount mode has changed from UDP to TCP for increased reliability. If you rely on (insecurely) NFS mounting across a firewall you may need to update your firewall rules. 20080208: Belatedly note the addition of m_collapse for compacting mbuf chains. 20080126: The fts(3) structures have been changed to use adequate integer types for their members and so to be able to cope with huge file trees. The old fts(3) ABI is preserved through symbol versioning in libc, so third-party binaries using fts(3) should still work, although they will not take advantage of the extended types. At the same time, some third-party software might fail to build after this change due to unportable assumptions made in its source code about fts(3) structure members. Such software should be fixed by its vendor or, in the worst case, in the ports tree. FreeBSD_version 800015 marks this change for the unlikely case that a portable fix is impossible. 20080123: To upgrade to -current after this date, you must be running FreeBSD not older than 6.0-RELEASE. Upgrading to -current from 5.x now requires a stop over at RELENG_6 or RELENG_7 systems. 20071128: The ADAPTIVE_GIANT kernel option has been retired because its functionality is the default now. 20071118: The AT keyboard emulation of sunkbd(4) has been turned on by default. In order to make the special symbols of the Sun keyboards driven by sunkbd(4) work under X these now have to be configured the same way as Sun USB keyboards driven by ukbd(4) (which also does AT keyboard emulation), f.e.: Option "XkbLayout" "us" Option "XkbRules" "xorg" Option "XkbSymbols" "pc(pc105)+sun_vndr/usb(sun_usb)+us" 20071024: It has been decided that it is desirable to provide ABI backwards compatibility to the FreeBSD 4/5/6 versions of the PCIOCGETCONF, PCIOCREAD and PCIOCWRITE IOCTLs, which was broken with the introduction of PCI domain support (see the 20070930 entry). Unfortunately, this required the ABI of PCIOCGETCONF to be broken again in order to be able to provide backwards compatibility to the old version of that IOCTL. Thus consumers of PCIOCGETCONF have to be recompiled again. As for prominent ports this affects neither pciutils nor xorg-server this time, the hal port needs to be rebuilt however. 20071020: The misnamed kthread_create() and friends have been renamed to kproc_create() etc. Many of the callers already used kproc_start().. I will return kthread_create() and friends in a while with implementations that actually create threads, not procs. Renaming corresponds with version 800002. 20071010: RELENG_7 branched. 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. 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 ad0: "gpart bootcode -p /boot/gptzfsboot -i 1 ad0" 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 -p [5] make installworld mergemaster -i [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} 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 -p [5] make installworld mergemaster -i [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 October 10, 2007. 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/10.3/contrib/wpa/src/ap/wpa_auth.c =================================================================== --- releng/10.3/contrib/wpa/src/ap/wpa_auth.c (revision 324739) +++ releng/10.3/contrib/wpa/src/ap/wpa_auth.c (revision 324740) @@ -1,3022 +1,3048 @@ /* * IEEE 802.11 RSN / WPA Authenticator * Copyright (c) 2004-2011, 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 "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(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 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_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 *prev_psk) { if (wpa_auth->cb.get_psk == NULL) return NULL; return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, 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); } 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 */ 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; wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); for (group = wpa_auth->group; group; group = group->next) { group->GTKReKey = TRUE; do { group->changed = FALSE; wpa_group_sm_step(wpa_auth, group); } while (group->changed); } 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(group)]; u8 rkey[32]; 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); os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); 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->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->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); } 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); 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 */ 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) { struct wpa_state_machine *sm; sm = os_zalloc(sizeof(struct wpa_state_machine)); if (sm == NULL) return NULL; os_memcpy(sm->addr, addr, ETH_ALEN); sm->wpa_auth = wpa_auth; sm->group = wpa_auth->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"); 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) { if (sm->GUpdateStationKeys) { sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; } #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ftie); #endif /* CONFIG_IEEE80211R */ os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); 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; } 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; 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; size_t eapol_key_ie_len; if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; if (data_len < sizeof(*hdr) + sizeof(*key)) return; hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); 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) - sizeof(*key)) { wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " "key_data overflow (%d > %lu)", key_data_length, (unsigned long) (data_len - sizeof(*hdr) - sizeof(*key))); 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) && 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 (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; wpa_replay_counter_mark_invalid(sm->prev_key_replay, key->replay_counter); 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((u8 *) (key + 1), 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 { 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 */ 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->PTK, 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); #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); #endif /* CONFIG_PEERKEY */ } else if (key_data_length > 0 && wpa_parse_kde_ies((const u8 *) (key + 1), 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); 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; size_t len; int alg; int key_data_len, pad_len = 0; u8 *buf, *pos; int version, pairwise; int i; len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); if (force_version) version = force_version; 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 || 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); 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); 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 + 1, kde, kde_len); 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 || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf, (u8 *) (key + 1))) { os_free(hdr); os_free(buf); return; } WPA_PUT_BE16(key->key_data_length, key_data_len); } else { 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, 16); os_memcpy(key + 1, buf, key_data_len); rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len); WPA_PUT_BE16(key->key_data_length, key_data_len); } os_free(buf); } if (key_info & WPA_KEY_INFO_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; } wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len, key->key_mic); } 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(struct wpa_ptk *PTK, u8 *data, size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info; int ret = 0; u8 mic[16]; if (data_len < sizeof(*hdr) + sizeof(*key)) return -1; hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); os_memcpy(mic, key->key_mic, 16); os_memset(key->key_mic, 0, 16); if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK, data, data_len, key->key_mic) || os_memcmp(mic, key->key_mic, 16) != 0) ret = -1; os_memcpy(key->key_mic, mic, 16); 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, 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: 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); } 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; } wpa_group_init_gmk_and_counter(wpa_auth, group); wpa_gtk_update(wpa_auth, group); wpa_group_config_group_keys(wpa_auth, group); } SM_STATE(WPA_PTK, AUTHENTICATION2) { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); wpa_group_ensure_init(sm->wpa_auth, sm->group); /* * 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."); wpa_sta_disconnect(sm->wpa_auth, sm->addr); return; } wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce, WPA_NONCE_LEN); sm->ReAuthenticationRequest = FALSE; /* 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"); } 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, 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->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)) { 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 { /* * 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 *pmk, struct wpa_ptk *ptk) { size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len); #endif /* CONFIG_IEEE80211R */ wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce, (u8 *) ptk, ptk_len, wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); return 0; } SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { struct wpa_ptk PTK; int ok = 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, pmk); if (pmk == NULL) break; } else pmk = sm->PMK; wpa_derive_ptk(sm, pmk, &PTK); if (wpa_verify_key_mic(&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"); 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(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) { return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde); } 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; 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, igtk.pn) < 0) os_memset(igtk.pn, 0, sizeof(igtk.pn)); os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_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, WPA_IGTK_LEN) < 0) return pos; } pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, (const u8 *) &igtk, sizeof(igtk), 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 */ 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 */ 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 */ 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.tk1, 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, 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; u8 *kde, *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 = os_malloc(kde_len); if (kde == NULL) return; pos = kde; 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); } else { kde = gtk; pos = kde + 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, pos - kde, gsm->GN, 1); if (sm->wpa == WPA_VERSION_WPA2) os_free(kde); } 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) { 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], WPA_IGTK_LEN) < 0) ret = -1; wpa_hexdump_key(MSG_DEBUG, "IGTK", group->IGTK[group->GN_igtk - 4], WPA_IGTK_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->is_wnmsleep) return; wpa_group_update_sta(sm, NULL); } void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) { 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; /* * IGTK subelement: * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] */ *pos++ = WNM_SLEEP_SUBELEM_IGTK; *pos++ = 2 + 6 + WPA_IGTK_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], WPA_IGTK_LEN); pos += WPA_IGTK_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], WPA_IGTK_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 && wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, broadcast_ether_addr, group->GN_igtk, group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN) < 0) ret = -1; #endif /* CONFIG_IEEE80211W */ return ret; } 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) 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_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 bool) { return bool ? "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 (ret < 0 || (size_t) ret >= buflen - len) 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 (ret < 0 || (size_t) ret >= buflen - len) 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 (ret < 0 || (size_t) ret >= buflen - len) 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 (ret < 0 || (size_t) ret >= buflen - len) 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 (ret < 0 || (size_t) ret >= buflen - len) 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->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, wpa_auth->addr, sta_addr, session_timeout, eapol, WPA_KEY_MGMT_IEEE8021X)) return 0; return -1; } 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; wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); 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); } Index: releng/10.3/contrib/wpa/src/ap/wpa_auth.h =================================================================== --- releng/10.3/contrib/wpa/src/ap/wpa_auth.h (revision 324739) +++ releng/10.3/contrib/wpa/src/ap/wpa_auth.h (revision 324740) @@ -1,292 +1,293 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator * Copyright (c) 2004-2007, 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" #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 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[16]; 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[16]; /* 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; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R #define SSID_LEN 32 u8 ssid[SSID_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; }; 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 (*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 *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 */ }; 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_auth_uses_mfp(struct wpa_state_machine *sm); struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *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); typedef enum { WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, WPA_REAUTH_EAPOL, WPA_ASSOC_FT } wpa_event; void wpa_remove_ptk(struct wpa_state_machine *sm); int wpa_auth_sm_event(struct wpa_state_machine *sm, 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_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); #endif /* WPA_AUTH_H */ Index: releng/10.3/contrib/wpa/src/ap/wpa_auth_ft.c =================================================================== --- releng/10.3/contrib/wpa/src/ap/wpa_auth_ft.c (revision 324739) +++ releng/10.3/contrib/wpa/src/ap/wpa_auth_ft.c (revision 324740) @@ -1,1654 +1,1664 @@ /* * hostapd - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2004-2009, 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 "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_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(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(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_authenticator *wpa_auth, const u8 *s1kh_id, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *pmk_r0_name) { struct ft_remote_r0kh *r0kh; struct ft_r0kh_r1kh_pull_frame frame, f; r0kh = wpa_auth->conf.r0kh_list; while (r0kh) { if (r0kh->id_len == r0kh_id_len && os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0) break; r0kh = r0kh->next; } if (r0kh == NULL) 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, 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, sizeof(f.nonce))) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; } os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, f.nonce, frame.nonce) < 0) return -1; wpa_ft_rrb_send(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, size_t ptk_len) { 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); wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name, (u8 *) ptk, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); return 0; } 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) { 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, 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, 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; } #ifdef NEED_AP_MLME if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { struct wmm_tspec_element *tspec; int res; 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)); res = wmm_process_tspec(tspec); 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(*tspec); } return pos; } #endif /* NEED_AP_MLME */ if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) { struct wmm_tspec_element *tspec; int res; tspec = (struct wmm_tspec_element *) pos; os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos, sizeof(*tspec)); 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(*tspec); } 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 (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK) 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->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.tk1, klen)) return; /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; + sm->tk_already_set = TRUE; } static u16 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, ptk_len; 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->wpa_auth, sm->addr, sm->r0kh_id, sm->r0kh_id_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; } /* * TODO: Should return "status pending" (and the caller should * not send out response now). The real response will be sent * once the response from R0KH is received. */ return WLAN_STATUS_INVALID_PMKID; } 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); ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48; wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, pmk_r1_name, (u8 *) &sm->PTK, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) &sm->PTK, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); sm->pairwise = pairwise; + 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; 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); status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, &resp_ies_len); 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[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(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(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(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(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->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(mic, ftie->mic, 16) != 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, 16); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); 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); 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 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, *pos; size_t resp_ies_len, rlen; struct ft_rrb_frame *frame; 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); status = wpa_ft_process_auth_req(sm, body, len, &resp_ies, &resp_ies_len); 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); 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); os_free(resp_ies); } 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 *frame, f; 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(*frame)) 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; } frame = (struct ft_r0kh_r1kh_pull_frame *) data; /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, frame->nonce, f.nonce) < 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); if (aes_wrap(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 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 *frame, f; struct ft_remote_r0kh *r0kh; int pairwise; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); if (data_len < sizeof(*frame)) 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; } frame = (struct ft_r0kh_r1kh_resp_frame *) data; /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, frame->nonce, f.nonce) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "response from " MACSTR, MAC2STR(src_addr)); return -1; } if (os_memcmp(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; } /* TODO: verify that matches with a pending request * and call this requests callback function to finish request * processing */ 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); 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; } 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 *frame, f; 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(*frame)) 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; } frame = (struct ft_r0kh_r1kh_push_frame *) data; /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, frame->timestamp, f.timestamp) < 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(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; } 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; 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); if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, f.timestamp, frame.timestamp) < 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/10.3/contrib/wpa/src/ap/wpa_auth_i.h =================================================================== --- releng/10.3/contrib/wpa/src/ap/wpa_auth_i.h (revision 324739) +++ releng/10.3/contrib/wpa/src/ap/wpa_auth_i.h (revision 324740) @@ -1,232 +1,233 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions * Copyright (c) 2004-2007, 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]; 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 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; #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; #endif /* CONFIG_IEEE80211R */ int pending_1_of_4_timeout; }; /* 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_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_LEN]; int GN_igtk, GM_igtk; #endif /* CONFIG_IEEE80211W */ }; 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; }; 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, struct wpa_eapol_key *key); void wpa_smk_m1(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key); void wpa_smk_m3(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key); #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, size_t ptk_len); 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/10.3/contrib/wpa/src/common/wpa_common.h =================================================================== --- releng/10.3/contrib/wpa/src/common/wpa_common.h (revision 324739) +++ releng/10.3/contrib/wpa/src/common/wpa_common.h (revision 324740) @@ -1,394 +1,405 @@ /* * WPA definitions shared between hostapd and wpa_supplicant * Copyright (c) 2002-2008, 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 #define WPA_MAX_SSID_LEN 32 /* 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_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_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2) #if 0 #define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3) #endif #define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4) #define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5) #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_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #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_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #ifdef CONFIG_IEEE80211W #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #endif /* CONFIG_IEEE80211W */ #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) /* 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 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 #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_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_ptk - WPA Pairwise Transient Key * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy */ struct wpa_ptk { u8 kck[16]; /* EAPOL-Key Key Confirmation Key (KCK) */ u8 kek[16]; /* EAPOL-Key Key Encryption Key (KEK) */ u8 tk1[16]; /* Temporal Key 1 (TK1) */ union { u8 tk2[16]; /* Temporal Key 2 (TK2) */ struct { u8 tx_mic_key[8]; u8 rx_mic_key[8]; } auth; } u; } STRUCT_PACKED; +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 struct wpa_igtk_kde { u8 keyid[2]; u8 pn[6]; u8 igtk[WPA_IGTK_LEN]; } STRUCT_PACKED; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R 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; #endif /* CONFIG_IEEE80211R */ #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, u8 *mic); void 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, u8 *ptk, size_t ptk_len, int use_sha256); #ifdef CONFIG_IEEE80211R int wpa_ft_mic(const u8 *kck, 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); void 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, u8 *ptk, size_t ptk_len, u8 *ptk_name); #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); const char * wpa_cipher_txt(int cipher); const char * wpa_key_mgmt_txt(int key_mgmt, int proto); 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_pairwise(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); #endif /* WPA_COMMON_H */ Index: releng/10.3/contrib/wpa/src/rsn_supp/tdls.c =================================================================== --- releng/10.3/contrib/wpa/src/rsn_supp/tdls.c (revision 324739) +++ releng/10.3/contrib/wpa/src/rsn_supp/tdls.c (revision 324740) @@ -1,2334 +1,2362 @@ /* * 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 "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) unsigned int tdls_testing = 0; #endif /* CONFIG_TDLS_TESTING */ #define TPK_LIFETIME 43200 /* 12 hours */ #define TPK_RETRY_COUNT 3 #define TPK_TIMEOUT 5000 /* 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); #define TDLS_MAX_IE_LEN 80 #define IEEE80211_MAX_SUPP_RATES 32 struct wpa_tdls_peer { struct wpa_tdls_peer *next; 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; 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; 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; }; 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, const u8 *buf, size_t len) { return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token, status_code, buf, len); } static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, u8 dialog_token, u16 status_code, 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 msg_len=%u", MAC2STR(dest), action_code, dialog_token, status_code, (unsigned int) msg_len); if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token, status_code, 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); peer->sm_tmr.count = TPK_RETRY_COUNT; peer->sm_tmr.timer = TPK_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.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, 0, 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 free_peer) { int ret; if (sm->tdls_external_setup) { ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); /* disable the link after teardown was sent */ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); } else { ret = wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); } if (sm->tdls_external_setup || free_peer) wpa_tdls_peer_free(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--; peer->sm_tmr.timer = TPK_TIMEOUT; 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.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, 0, 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, 1); } } 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(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(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, 1); } } static void wpa_tdls_peer_free(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->initiator = 0; os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = NULL; peer->rsnie_i_len = peer->rsnie_p_len = 0; peer->cipher = 0; - peer->tpk_set = peer->tpk_success = 0; + peer->tk_set = peer->tpk_set = peer->tpk_success = 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_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); } } 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; } 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, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, rbuf, pos - rbuf); os_free(rbuf); /* clear the Peerkey statemachine */ wpa_tdls_peer_free(sm, peer); 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, 0); } void wpa_tdls_disable_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) { wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr); wpa_tdls_peer_free(sm, peer); } } 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 */ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown"); return -1; } 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_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); /* clear the Peerkey statemachine */ wpa_tdls_peer_free(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 * @status - status code mentioning reason */ static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst, u8 tdls_action, u8 dialog_token, 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, NULL, 0); } static struct wpa_tdls_peer * wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; 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; 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)); wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0, rbuf, pos - rbuf); os_free(rbuf); return 0; } 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; 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); skip_ies: wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, rbuf, pos - rbuf); os_free(rbuf); return 0; } 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; 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); skip_ies: wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0, rbuf, pos - rbuf); os_free(rbuf); return 0; } static int wpa_tdls_send_discovery_response(struct wpa_sm *sm, struct wpa_tdls_peer *peer, u8 dialog_token) { wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response " "(peer " MACSTR ")", MAC2STR(peer->addr)); return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE, dialog_token, 0, NULL, 0); } 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)]; if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1, len - (sizeof(struct wpa_tdls_frame) + 1), &kde) < 0) return -1; 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); 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, 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 = kde->supp_rates_len - 2; if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES) peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES; os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len); if (kde->ext_supp_rates) { int clen = kde->ext_supp_rates_len - 2; if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES) clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len; os_memcpy(peer->supp_rates + peer->supp_rates_len, kde->ext_supp_rates + 2, clen); peer->supp_rates_len += clen; } 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); for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) { existing_peer = 1; break; } } if (peer == NULL) { peer = wpa_tdls_add_peer(sm, src_addr); if (peer == NULL) goto error; } /* capability information */ peer->capability = WPA_GET_LE16(cpos); cpos += 2; ielen = len - (cpos - buf); /* start of IE in buf */ if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1"); goto error; } 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_NOT_IN_SAME_BSS; goto error; } wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR, MAC2STR(src_addr)); if (copy_supp_rates(&kde, peer) < 0) goto error; #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) break; } if (peer == NULL) { peer = wpa_tdls_add_peer(sm, src_addr); if (peer == NULL) goto error; } wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of " "TDLS setup - send own request"); peer->initiator = 1; 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: /* 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"); #if 0 /* TODO: Disabling the link would be more proper * operation here, but it seems to trigger a race with * some drivers handling the new request frame. */ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); #else if (sm->tdls_external_setup) wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); else wpa_tdls_del_key(sm, peer); #endif wpa_tdls_peer_free(sm, peer); } /* * 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 (peer->initiator) { 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_tdls_peer_free(sm, peer); } } } #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->inonce, ftie->Snonce, WPA_NONCE_LEN); 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_get_random(peer->rnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->ctx, MSG_WARNING, "TDLS: Failed to get random data for responder nonce"); wpa_tdls_peer_free(sm, peer); 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: /* add the peer to the driver as a "setup in progress" peer */ wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); 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_tdls_disable_link(sm, peer->addr); goto error; } return 0; error: wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, status); return -1; } static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { peer->tpk_success = 1; 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 */ } /* add supported rates and capabilities to the TDLS peer */ wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability, peer->supp_rates, peer->supp_rates_len); 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; 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->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) 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); if (sm->tdls_external_setup) wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); 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) return -1; /* capability information */ peer->capability = WPA_GET_LE16(pos); pos += 2; ielen = len - (pos - buf); /* start of IE in buf */ if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2"); goto error; } #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 (!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); /* * 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_peer_free(sm, peer); if (sm->tdls_external_setup) wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); return -1; } wpa_tdls_set_key(sm, peer); skip_rsn: peer->dtoken = dtoken; wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " "TPK Handshake Message 3"); wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer); wpa_tdls_enable_link(sm, peer); return 0; error: wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, status); if (sm->tdls_external_setup) wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); 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; 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) return -1; 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); if (sm->tdls_external_setup) wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); return -1; } pos += 2 /* status code */ + 1 /* dialog token */; ielen = len - (pos - buf); /* start of IE in buf */ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3"); return -1; } if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3"); return -1; } 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"); return -1; } 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"); return -1; } 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"); return -1; } 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"); return -1; } 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"); return -1; } 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"); return -1; } if (kde.key_lifetime == NULL) { wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3"); return -1; } 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); if (sm->tdls_external_setup) wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); return -1; } if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid, (u8 *) timeoutie, ftie) < 0) { wpa_tdls_del_key(sm, peer); wpa_tdls_peer_free(sm, peer); return -1; } if (wpa_tdls_set_key(sm, peer) < 0) return -1; skip_rsn: wpa_tdls_enable_link(sm, peer); return 0; } 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; } /* Find existing entry and if found, use that instead of adding * a new one */ for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) break; } if (peer == NULL) { peer = wpa_tdls_add_peer(sm, addr); if (peer == NULL) return -1; } peer->initiator = 1; /* add the peer to the driver as a "setup in progress" peer */ wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); if (wpa_tdls_send_tpk_m1(sm, peer) < 0) { wpa_tdls_disable_link(sm, peer->addr); return -1; } return 0; } int wpa_tdls_reneg(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 == NULL || !peer->tpk_success) return -1; if (sm->tdls_external_setup) { /* * Disable previous link to allow renegotiation to be completed * on AP path. */ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); } return wpa_tdls_start(sm, addr); } /** * 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) < 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"); return 0; } static void wpa_tdls_remove_peers(struct wpa_sm *sm) { struct wpa_tdls_peer *peer, *tmp; peer = sm->tdls; sm->tdls = NULL; 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); os_free(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(const u8 *ies, size_t len) { struct wpa_eapol_ie_parse elems; if (ies == NULL) return 0; if (wpa_supplicant_parse_ies(ies, len, &elems) < 0) return 0; if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) return 0; /* bit 38 - TDLS Prohibited */ return !!(elems.ext_capab[2 + 4] & 0x40); } void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { sm->tdls_prohibited = wpa_tdls_prohibited(ies, len); wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS", sm->tdls_prohibited ? "prohibited" : "allowed"); } void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) { wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on " "(Re)Association Response IEs"); sm->tdls_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; } Index: releng/10.3/contrib/wpa/src/rsn_supp/wpa.c =================================================================== --- releng/10.3/contrib/wpa/src/rsn_supp/wpa.c (revision 324739) +++ releng/10.3/contrib/wpa/src/rsn_supp/wpa.c (revision 324740) @@ -1,2702 +1,2771 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing * Copyright (c) 2003-2012, 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) * @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, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic) { 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, ver, msg, msg_len, key_mic)) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: Failed to generate EAPOL-Key " "version %d MIC", ver); goto out; } wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16); wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16); 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 rlen; struct wpa_eapol_key *reply; int key_info, ver; u8 bssid[ETH_ALEN], *rbuf; 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; } rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*reply), &rlen, (void *) &reply); if (rbuf == NULL) return; reply->type = sm->proto == WPA_PROTO_RSN ? 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); WPA_PUT_BE16(reply->key_data_length, 0); 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, ver, bssid, ETH_P_EAPOL, rbuf, rlen, key_info & WPA_KEY_INFO_MIC ? reply->key_mic : NULL); } 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(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; if (sm->proto == WPA_PROTO_RSN && !wpa_key_mgmt_ft(sm->key_mgmt)) { sa = pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, 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; } 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_ft(sm->key_mgmt)) { /* 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 rlen; struct wpa_eapol_key *reply; u8 *rbuf; 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); rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*reply) + wpa_ie_len, &rlen, (void *) &reply); if (rbuf == NULL) { os_free(rsn_ie_buf); return -1; } reply->type = sm->proto == WPA_PROTO_RSN ? 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) 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); 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, ver, dst, ETH_P_EAPOL, rbuf, rlen, reply->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) { size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len); #endif /* CONFIG_IEEE80211R */ wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, (u8 *) ptk, ptk_len, wpa_key_mgmt_sha256(sm->key_mgmt)); return 0; } 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) { struct wpa_eapol_ie_parse ie; struct wpa_ptk *ptk; u8 buf[8]; int res; 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)); #ifndef CONFIG_NO_WPA2 if (sm->proto == WPA_PROTO_RSN) { /* RSN: msg 1/4 should contain PMKID for the selected PMK */ const u8 *_buf = (const u8 *) (key + 1); size_t len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len); if (wpa_supplicant_parse_ies(_buf, len, &ie) < 0) goto failed; if (ie.pmkid) { wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " "Authenticator", ie.pmkid, PMKID_LEN); } } #endif /* CONFIG_NO_WPA2 */ 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); /* Supplicant: swap tx/rx Mic keys */ os_memcpy(buf, ptk->u.auth.tx_mic_key, 8); os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8); os_memcpy(ptk->u.auth.rx_mic_key, buf, 8); sm->tptk_set = 1; if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, ptk)) goto failed; os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); return; failed: 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) { 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, (u8 *) sm->ptk.tk1, 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; } + 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)"); 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); return -1; } + 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) { #ifndef CONFIG_NO_WPA2 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 (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"); return -1; } wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); return 0; #else /* CONFIG_NO_WPA2 */ return -1; #endif /* CONFIG_NO_WPA2 */ } +#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_IGTK_LEN; + 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_ALG_IGTK, broadcast_ether_addr, + keyidx, 0, igtk->pn, sizeof(igtk->pn), + igtk->igtk, WPA_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 (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) return 0; if (ie->igtk) { const struct wpa_igtk_kde *igtk; - u16 keyidx; if (ie->igtk_len != sizeof(*igtk)) 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, WPA_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_ALG_IGTK, broadcast_ether_addr, - keyidx, 0, igtk->pn, sizeof(igtk->pn), - igtk->igtk, WPA_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(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 * @kde: KDEs to include the EAPOL-Key frame * @kde_len: Length of KDEs * @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, const u8 *kde, size_t kde_len, struct wpa_ptk *ptk) { size_t rlen; struct wpa_eapol_key *reply; u8 *rbuf; if (kde) wpa_hexdump(MSG_DEBUG, "WPA: KDE for msg 4/4", kde, kde_len); rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*reply) + kde_len, &rlen, (void *) &reply); if (rbuf == NULL) return -1; reply->type = sm->proto == WPA_PROTO_RSN ? 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) 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_PUT_BE16(reply->key_data_length, kde_len); if (kde) os_memcpy(reply + 1, kde, kde_len); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL, rbuf, rlen, reply->key_mic); return 0; } static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_eapol_key *key, u16 ver) { u16 key_info, keylen, len; const u8 *pos; 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); pos = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); if (wpa_supplicant_parse_ies(pos, 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 && ie.igtk_len != sizeof(struct wpa_igtk_kde)) { 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; } if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, NULL, 0, &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 (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; } wpa_sm_set_rekey_offload(sm); 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(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, size_t keydatalen, int key_info, size_t extra_len, u16 ver, struct wpa_gtk_data *gd) { size_t maxkeylen; u8 ek[32]; gd->gtk_len = WPA_GET_BE16(key->key_length); maxkeylen = keydatalen; if (keydatalen > extra_len) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Truncated EAPOL-Key packet: " "key_data_length=%lu > extra_len=%lu", (unsigned long) keydatalen, (unsigned long) extra_len); return -1; } 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 (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; 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) { os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, 16); if (keydatalen > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: RC4 key data too long (%lu)", (unsigned long) keydatalen); return -1; } os_memcpy(gd->gtk, key + 1, keydatalen); if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: RC4 failed"); return -1; } } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (keydatalen % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %lu", (unsigned long) keydatalen); 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) keydatalen, (unsigned long) maxkeylen); return -1; } if (aes_unwrap(sm->ptk.kek, maxkeylen / 8, (const u8 *) (key + 1), 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 rlen; struct wpa_eapol_key *reply; u8 *rbuf; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*reply), &rlen, (void *) &reply); if (rbuf == NULL) return -1; reply->type = sm->proto == WPA_PROTO_RSN ? 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) 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_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, ver, sm->bssid, ETH_P_EAPOL, rbuf, rlen, reply->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, int extra_len, u16 ver) { u16 key_info, keydatalen; int rekey, ret; struct wpa_gtk_data gd; 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); keydatalen = WPA_GET_BE16(key->key_data_length); if (sm->proto == WPA_PROTO_RSN) { ret = wpa_supplicant_process_1_of_2_rsn(sm, (const u8 *) (key + 1), keydatalen, key_info, &gd); } else { ret = wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen, key_info, extra_len, 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; 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); wpa_sm_set_rekey_offload(sm); } else { wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); } return; failed: wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_eapol_key *key, u16 ver, const u8 *buf, size_t len) { u8 mic[16]; int ok = 0; os_memcpy(mic, key->key_mic, 16); if (sm->tptk_set) { os_memset(key->key_mic, 0, 16); wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, key->key_mic); if (os_memcmp(mic, key->key_mic, 16) != 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)); } } if (!ok && sm->ptk_set) { os_memset(key->key_mic, 0, 16); wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, key->key_mic); if (os_memcmp(mic, key->key_mic, 16) != 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) { u16 keydatalen = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", (u8 *) (key + 1), keydatalen); 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) { u8 ek[32]; os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, 16); if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: RC4 failed"); return -1; } } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { u8 *buf; if (keydatalen % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %d", keydatalen); return -1; } keydatalen -= 8; /* AES-WRAP adds 8 bytes */ buf = os_malloc(keydatalen); 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, keydatalen / 8, (u8 *) (key + 1), 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 + 1, buf, keydatalen); os_free(buf); WPA_PUT_BE16(key->key_data_length, keydatalen); } 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", (u8 *) (key + 1), keydatalen); 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) { #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), WPA_GET_BE16(key->key_data_length)); 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", key->key_mic, 16); #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, extra_len; struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info, ver; u8 *tmp; int ret = -1; struct wpa_peerkey *peerkey = NULL; #ifdef CONFIG_IEEE80211R sm->ft_completed = 0; #endif /* CONFIG_IEEE80211R */ if (len < sizeof(*hdr) + sizeof(*key)) { 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) + sizeof(*key)); return 0; } tmp = os_malloc(len); if (tmp == NULL) return -1; os_memcpy(tmp, buf, len); hdr = (struct ieee802_1x_hdr *) tmp; key = (struct wpa_eapol_key *) (hdr + 1); 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; } if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { 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 (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; } wpa_eapol_key_dump(sm, key); eapol_sm_notify_lower_layer_success(sm->eapol, 0); wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len); 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); } 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_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor version %d", 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) { 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 && 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 goto out; } if (sm->pairwise_cipher == WPA_CIPHER_GCMP && 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, key, ver, tmp, data_len)) goto out; #ifdef CONFIG_PEERKEY if ((key_info & WPA_KEY_INFO_MIC) && peerkey && peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp, data_len)) goto out; #endif /* CONFIG_PEERKEY */ extra_len = data_len - sizeof(*hdr) - sizeof(*key); if (WPA_GET_BE16(key->key_data_length) > extra_len) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " "frame - key_data overflow (%d > %lu)", WPA_GET_BE16(key->key_data_length), (unsigned long) extra_len); goto out; } extra_len = WPA_GET_BE16(key->key_data_length); if (sm->proto == WPA_PROTO_RSN && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { if (wpa_supplicant_decrypt_key_data(sm, key, ver)) goto out; extra_len = WPA_GET_BE16(key->key_data_length); } 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); } else if (key_info & WPA_KEY_INFO_MIC) { /* 3/4 4-Way Handshake */ wpa_supplicant_process_3_of_4(sm, key, ver); } else { /* 1/4 4-Way Handshake */ wpa_supplicant_process_1_of_4(sm, src_addr, key, ver); } } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { /* PeerKey SMK Handshake */ peerkey_rx_eapol_smk(sm, src_addr, key, extra_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, extra_len, ver); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: EAPOL-Key (Group) without Mic bit - " "dropped"); } } ret = 1; out: os_free(tmp); 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 ? 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; 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 (ret < 0 || (size_t) ret >= buflen) 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 (ret >= 0 && (size_t) ret < buflen) 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); 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; sm->tptk_set = 0; + 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 */ } /** * 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) { 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 */ } /** * 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 * * Configure the PMK for WPA state machine. */ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) { 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 */ } /** * 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; } 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; } } /** * 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_param - Get WPA state machine parameters * @sm: Pointer to WPA state machine data from wpa_sm_init() * @param: Parameter field * Returns: Parameter value */ unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param) { if (sm == NULL) return 0; switch (param) { case RSNA_PMK_LIFETIME: return sm->dot11RSNAConfigPMKLifetime; case RSNA_PMK_REAUTH_THRESHOLD: return sm->dot11RSNAConfigPMKReauthThreshold; case RSNA_SA_TIMEOUT: return sm->dot11RSNAConfigSATimeout; case WPA_PARAM_PROTO: return sm->proto; case WPA_PARAM_PAIRWISE: return sm->pairwise_cipher; case WPA_PARAM_GROUP: return sm->group_cipher; case WPA_PARAM_KEY_MGMT: return sm->key_mgmt; #ifdef CONFIG_IEEE80211W case WPA_PARAM_MGMT_GROUP: return sm->mgmt_group_cipher; #endif /* CONFIG_IEEE80211W */ case WPA_PARAM_RSN_ENABLED: return sm->rsn_enabled; default: return 0; } } /** * 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 (ret < 0 || ret >= end - pos) 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 (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } } return pos - buf; } /** * 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) { #ifndef CONFIG_NO_WPA2 return pmksa_cache_list(sm->pmksa, buf, len); #else /* CONFIG_NO_WPA2 */ return -1; #endif /* CONFIG_NO_WPA2 */ } 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 */ } 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) { #ifndef CONFIG_NO_WPA2 pmksa_cache_flush(sm->pmksa, network_ctx); #endif /* CONFIG_NO_WPA2 */ } #ifdef CONFIG_WNM int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) { struct wpa_gtk_data gd; #ifdef CONFIG_IEEE80211W struct wpa_igtk_kde igd; u16 keyidx; #endif /* CONFIG_IEEE80211W */ u16 keyinfo; u8 keylen; /* plaintext key len */ u8 *key_rsc; os_memset(&gd, 0, sizeof(gd)); #ifdef CONFIG_IEEE80211W os_memset(&igd, 0, sizeof(igd)); #endif /* CONFIG_IEEE80211W */ 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; } if (subelem_id == WNM_SLEEP_SUBELEM_GTK) { 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)) { wpa_printf(MSG_DEBUG, "Failed to install the GTK in " "WNM mode"); return -1; } #ifdef CONFIG_IEEE80211W } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { - os_memcpy(igd.keyid, buf + 2, 2); - os_memcpy(igd.pn, buf + 4, 6); + const struct wpa_igtk_kde *igtk; - keyidx = WPA_GET_LE16(igd.keyid); - os_memcpy(igd.igtk, buf + 10, WPA_IGTK_LEN); - - wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", - igd.igtk, WPA_IGTK_LEN); - if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, - keyidx, 0, igd.pn, sizeof(igd.pn), - igd.igtk, WPA_IGTK_LEN) < 0) { - wpa_printf(MSG_DEBUG, "Failed to install the IGTK in " - "WNM mode"); + igtk = (const struct wpa_igtk_kde *) (buf + 2); + if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0) return -1; - } #endif /* CONFIG_IEEE80211W */ } else { wpa_printf(MSG_DEBUG, "Unknown element id"); return -1; } return 0; } #endif /* CONFIG_WNM */ Index: releng/10.3/contrib/wpa/src/rsn_supp/wpa_ft.c =================================================================== --- releng/10.3/contrib/wpa/src/rsn_supp/wpa_ft.c (revision 324739) +++ releng/10.3/contrib/wpa/src/rsn_supp/wpa_ft.c (revision 324740) @@ -1,832 +1,840 @@ /* * WPA Supplicant - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2006-2007, 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, size_t ptk_len) { 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); wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, sm->bssid, sm->pmk_r1_name, (u8 *) ptk, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); return 0; } /** * 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 * @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, 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 (sm->group_cipher != WPA_CIPHER_CCMP && sm->group_cipher != WPA_CIPHER_GCMP && sm->group_cipher != WPA_CIPHER_TKIP) { 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 { 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, 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.tk1, 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, 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, ptk_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 (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { 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(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(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; ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, bssid, sm->pmk_r1_name, (u8 *) &sm->ptk, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) &sm->ptk, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, sm->pmk_r1_name, sm->ptk.kck, 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 (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) return 0; return sm->ft_completed; } 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, 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 (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, 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[16]; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { 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(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(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(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->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(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 */ } 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, 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/10.3/contrib/wpa/src/rsn_supp/wpa_i.h =================================================================== --- releng/10.3/contrib/wpa/src/rsn_supp/wpa_i.h (revision 324739) +++ releng/10.3/contrib/wpa/src/rsn_supp/wpa_i.h (revision 324740) @@ -1,317 +1,325 @@ /* * Internal WPA/RSN supplicant state machine definitions * Copyright (c) 2004-2010, 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; + int ptk_installed; 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; 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_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; #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 */ }; 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.kck, 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) { if (sm->ctx->tdls_get_capa) return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported, tdls_ext_setup); 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, 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, 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 capability, const u8 *supp_rates, size_t supp_rates_len) { if (sm->ctx->tdls_peer_addset) return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, capability, supp_rates, supp_rates_len); return -1; } #endif /* CONFIG_TDLS */ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, 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, const u8 *kde, size_t kde_len, 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, size_t ptk_len); void wpa_tdls_assoc(struct wpa_sm *sm); void wpa_tdls_disassoc(struct wpa_sm *sm); #endif /* WPA_I_H */ Index: releng/10.3/contrib/wpa/wpa_supplicant/events.c =================================================================== --- releng/10.3/contrib/wpa/wpa_supplicant/events.c (revision 324739) +++ releng/10.3/contrib/wpa/wpa_supplicant/events.c (revision 324740) @@ -1,2941 +1,2942 @@ /* * WPA Supplicant - Driver event processing * Copyright (c) 2003-2012, 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 "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" static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct os_time now; if (ssid == NULL || ssid->disabled_until.sec == 0) return 0; os_get_time(&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; } static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid, *old_ssid; int res; if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) return 0; 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); wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_ie, &wpa_ie_len); } 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_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"); 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); #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; #endif /* CONFIG_SME */ #ifdef CONFIG_P2P os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); #endif /* CONFIG_P2P */ wpa_s->current_bss = NULL; wpa_s->assoc_freq = 0; #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SME if (wpa_s->sme.ft_ies) sme_update_ft_ies(wpa_s, NULL, NULL, 0); #endif /* CONFIG_SME */ #endif /* CONFIG_IEEE80211R */ 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; wpa_s->key_mgmt = 0; + 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, 1); 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, type; if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL) return 0; if (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); if (sim && aka) type = SCARD_TRY_BOTH; else if (aka) type = SCARD_USIM_ONLY; else type = SCARD_GSM_SIM_ONLY; wpa_s->scard = scard_init(type, NULL); 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 wpa_supplicant_match_privacy(struct wpa_bss *bss, struct wpa_ssid *ssid) { int i, privacy = 0; if (ssid->mixed_cell) return 1; #ifdef CONFIG_WPS if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) return 1; #endif /* CONFIG_WPS */ for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) { privacy = 1; break; } } #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 (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) && (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? wpa_s->conf->pmf : ssid->ieee80211w) == 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 (!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 ht_supported(const struct hostapd_hw_modes *mode) { if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) { /* * The driver did not indicate whether it supports HT. Assume * it does to avoid connection issues. */ return 1; } /* * IEEE Std 802.11n-2009 20.1.1: * An HT non-AP STA shall support all EQM rates for one spatial stream. */ return mode->mcs_set[0] == 0xff; } 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; } 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", r / 10, r % 10); return 0; } } } return 1; } static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int i, struct wpa_bss *bss, struct wpa_ssid *group) { u8 wpa_ie_len, rsn_ie_len; int wpa; struct wpa_blacklist *e; const u8 *ie; struct wpa_ssid *ssid; 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; wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' " "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s", i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), wpa_ie_len, rsn_ie_len, bss->caps, bss->level, wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : ""); 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 = 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; } if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) continue; if (!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_supplicant_match_privacy(bss, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy " "mismatch"); continue; } if (bss->caps & IEEE80211_CAP_IBSS) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - IBSS (adhoc) " "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 /* * 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 */ /* 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) { unsigned int i; 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); 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; } static struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, struct wpa_ssid **selected_ssid) { struct wpa_bss *selected = NULL; int prio; if (wpa_s->last_scan_res == NULL || wpa_s->last_scan_res_used == 0) return NULL; /* no scan results from last update */ while (selected == NULL) { for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { selected = wpa_supplicant_select_bss( wpa_s, wpa_s->conf->pssid[prio], selected_ssid); 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; } 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); #ifdef CONFIG_P2P wpa_s->sta_scan_pending = 0; #endif /* CONFIG_P2P */ 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"); #ifdef CONFIG_P2P if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1) return -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS wpas_wps_cancel(wpa_s); #endif /* CONFIG_WPS */ return -1; } /* * 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) || os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) != 0))) { 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: " "reassociate: %d selected: "MACSTR " bssid: " MACSTR " pending: " MACSTR " wpa_state: %s", wpa_s->reassociate, MAC2STR(selected->bssid), MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), wpa_supplicant_state_txt(wpa_s->wpa_state)); wpa_supplicant_associate(wpa_s, selected, ssid); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Already associated 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) 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; int min_diff; 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", MAC2STR(current_bss->bssid), current_bss->level); wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR " level=%d", MAC2STR(selected->bssid), selected->level); 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 (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) { struct wpa_scan_results *scan_res; 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); #ifdef CONFIG_P2P if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && wpa_s->global->p2p != NULL && !wpa_s->sta_scan_pending && !wpa_s->scan_res_handler) { wpa_s->global->p2p_cb_on_scan_complete = 0; if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " "stopped scan processing"); wpa_s->sta_scan_pending = 1; wpa_supplicant_req_scan(wpa_s, 5, 0); return -1; } } wpa_s->sta_scan_pending = 0; #endif /* CONFIG_P2P */ 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) 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); return -1; } #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 (wpa_s->scan_res_handler) { 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); wpa_scan_results_free(scan_res); return -2; } 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 */ wpa_scan_results_free(scan_res); return 0; } wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available"); 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 (sme_proc_obss_scan(wpa_s) > 0) { wpa_scan_results_free(scan_res); return 0; } if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) { wpa_scan_results_free(scan_res); return 0; } if (autoscan_notify_scan(wpa_s, scan_res)) { wpa_scan_results_free(scan_res); return 0; } if (wpa_s->disconnected) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); wpa_scan_results_free(scan_res); return 0; } if (!wpas_driver_bss_selection(wpa_s) && bgscan_notify_scan(wpa_s, scan_res) == 1) { wpa_scan_results_free(scan_res); return 0; } wpas_wps_update_ap_info(wpa_s, scan_res); wpa_scan_results_free(scan_res); return wpas_select_network_from_last_scan(wpa_s); } int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s) { struct wpa_bss *selected; struct wpa_ssid *ssid = NULL; selected = wpa_supplicant_pick_network(wpa_s, &ssid); if (selected) { int skip; skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid); if (skip) { 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; } 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 { 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); wpa_supplicant_rsn_preauth_scan_results(wpa_s); } else { int timeout_sec = wpa_s->scan_interval; int timeout_usec = 0; #ifdef CONFIG_P2P if (wpas_p2p_scan_no_go_seen(wpa_s) == 1) return 0; if (wpa_s->p2p_in_provisioning) { /* * Use shorter wait during P2P Provisioning * state 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 */ if (wpa_supplicant_req_sched_scan(wpa_s)) wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); } } return 0; } static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { const char *rn, *rn2; struct wpa_supplicant *ifs; if (_wpa_supplicant_event_scan_results(wpa_s, data) != 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; } /* * Check other interfaces to see if they have the same radio-name. If * so, they get updated with this same scan info. */ if (!wpa_s->driver->get_radio_name) return; rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); if (rn == NULL || rn[0] == '\0') return; wpa_dbg(wpa_s, MSG_DEBUG, "Checking for other virtual interfaces " "sharing same radio (%s) in event_scan_results", rn); for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { if (ifs == wpa_s || !ifs->driver->get_radio_name) continue; rn2 = ifs->driver->get_radio_name(ifs->drv_priv); if (rn2 && os_strcmp(rn, rn2) == 0) { wpa_printf(MSG_DEBUG, "%s: Updating scan results from " "sibling", ifs->ifname); _wpa_supplicant_event_scan_results(ifs, data); } } } #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 */ } 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; 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 */ } 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) { u8 bssid[ETH_ALEN]; 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 */ 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; 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 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 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; struct wpa_driver_capa capa; #ifdef CONFIG_AP if (wpa_s->ap_iface) { 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 */ 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)); 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->current_ssid) { struct wpa_bss *bss = NULL; bss = wpa_supplicant_get_new_bss(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, bssid); } if (bss) wpa_s->current_bss = bss; } 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) 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; #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)) { 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_time now, age; os_get_time(&now); os_time_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_drv_get_capa(wpa_s, &capa) == 0 && capa.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); } 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"); wpas_auth_failed(wpa_s); } if (!wpa_s->auto_reconnect_disabled || wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to " "reconnect (wps=%d wpa_state=%d)", wpa_s->key_mgmt == WPA_KEY_MGMT_WPS, 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; 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_s->keys_cleared = 0; 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) { #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 */ } } #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_time t; wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected"); pairwise = (data && data->michael_mic_failure.unicast); os_get_time(&t); if ((wpa_s->last_michael_mic_error && t.sec - 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.sec; 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"); } wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); 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_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); wpa_s->ibss_rsn = NULL; #endif /* CONFIG_IBSS_RSN */ #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_start(wpa_s->wpa, data->tdls.peer); break; case TDLS_REQUEST_TEARDOWN: wpa_tdls_send_teardown(wpa_s->wpa, data->tdls.peer, data->tdls.reason_code); 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); } #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 */ } void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct wpa_supplicant *wpa_s = ctx; u16 reason_code = 0; int locally_generated = 0; 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); break; case EVENT_DISASSOC: wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification"); if (data) { wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", data->disassoc_info.reason_code, data->disassoc_info.locally_generated ? " (locally generated)" : ""); if (data->disassoc_info.addr) wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, MAC2STR(data->disassoc_info.addr)); } #ifdef CONFIG_AP if (wpa_s->ap_iface && data && data->disassoc_info.addr) { hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], data->disassoc_info.addr); break; } if (wpa_s->ap_iface) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in " "AP mode"); break; } #endif /* CONFIG_AP */ if (data) { reason_code = data->disassoc_info.reason_code; locally_generated = data->disassoc_info.locally_generated; wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)", data->disassoc_info.ie, data->disassoc_info.ie_len); #ifdef CONFIG_P2P wpas_p2p_disassoc_notif( wpa_s, data->disassoc_info.addr, reason_code, data->disassoc_info.ie, data->disassoc_info.ie_len, locally_generated); #endif /* CONFIG_P2P */ } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_disassoc(wpa_s, data); /* fall through */ case EVENT_DEAUTH: if (event == EVENT_DEAUTH) { wpa_dbg(wpa_s, MSG_DEBUG, "Deauthentication notification"); if (data) { reason_code = data->deauth_info.reason_code; locally_generated = data->deauth_info.locally_generated; wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", data->deauth_info.reason_code, data->deauth_info.locally_generated ? " (locally generated)" : ""); if (data->deauth_info.addr) { wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, MAC2STR(data->deauth_info. addr)); } wpa_hexdump(MSG_DEBUG, "Deauthentication frame IE(s)", data->deauth_info.ie, data->deauth_info.ie_len); } } #ifdef CONFIG_AP if (wpa_s->ap_iface && data && data->deauth_info.addr) { hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], data->deauth_info.addr); break; } if (wpa_s->ap_iface) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in " "AP mode"); break; } #endif /* CONFIG_AP */ 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))) wpas_auth_failed(wpa_s); #ifdef CONFIG_P2P if (event == EVENT_DEAUTH && data) { if (wpas_p2p_deauth_notif(wpa_s, data->deauth_info.addr, reason_code, data->deauth_info.ie, data->deauth_info.ie_len, locally_generated) > 0) { /* * The interface was removed, so cannot * continue processing any additional * operations after this. */ break; } } #endif /* CONFIG_P2P */ wpa_supplicant_event_disassoc_finish(wpa_s, reason_code, locally_generated); break; case EVENT_MICHAEL_MIC_FAILURE: wpa_supplicant_event_michael_mic_failure(wpa_s, data); break; #ifndef CONFIG_NO_SCAN_PROCESSING case EVENT_SCAN_RESULTS: wpa_supplicant_event_scan_results(wpa_s, data); #ifdef CONFIG_P2P if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && wpa_s->global->p2p != NULL && wpa_s->wpa_state != WPA_AUTHENTICATING && wpa_s->wpa_state != WPA_ASSOCIATING) { wpa_s->global->p2p_cb_on_scan_complete = 0; if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " "continued after scan result processing"); } } #endif /* CONFIG_P2P */ 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: if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_auth_timed_out(wpa_s, data); break; case EVENT_ASSOC_TIMED_OUT: 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; } #ifdef CONFIG_AP wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, data->ch_switch.ht_enabled, data->ch_switch.ch_offset); #endif /* CONFIG_AP */ break; case EVENT_RX_MGMT: { u16 fc, stype; const struct ieee80211_mgmt *mgmt; mgmt = (const struct ieee80211_mgmt *) data->rx_mgmt.frame; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); if (wpa_s->ap_iface == NULL) { #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.ssi_signal); break; } #endif /* CONFIG_P2P */ wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received " "management frame in non-AP mode"); break; } 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); break; } #endif /* CONFIG_AP */ case EVENT_RX_ACTION: wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR " Category=%u DataLen=%d freq=%d MHz", MAC2STR(data->rx_action.sa), data->rx_action.category, (int) data->rx_action.len, data->rx_action.freq); #ifdef CONFIG_IEEE80211R if (data->rx_action.category == WLAN_ACTION_FT) { ft_rx_action(wpa_s, data->rx_action.data, data->rx_action.len); break; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W #ifdef CONFIG_SME if (data->rx_action.category == WLAN_ACTION_SA_QUERY) { sme_sa_query_rx(wpa_s, data->rx_action.sa, data->rx_action.data, data->rx_action.len); break; } #endif /* CONFIG_SME */ #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM if (data->rx_action.category == WLAN_ACTION_WNM) { ieee802_11_rx_wnm_action(wpa_s, &data->rx_action); break; } #endif /* CONFIG_WNM */ #ifdef CONFIG_GAS if (data->rx_action.category == WLAN_ACTION_PUBLIC && gas_query_rx(wpa_s->gas, data->rx_action.da, data->rx_action.sa, data->rx_action.bssid, data->rx_action.data, data->rx_action.len, data->rx_action.freq) == 0) break; #endif /* CONFIG_GAS */ #ifdef CONFIG_TDLS if (data->rx_action.category == WLAN_ACTION_PUBLIC && data->rx_action.len >= 4 && data->rx_action.data[0] == WLAN_TDLS_DISCOVERY_RESPONSE) { wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery " "Response from " MACSTR, MAC2STR(data->rx_action.sa)); break; } #endif /* CONFIG_TDLS */ #ifdef CONFIG_P2P wpas_p2p_rx_action(wpa_s, data->rx_action.da, data->rx_action.sa, data->rx_action.bssid, data->rx_action.category, data->rx_action.data, data->rx_action.len, data->rx_action.freq); #endif /* CONFIG_P2P */ 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 */ #ifdef CONFIG_P2P 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, data->rx_probe_req.ssi_signal); #endif /* CONFIG_P2P */ 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 */ #ifdef CONFIG_P2P wpas_p2p_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq, data->remain_on_channel.duration); #endif /* CONFIG_P2P */ 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 */ #ifdef CONFIG_P2P wpas_p2p_cancel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq); #endif /* CONFIG_P2P */ break; #ifdef CONFIG_P2P case EVENT_P2P_DEV_FOUND: { struct p2p_peer_info peer_info; os_memset(&peer_info, 0, sizeof(peer_info)); if (data->p2p_dev_found.dev_addr) os_memcpy(peer_info.p2p_device_addr, data->p2p_dev_found.dev_addr, ETH_ALEN); if (data->p2p_dev_found.pri_dev_type) os_memcpy(peer_info.pri_dev_type, data->p2p_dev_found.pri_dev_type, sizeof(peer_info.pri_dev_type)); if (data->p2p_dev_found.dev_name) os_strlcpy(peer_info.device_name, data->p2p_dev_found.dev_name, sizeof(peer_info.device_name)); peer_info.config_methods = data->p2p_dev_found.config_methods; peer_info.dev_capab = data->p2p_dev_found.dev_capab; peer_info.group_capab = data->p2p_dev_found.group_capab; /* * FIX: new_device=1 is not necessarily correct. We should * maintain a P2P peer database in wpa_supplicant and update * this information based on whether the peer is truly new. */ wpas_dev_found(wpa_s, data->p2p_dev_found.addr, &peer_info, 1); break; } case EVENT_P2P_GO_NEG_REQ_RX: wpas_go_neg_req_rx(wpa_s, data->p2p_go_neg_req_rx.src, data->p2p_go_neg_req_rx.dev_passwd_id); break; case EVENT_P2P_GO_NEG_COMPLETED: wpas_go_neg_completed(wpa_s, data->p2p_go_neg_completed.res); break; case EVENT_P2P_PROV_DISC_REQUEST: wpas_prov_disc_req(wpa_s, data->p2p_prov_disc_req.peer, data->p2p_prov_disc_req.config_methods, data->p2p_prov_disc_req.dev_addr, data->p2p_prov_disc_req.pri_dev_type, data->p2p_prov_disc_req.dev_name, data->p2p_prov_disc_req.supp_config_methods, data->p2p_prov_disc_req.dev_capab, data->p2p_prov_disc_req.group_capab, NULL, 0); break; case EVENT_P2P_PROV_DISC_RESPONSE: wpas_prov_disc_resp(wpa_s, data->p2p_prov_disc_resp.peer, data->p2p_prov_disc_resp.config_methods); break; case EVENT_P2P_SD_REQUEST: wpas_sd_request(wpa_s, data->p2p_sd_req.freq, data->p2p_sd_req.sa, data->p2p_sd_req.dialog_token, data->p2p_sd_req.update_indic, data->p2p_sd_req.tlvs, data->p2p_sd_req.tlvs_len); break; case EVENT_P2P_SD_RESPONSE: wpas_sd_response(wpa_s, data->p2p_sd_resp.sa, data->p2p_sd_resp.update_indic, data->p2p_sd_resp.tlvs, data->p2p_sd_resp.tlvs_len); break; #endif /* CONFIG_P2P */ 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: 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); #ifdef CONFIG_AP if (!wpa_s->ap_iface) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); 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"); wpa_supplicant_mark_disassoc(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); break; case EVENT_CHANNEL_LIST_CHANGED: if (wpa_s->drv_priv == NULL) break; /* Ignore event during drv initialization */ free_hw_features(wpa_s); wpa_s->hw.modes = wpa_drv_get_hw_feature_data( wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags); #ifdef CONFIG_P2P wpas_p2p_update_channel_list(wpa_s); #endif /* CONFIG_P2P */ break; case EVENT_INTERFACE_UNAVAILABLE: #ifdef CONFIG_P2P wpas_p2p_interface_unavailable(wpa_s); #endif /* CONFIG_P2P */ 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; #ifdef CONFIG_P2P wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24, data->best_chan.freq_5, data->best_chan.freq_overall); #endif /* CONFIG_P2P */ 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_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->sched_scanning = 0; wpa_supplicant_notify_scanning(wpa_s, 0); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) break; /* * If we timed out, start a new sched scan to continue * searching for more SSIDs. */ if (wpa_s->sched_scan_timed_out) wpa_supplicant_req_sched_scan(wpa_s); break; case EVENT_WPS_BUTTON_PUSHED: #ifdef CONFIG_WPS wpas_wps_start_pbc(wpa_s, NULL, 0); #endif /* CONFIG_WPS */ break; default: wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; } } Index: releng/10.3/contrib/wpa/wpa_supplicant/wnm_sta.c =================================================================== --- releng/10.3/contrib/wpa/wpa_supplicant/wnm_sta.c (revision 324739) +++ releng/10.3/contrib/wpa/wpa_supplicant/wnm_sta.c (revision 324740) @@ -1,423 +1,439 @@ /* * wpa_supplicant - WNM * Copyright (c) 2011-2012, 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 "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "scan.h" #define MAX_TFS_IE_LEN 1024 /* 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 + 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] | Diaglog Token [1] | Key Data Len [2] | Key Data | * WNM-Sleep Mode IE | TFS Response IE */ u8 *pos = (u8 *) frm; /* point to action field */ u16 key_len_total = le_to_host16(*((u16 *)(frm+2))); struct wnm_sleep_element *wnmsleep_ie = NULL; /* multiple TFS Resp IE (assuming consecutive) */ u8 *tfsresp_ie_start = NULL; u8 *tfsresp_ie_end = NULL; + 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; + } + wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d", frm[0], frm[1], key_len_total); pos += 4 + key_len_total; if (pos > frm + len) { wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); return; } 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); } } static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s, u8 dialog_token, u8 status, u8 delay, const u8 *target_bssid) { u8 buf[1000], *pos; struct ieee80211_mgmt *mgmt; size_t len; 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); 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; } len = pos - (u8 *) &mgmt->u.action.category; 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); } static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end, int reply) { u8 dialog_token; u8 mode; u16 disassoc_timer; if (pos + 5 > end) return; dialog_token = pos[0]; mode = pos[1]; disassoc_timer = WPA_GET_LE16(pos + 2); wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " "dialog_token=%u request_mode=0x%x " "disassoc_timer=%u validity_interval=%u", dialog_token, mode, disassoc_timer, pos[4]); pos += 5; if (mode & 0x08) pos += 12; /* BSS Termination Duration */ if (mode & 0x10) { 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'; wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation Imminent - " "session_info_url=%s", url); } if (mode & 0x04) { wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " "Disassociation Timer %u", disassoc_timer); if (disassoc_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 (reply) { /* TODO: add support for reporting Accept */ wnm_send_bss_transition_mgmt_resp(wpa_s, dialog_token, 1 /* Reject - unspecified */, 0, NULL); } } void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, struct rx_action *action) { const u8 *pos, *end; u8 act; if (action->data == NULL || action->len == 0) return; pos = action->data; end = pos + action->len; act = *pos++; wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, act, MAC2STR(action->sa)); if (wpa_s->wpa_state < WPA_ASSOCIATED || os_memcmp(action->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, !(action->da[0] & 0x01)); break; case WNM_SLEEP_MODE_RESP: ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len); break; default: break; } } Index: releng/10.3/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h =================================================================== --- releng/10.3/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h (revision 324739) +++ releng/10.3/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h (revision 324740) @@ -1,808 +1,809 @@ /* * wpa_supplicant - Internal definitions * Copyright (c) 2003-2012, 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 "config_ssid.h" extern const char *wpa_supplicant_version; extern const char *wpa_supplicant_license; #ifndef CONFIG_NO_STDOUT_DEBUG extern const char *wpa_supplicant_full_license1; extern const char *wpa_supplicant_full_license2; extern const char *wpa_supplicant_full_license3; extern const char *wpa_supplicant_full_license4; extern const char *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; /** * 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; }; /** * 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; /** * 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; }; 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_freq_range { unsigned int min; unsigned int max; }; /** * 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; u8 p2p_dev_addr[ETH_ALEN]; 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 *p2p_disallow_freq; unsigned int num_p2p_disallow_freq; enum wpa_conc_pref { WPA_CONC_PREF_NOT_SET, WPA_CONC_PREF_STA, WPA_CONC_PREF_P2P } conc_pref; unsigned int p2p_cb_on_scan_complete:1; #ifdef CONFIG_WIFI_DISPLAY int wifi_display; #define MAX_WFD_SUBELEMS 10 struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS]; #endif /* CONFIG_WIFI_DISPLAY */ }; /** * 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_time last_attempt; }; struct wpa_ssid_value { u8 ssid[32]; size_t ssid_len; }; /** * 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_supplicant *parent; struct wpa_supplicant *next; struct l2_packet_data *l2; struct l2_packet_data *l2_br; unsigned char own_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; struct wpa_config *conf; int countermeasures; os_time_t 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 disconnected; /* all connections disabled; i.e., do no reassociate * before this has been cleared */ struct wpa_ssid *current_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; /* 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; int last_scan_full; struct os_time last_scan; 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; 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; #ifdef PCSC_FUNCS char imsi[20]; int mnc_len; #endif /* PCSC_FUNCS */ unsigned char last_eapol_src[ETH_ALEN]; int keys_cleared; 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; int scan_runs; /* number of scan runs since WPS was started */ int *next_scan_freqs; 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 */ unsigned int drv_flags; unsigned int drv_enc; /* * A bitmap of supported protocols for probe response offload. See * struct wpa_driver_capa in driver.h */ unsigned int probe_resp_offloads; 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; int blacklist_cleared; struct wpabuf *pending_eapol_rx; struct os_time pending_eapol_rx_time; u8 pending_eapol_rx_src[ETH_ALEN]; unsigned int last_eapol_matches_bssid:1; + unsigned int wnmsleep_used:1; 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[32]; 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_time sa_query_start; u8 sched_obss_scan; u16 obss_scan_int; u16 bss_max_idle_period; enum { SME_SAE_INIT, SME_SAE_COMMIT, SME_SAE_CONFIRM } sae_state; u16 sae_send_confirm; } 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 */ 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; 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; #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 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; 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 } 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 sta_scan_pending:1; 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 user_initiated_pd:1; int p2p_persistent_go_freq; int p2p_persistent_id; int p2p_go_intent; int p2p_connect_freq; struct os_time p2p_auto_started; #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; u16 wps_ap_channel; 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 fetch_all_anqp:1; #endif /* CONFIG_INTERWORKING */ unsigned int drv_capa_known; struct { struct hostapd_hw_modes *modes; u16 num_modes; u16 flags; } hw; int pno; /* WLAN_REASON_* reason codes. Negative if locally generated. */ int disconnect_reason; struct ext_password_data *ext_pw; struct wpabuf *last_gas_resp; u8 last_gas_addr[ETH_ALEN]; u8 last_gas_dialog_token; unsigned int no_keep_alive:1; }; /* wpa_supplicant.c */ void wpa_supplicant_apply_ht_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_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 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); 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); enum wpa_key_mgmt key_mgmt2driver(int key_mgmt); enum wpa_cipher cipher_suite2driver(int cipher); 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); 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); /** * 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); /* 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 wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s); /* eap_register.c */ int eap_register_methods(void); /** * Utility method to tell if a given network is a persistent group * @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_init_ext_pw(struct wpa_supplicant *wpa_s); #endif /* WPA_SUPPLICANT_I_H */ Index: releng/10.3/sys/conf/newvers.sh =================================================================== --- releng/10.3/sys/conf/newvers.sh (revision 324739) +++ releng/10.3/sys/conf/newvers.sh (revision 324740) @@ -1,232 +1,232 @@ #!/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. # 4. 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$ TYPE="FreeBSD" REVISION="10.3" -BRANCH="RELEASE-p21" +BRANCH="RELEASE-p22" if [ "X${BRANCH_OVERRIDE}" != "X" ]; then BRANCH=${BRANCH_OVERRIDE} fi RELEASE="${REVISION}-${BRANCH}" VERSION="${TYPE} ${RELEASE}" if [ "X${SYSDIR}" = "X" ]; then SYSDIR=$(dirname $0)/.. fi if [ "X${PARAMFILE}" != "X" ]; 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 year=$(sed -Ee '/^Copyright .* The FreeBSD Project/!d;s/^.*1992-([0-9]*) .*$/\1/g' ${SYSDIR}/../COPYRIGHT) # 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 [ X"$COPYRIGHT" = X ]; then COPYRIGHT="/*- * Copyright (c) 1992-$year The FreeBSD Project. * All rights reserved. * */" fi # add newline COPYRIGHT="$COPYRIGHT " 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 '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]*) 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" 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" ;; 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 cat << EOF > vers.c $COPYRIGHT #define SCCSSTR "@(#)${VERSION} #${v}${svn}${git}${hg}${p4version}: ${t}" #define VERSTR "${VERSION} #${v}${svn}${git}${hg}${p4version}: ${t}\\n ${u}@${h}:${d}\\n" #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